changeset 6994:f29a46d66886

[Accessibility, Windows] Implement HighContrast Mode Detection
author Joseph Andresen <joseph.andresen@oracle.com>
date Mon, 12 May 2014 14:23:10 -0700
parents 9a883f78c710
children 5b683dba59a5
files apps/toys/Hello/src/main/java/hello/HelloHighContrast.java modules/graphics/src/main/java/com/sun/glass/ui/Application.java modules/graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java modules/graphics/src/main/java/com/sun/javafx/application/PlatformImpl.java modules/graphics/src/main/java/com/sun/javafx/css/StyleManager.java modules/graphics/src/main/java/com/sun/javafx/tk/Toolkit.java modules/graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java modules/graphics/src/main/native-glass/win/GlassApplication.cpp modules/graphics/src/main/native-glass/win/GlassApplication.h modules/graphics/src/main/native-glass/win/Utils.h
diffstat 10 files changed, 266 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/toys/Hello/src/main/java/hello/HelloHighContrast.java	Mon May 12 14:23:10 2014 -0700
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package hello;
+
+import com.sun.javafx.css.StyleManager;
+import javafx.application.Application;
+import javafx.geometry.Insets;
+import javafx.scene.Scene;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+
+public class HelloHighContrast extends Application {
+
+    private static final String MODENA_PATH = "com/sun/javafx/scene/control/skin/modena/";
+    private String lastStyleUsed = null;
+
+    public static void main(String[] args) {
+        Application.launch(args);
+    }
+
+    @Override public void start(Stage stage) {
+        final ToggleGroup group = new ToggleGroup();
+        group.selectedToggleProperty().addListener(ov -> {
+            // remove old style
+            if (lastStyleUsed != null) {
+                StyleManager.getInstance().removeUserAgentStylesheet(MODENA_PATH + lastStyleUsed);
+                lastStyleUsed = null;
+            }
+
+            // install new style
+            String userData = (String) group.getSelectedToggle().getUserData();
+            if (userData != null) {
+                lastStyleUsed = userData;
+                StyleManager.getInstance().addUserAgentStylesheet(MODENA_PATH + userData);
+            }
+        });
+        
+        ToggleButton disableHighContrast = new ToggleButton("Disable High Contrast");
+        disableHighContrast.setMaxWidth(Double.MAX_VALUE);
+        disableHighContrast.setUserData(null);
+        disableHighContrast.setToggleGroup(group);
+        disableHighContrast.setSelected(true);
+
+        ToggleButton whiteOnBlackBtn = new ToggleButton("White on black");
+        whiteOnBlackBtn.setMaxWidth(Double.MAX_VALUE);
+        whiteOnBlackBtn.setUserData("whiteOnBlack.css");
+        whiteOnBlackBtn.setToggleGroup(group);
+
+        ToggleButton blackOnWhiteBtn = new ToggleButton("Black on white");
+        blackOnWhiteBtn.setMaxWidth(Double.MAX_VALUE);
+        blackOnWhiteBtn.setUserData("blackOnWhite.css");
+        blackOnWhiteBtn.setToggleGroup(group);
+
+        ToggleButton yellowOnBlackBtn = new ToggleButton("Yellow on black");
+        yellowOnBlackBtn.setMaxWidth(Double.MAX_VALUE);
+        yellowOnBlackBtn.setUserData("yellowOnBlack.css");
+        yellowOnBlackBtn.setToggleGroup(group);
+
+        VBox vbox = new VBox(10, disableHighContrast, whiteOnBlackBtn, blackOnWhiteBtn, yellowOnBlackBtn);
+        vbox.setPadding(new Insets(10));
+
+        Scene scene = new Scene(vbox);
+
+        stage.setScene(scene);
+        stage.setWidth(200);
+        stage.setHeight(200);
+        stage.show();
+    }
+}
\ No newline at end of file
--- a/modules/graphics/src/main/java/com/sun/glass/ui/Application.java	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/Application.java	Mon May 12 14:23:10 2014 -0700
@@ -86,7 +86,10 @@
         }
         // currently used only on Mac OS X
         public void handleQuitAction(Application app, long time) {
-    }
+        }
+        public boolean handleThemeChanged(String themeName) {
+            return false;
+        }
     }
 
     private EventHandler eventHandler;
@@ -254,6 +257,14 @@
         }
     }
     
+    private boolean notifyThemeChanged(String themeName) {
+        EventHandler handler = getEventHandler();
+        if (handler != null) {
+            return handler.handleThemeChanged(themeName);
+        }
+        return false;
+    }
+
     private void notifyDidResignActive() {
         EventHandler handler = getEventHandler();
         if (handler != null) {
@@ -660,6 +671,15 @@
     protected abstract int staticView_getMultiClickMaxX();
     protected abstract int staticView_getMultiClickMaxY();
 
+    /**
+     * Gets the Name of the currently active high contrast theme.
+     * If null, then high contrast is not enabled.
+     */
+    public String getHighContrastTheme() {
+        checkEventThread();
+        return null;
+    }
+
     protected boolean _supportsInputMethods() {
         // Overridden in subclasses
         return false;
--- a/modules/graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java	Mon May 12 14:23:10 2014 -0700
@@ -257,6 +257,12 @@
         }
     }
 
+    private native String _getHighContrastTheme();
+    @Override public String getHighContrastTheme() {
+    	checkEventThread();
+        return _getHighContrastTheme();
+    }
+ 
     @Override
     protected boolean _supportsInputMethods() {
         return true;
--- a/modules/graphics/src/main/java/com/sun/javafx/application/PlatformImpl.java	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/java/com/sun/javafx/application/PlatformImpl.java	Mon May 12 14:23:10 2014 -0700
@@ -537,6 +537,63 @@
         }
     }
 
+    private static String accessibilityTheme;
+    public static boolean setAccessibilityTheme(String platformTheme) {
+        if (accessibilityTheme != null) {
+            StyleManager.getInstance().removeUserAgentStylesheet(accessibilityTheme);
+            accessibilityTheme = null;
+        }
+
+        // check to see if there is an override to enable a high-contrast theme
+        final String userTheme = AccessController.doPrivileged(
+                (PrivilegedAction<String>) () -> System.getProperty("com.sun.javafx.highContrastTheme"));
+
+        if (isCaspian()) {
+            if (platformTheme != null || userTheme != null) {
+                // caspian has only one high contrast theme, use it regardless of the user or platform theme.
+                accessibilityTheme = "com/sun/javafx/scene/control/skin/caspian/highcontrast.css";
+            }
+        } else if (isModena()) {
+            // User-defined property takes precedence
+            if (userTheme != null) {
+                switch (userTheme.toUpperCase()) {
+                    case "BLACKONWHITE":
+                        accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/blackOnWhite.css";
+                        break;
+                    case "WHITEONBLACK":
+                        accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/whiteOnBlack.css";
+                        break;
+                    case "YELLOWONBLACK":
+                        accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/yellowOnBlack.css";
+                        break;
+                    default:
+                }
+            } else {
+                if (platformTheme != null) {
+                    // The following names are Platform specific (Windows 7 and 8)
+                    switch (platformTheme) {
+                        case "High Contrast White":
+                            accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/blackOnWhite.css";
+                            break;
+                        case "High Contrast Black":
+                            accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/whiteOnBlack.css";
+                            break;
+                        case "High Contrast #1":
+                        case "High Contrast #2": //TODO #2 should be green on black
+                            accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/yellowOnBlack.css";
+                            break;
+                        default:
+                    }
+                }   
+            }
+        }
+        if (accessibilityTheme != null) {
+            StyleManager.getInstance().addUserAgentStylesheet(accessibilityTheme);
+            return true;
+        }
+        return false;
+    }
+
     private static void _setPlatformUserAgentStylesheet(String stylesheetUrl) {
         isModena = isCaspian = false;
         // check for command line override
@@ -603,26 +660,11 @@
                         return null;
                     }
             );
-
-            // check to see if there is an override to enable a high-contrast theme
-            final String highContrastName = AccessController.doPrivileged(
-                    (PrivilegedAction<String>) () -> System.getProperty("com.sun.javafx.highContrastTheme"));
-            if (highContrastName != null) {
-                switch (highContrastName.toUpperCase()) {
-                    case "BLACKONWHITE": StyleManager.getInstance().addUserAgentStylesheet(
-                                                "com/sun/javafx/scene/control/skin/modena/blackOnWhite.css");
-                                         break;
-                    case "WHITEONBLACK": StyleManager.getInstance().addUserAgentStylesheet(
-                                                "com/sun/javafx/scene/control/skin/modena/whiteOnBlack.css");
-                                         break;
-                    case "YELLOWONBLACK": StyleManager.getInstance().addUserAgentStylesheet(
-                                                "com/sun/javafx/scene/control/skin/modena/yellowOnBlack.css");
-                                         break;
-                }
-            }
         } else {
             StyleManager.getInstance().setDefaultUserAgentStylesheet(stylesheetUrl);
         }
+        // Ensure that accessibility starts right
+        setAccessibilityTheme(Toolkit.getToolkit().getThemeName());
     }
 
     public static void addNoTransparencyStylesheetToScene(final Scene scene) {
--- a/modules/graphics/src/main/java/com/sun/javafx/css/StyleManager.java	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/java/com/sun/javafx/css/StyleManager.java	Mon May 12 14:23:10 2014 -0700
@@ -25,6 +25,7 @@
 
 package com.sun.javafx.css;
 
+import javafx.application.Application;
 import javafx.css.Styleable;
 import java.io.FileNotFoundException;
 import java.io.FilePermission;
@@ -1272,6 +1273,41 @@
         CssError.setCurrentScene(null);
 
     }
+    
+    /**
+     * Removes the specified stylesheet from the application default user agent
+     * stylesheet list.
+     * @param url  The file URL, either relative or absolute, as a String.
+     */
+    public void removeUserAgentStylesheet(String url) {
+        if (url == null ) {
+            throw new IllegalArgumentException("null arg url");
+        }
+ 
+        final String fname = url.trim();
+        if (fname.isEmpty()) {
+            return;
+        }
+ 
+        // if we already have this stylesheet, remove it!
+        boolean removed = false;
+        for (int n = platformUserAgentStylesheetContainers.size() - 1; n >= 0; n--) {
+            // don't remove the platform default user agent stylesheet
+            if (fname.equals(Application.getUserAgentStylesheet())) {
+                continue;
+            }
+ 
+            StylesheetContainer container = platformUserAgentStylesheetContainers.get(n);
+            if (fname.equals(container.fname)) {
+                platformUserAgentStylesheetContainers.remove(n);
+                removed = true;
+            }
+        }
+ 
+        if (removed) {
+            userAgentStylesheetsChanged();
+        }
+    }
 
     /**
      * Set the user agent stylesheet. This is the base default stylesheet for
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/Toolkit.java	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/Toolkit.java	Mon May 12 14:23:10 2014 -0700
@@ -922,4 +922,7 @@
         return imageAccessor;
     }
 
+    public String getThemeName() {
+        return null;
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java	Mon May 12 14:23:10 2014 -0700
@@ -82,6 +82,7 @@
 import com.sun.glass.ui.Timer;
 import com.sun.glass.ui.View;
 import com.sun.javafx.PlatformUtil;
+import com.sun.javafx.application.PlatformImpl;
 import com.sun.javafx.embed.HostInterface;
 import com.sun.javafx.geom.Path2D;
 import com.sun.javafx.geom.PathIterator;
@@ -310,6 +311,10 @@
                 @Override public void handleQuitAction(Application app, long time) {
                     GlassStage.requestClosingAllWindows();
                 }
+
+                @Override public boolean handleThemeChanged(String themeName) {
+                    return PlatformImpl.setAccessibilityTheme(themeName);
+                }
             });
         }
         launchLatch.countDown();
@@ -1487,4 +1492,9 @@
     public int getMultiClickMaxY() {
         return View.getMultiClickMaxY();
     }
+
+    @Override
+    public String getThemeName() {
+        return Application.GetApplication().getHighContrastTheme();
+    }
 }
--- a/modules/graphics/src/main/native-glass/win/GlassApplication.cpp	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/native-glass/win/GlassApplication.cpp	Mon May 12 14:23:10 2014 -0700
@@ -123,6 +123,18 @@
     return szGlassToolkitWindow;
 }
 
+jstring GlassApplication::GetThemeName(JNIEnv* env)
+{
+    HIGHCONTRAST contrastInfo;
+    contrastInfo.cbSize = sizeof(HIGHCONTRAST);
+    ::SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &contrastInfo, 0);
+    if (contrastInfo.dwFlags & HCF_HIGHCONTRASTON) {
+        size_t length = wcslen(contrastInfo.lpszDefaultScheme);
+        return env->NewString((jchar*)contrastInfo.lpszDefaultScheme, length);
+    }
+    return NULL;
+}
+
 LRESULT GlassApplication::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
 {
     switch (msg) {
@@ -174,6 +186,13 @@
         case WM_DISPLAYCHANGE:
             GlassScreen::HandleDisplayChange();
             break;
+        case WM_THEMECHANGED: {
+            JNIEnv* env = GetEnv();
+            jstring themeName = GlassApplication::GetThemeName(env);
+            jboolean result = env->CallBooleanMethod(m_grefThis, javaIDs.Application.notifyThemeChangedMID, themeName);
+            if (CheckAndClearException(env)) return 1;
+            return !result;
+        }
     }
     return ::DefWindowProc(GetHWND(), msg, wParam, lParam);
 }
@@ -333,6 +352,11 @@
     ASSERT(javaIDs.Application.reportExceptionMID);
     if (CheckAndClearException(env)) return;
 
+    javaIDs.Application.notifyThemeChangedMID =
+        env->GetMethodID(cls, "notifyThemeChanged", "(Ljava/lang/String;)Z");
+    ASSERT(javaIDs.Application.notifyThemeChangedMID);
+    if (CheckAndClearException(env)) return;
+
     //NOTE: substitute the cls
     cls = (jclass)env->FindClass("java/lang/Runnable");
     if (CheckAndClearException(env)) return;
@@ -427,6 +451,17 @@
 
 /*
  * Class:     com_sun_glass_ui_win_WinApplication
+ * Method:    _getHighContrastTheme
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_win_WinApplication__1getHighContrastTheme
+  (JNIEnv * env, jobject self)
+{
+    return GlassApplication::GetThemeName(env);
+}
+
+/*
+ * Class:     com_sun_glass_ui_win_WinApplication
  * Method:    _leaveNestedEventLoopImpl
  * Signature: (Ljava/lang/Object;)V
  */
--- a/modules/graphics/src/main/native-glass/win/GlassApplication.h	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/native-glass/win/GlassApplication.h	Mon May 12 14:23:10 2014 -0700
@@ -81,6 +81,7 @@
     static void ExecActionLater(Action *action);
     void RegisterClipboardViewer(jobject clipboard);
     void UnregisterClipboardViewer();
+    static jstring GetThemeName(JNIEnv* env);
 
     inline static DWORD GetMainThreadId()
     {
--- a/modules/graphics/src/main/native-glass/win/Utils.h	Tue May 13 08:51:11 2014 +1200
+++ b/modules/graphics/src/main/native-glass/win/Utils.h	Mon May 12 14:23:10 2014 -0700
@@ -502,6 +502,7 @@
     } Screen;
     struct {
         jmethodID reportExceptionMID;
+        jmethodID notifyThemeChangedMID;
     } Application;
 } JavaIDs;