Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8301302: Platform preferences API #1014

Closed
wants to merge 62 commits into from
Closed
Changes from 2 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
2d28c85
Platform preferences implementation
mstr2 Jul 4, 2023
df03228
documentation
mstr2 Jul 18, 2023
ba26271
Removed platform-independent preference keys
mstr2 Jul 23, 2023
00e9eb7
doc changes
mstr2 Jul 23, 2023
dbad68d
Move Appearance enum to javafx.application
mstr2 Jul 23, 2023
e16491a
removed unused code
mstr2 Jul 24, 2023
2d1c7a9
Ensure that ColorProperty changes are atomic when observed
mstr2 Jul 24, 2023
0589078
doc changes
mstr2 Jul 24, 2023
f8a9c8a
Added test
mstr2 Jul 24, 2023
40e2f28
Removed application preferences implementation
mstr2 Sep 5, 2023
b100c6c
Addressed review comments
mstr2 Sep 5, 2023
4b9f75d
Revert Application changes
mstr2 Sep 5, 2023
27022f3
Added javadoc link to list of platform preferences
mstr2 Sep 5, 2023
02d6a2f
improve javadoc
mstr2 Sep 6, 2023
4172fa6
Update Eclipse .classpath file
mstr2 Sep 6, 2023
5081fda
Suppress empty change message in test application
mstr2 Sep 6, 2023
08a3692
Format arrays in test application
mstr2 Sep 6, 2023
2837e33
Merge branch 'master' into feature/platform-preferences
mstr2 Sep 6, 2023
9cfded0
Fire only a single invalidation event
mstr2 Sep 6, 2023
c8fd0e7
changes per review
mstr2 Sep 6, 2023
1eaea48
change test class name
mstr2 Sep 6, 2023
c736dee
Add signal handler for gtk-theme-name
mstr2 Sep 6, 2023
aae55f9
Remove javadocs
mstr2 Sep 6, 2023
cb81762
Handle key removals
mstr2 Sep 8, 2023
a893fa4
Merge branch 'master' into feature/platform-preferences
mstr2 Oct 29, 2023
8a5b213
changed parameter name
mstr2 Oct 29, 2023
7e826df
Javadoc change
mstr2 Oct 31, 2023
684f48b
formatting
mstr2 Oct 31, 2023
167d4e8
review changes
mstr2 Nov 1, 2023
ad74e39
changend bool comparison
mstr2 Nov 1, 2023
38177a8
review changes
mstr2 Nov 2, 2023
d012dec
removed extra newline
mstr2 Nov 3, 2023
9b607c6
Rename Appearance to ColorScheme
mstr2 Nov 10, 2023
6ec7e99
Doc changes
mstr2 Nov 17, 2023
3cc29e9
Add eager type checking for typed getters
mstr2 Nov 18, 2023
9a817d1
Remove Preferences.getPaint
mstr2 Nov 18, 2023
bb6071c
Replace HashMap with Map.ofEntries
mstr2 Nov 18, 2023
a3d0010
Support polymorphic values
mstr2 Nov 20, 2023
17b2b08
Use JLS 5.5.1 casting conversion rules
mstr2 Nov 24, 2023
9083d3e
flip S and T
mstr2 Nov 24, 2023
9adef99
address review comments
mstr2 Nov 24, 2023
57c8d0c
rename local variables
mstr2 Nov 24, 2023
bee1bae
typo
mstr2 Nov 24, 2023
89f1c8f
initialize field with NULL
mstr2 Nov 29, 2023
9cc3b4b
initialize field with NULL
mstr2 Nov 30, 2023
dbf24bf
check for pending exceptions in macOS PlatformSupport
mstr2 Dec 1, 2023
0572a36
check for pending exceptions in GTK PlatformSupport
mstr2 Dec 2, 2023
f291ea4
check for pending exceptions in Windows PlatformSupport
mstr2 Dec 2, 2023
a04a8c6
check for null return values of JNI methods
mstr2 Dec 4, 2023
0dff7a4
check return value of JNI functions
mstr2 Dec 4, 2023
6c37544
rename GLASS_CHECK_EXCEPTIONALLY_RETURN
mstr2 Dec 6, 2023
db393fc
null checking
mstr2 Dec 6, 2023
0759ddd
changed error condition check
mstr2 Dec 6, 2023
a2bd32d
consistently use NULL
mstr2 Dec 6, 2023
070eab5
swap message fields
mstr2 Dec 6, 2023
515395b
fixed bug in test application
mstr2 Dec 6, 2023
098ca17
fixed HighContrastScheme
mstr2 Dec 7, 2023
eac1d82
format arrays in test application, indicate added/removed prefs
mstr2 Dec 7, 2023
efdab27
renamed Windows.SPI.HighContrastOn to Windows.SPI.HighContrast
mstr2 Dec 7, 2023
20cccc5
query resource bundles for high-contrast schemes only on Windows
mstr2 Dec 7, 2023
6457503
javadoc
mstr2 Dec 7, 2023
3354782
removed unused import
mstr2 Dec 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -51,7 +51,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;

import javafx.application.Application;
@@ -726,55 +725,6 @@ public static void setPlatformUserAgentStylesheet(final String stylesheetUrl) {
}
}

/**
* Enumeration of possible high contrast scheme values.
*
* For each scheme, a theme key is defined. These keys can be
* used, for instance, in a resource bundle that defines the theme name values
* for supported locales.
*
* The high contrast feature may not be available on all platforms.
*/
public enum HighContrastScheme {
HIGH_CONTRAST_BLACK("high.contrast.black.theme"),
HIGH_CONTRAST_WHITE("high.contrast.white.theme"),
HIGH_CONTRAST_1("high.contrast.1.theme"),
HIGH_CONTRAST_2("high.contrast.2.theme");

private final String themeKey;
HighContrastScheme(String themeKey) {
this.themeKey = themeKey;
}

public String getThemeKey() {
return themeKey;
}

/**
* Given a theme name string, this method finds the possible enum constant
* for which the result of a function, applying its theme key, matches the theme name.
*
* An example of such function can be {@code ResourceBundle::getString},
* as {@link java.util.ResourceBundle#getString(String)} returns a string for
* the given key.
*
* @param keyFunction a {@link Function} that returns a string for a given theme key string.
* @param themeName a string with the theme name
* @return the name of the enum constant or null if not found
*/
public static String fromThemeName(Function<String, String> keyFunction, String themeName) {
if (keyFunction == null || themeName == null) {
return null;
}
for (HighContrastScheme item : values()) {
if (themeName.equalsIgnoreCase(keyFunction.apply(item.getThemeKey()))) {
return item.toString();
}
}
return null;
}
}

private static String accessibilityTheme;
public static boolean setAccessibilityTheme(String platformTheme) {

@@ -823,7 +773,12 @@ private static void _setAccessibilityTheme(String platformTheme) {
} else {
if (platformTheme != null) {
// The following names are Platform specific (Windows 7 and 8)
switch (HighContrastScheme.valueOf(platformTheme)) {
var highContrastScheme = WindowsHighContrastScheme.fromThemeName(platformTheme);
if (highContrastScheme == null) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: You could eliminate the null check if you defined a new "NONE" or "UNKNOWN" enum and restored the (no-op) default: on line 837. It's fine the way you have it, if you prefer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a NONE constant.


switch (highContrastScheme) {
case HIGH_CONTRAST_WHITE:
accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/blackOnWhite.css";
break;
@@ -834,7 +789,6 @@ private static void _setAccessibilityTheme(String platformTheme) {
case HIGH_CONTRAST_2: //TODO #2 should be green on black
accessibilityTheme = "com/sun/javafx/scene/control/skin/modena/yellowOnBlack.css";
break;
default:
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2023, 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 com.sun.javafx.application;

import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

/**
* Enumeration of possible high contrast scheme values.
* <p>
* For each scheme, a theme key is defined. These keys can be
* used in a resource bundle that defines the theme name values
* for supported locales.
* <p>
* The high contrast feature may not be available on all platforms.
*/
enum WindowsHighContrastScheme {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather keep the old name: HighContrastScheme: It is in a non-platform specific package, and it applies to all platforms (even only with NONE).
You could add a comment about HIGH_CONTRAST_* enum constants being only available on Windows.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it never applied to all platforms, it reflects exactly the Windows implementation of high-contrast schemes. It will be moved once we add style themes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok


HIGH_CONTRAST_BLACK("high.contrast.black.theme"),
HIGH_CONTRAST_WHITE("high.contrast.white.theme"),
HIGH_CONTRAST_1("high.contrast.1.theme"),
HIGH_CONTRAST_2("high.contrast.2.theme");

private static final List<ResourceBundle> resourceBundles = Arrays.stream(Locale.getAvailableLocales())
.map(locale -> ResourceBundle.getBundle("com/sun/glass/ui/win/themes", locale))
.distinct()
.toList();

private final String themeKey;

WindowsHighContrastScheme(String themeKey) {
this.themeKey = themeKey;
}

public String getThemeKey() {
return themeKey;
}

/**
* Given a theme name string, this method finds the possible enum constant
* for which the result of a function, applying its theme key, matches the theme name.
*
* @param themeName a string with the localized theme name (for the locale of the OS, not the JVM)
* @return the enum constant or null if not found
*/
public static WindowsHighContrastScheme fromThemeName(String themeName) {
if (themeName == null) {
return null;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is called only from PlatformImpl that already does the null check on the the string. In general, null checks should be done on the "outer most layer" and then all the inner layers can rely on the value being non-null.

Is this method expected to be called from other places as well? If not, the method can be made package visible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method now returns NONE when another constant doesn't apply. I've removed the public modifier as you've suggested.


// Iterate over all resource bundles and try to find a value that matches the theme name
// we got from the OS. We can't just look in the properties file for the current locale,
// since we might be running on a JVM with a locale that is different from the OS.
for (WindowsHighContrastScheme item : values()) {
for (ResourceBundle resourceBundle : resourceBundles) {
Comment on lines +83 to +84
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on how often this is called, might it be worth caching a Map<String,WindowsHighContrastScheme> whose keys are the OS theme names and values are the corresponding enum values? This could be a follow-up enhancement if it were deemed important enough, but maybe it doesn't matter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class will probably change a bit and be moved around if and when we add style themes, so I think we can revisit it then.

if (themeName.equalsIgnoreCase(resourceBundle.getString(item.getThemeKey()))) {
return item;
}
}
}

return null;
}
}
35 changes: 22 additions & 13 deletions tests/manual/events/PlatformPreferencesChangedTest.java
Original file line number Diff line number Diff line change
@@ -69,7 +69,9 @@ public void start(Stage stage) {
new VBox(
new Label("1. On a supported platform, change any of the platform preferences."),
new Label(" See javafx.application.Platform.Preferences for a list of supported platforms.")),
new Label("2. Observe whether the changed preferences are reported in the log below."),
new VBox(
new Label("2. Observe whether the changed preferences are reported in the log below."),
new Label(" Added or removed preferences are marked with a plus or minus sign.")),
new Label("3. Click \"Pass\" if the changes were correctly reported, otherwise click \"Fail\"."),
new HBox(5, passButton, failButton, clearButton)
));
@@ -87,7 +89,13 @@ public void start(Stage stage) {
});

Platform.getPreferences().addListener((MapChangeListener<String, Object>)change -> {
appendText(textArea, "\t" + change.getKey() + " = " + change.getValueAdded());
if (change.wasRemoved() && change.wasAdded()) {
appendText(textArea, "\t" + formatEntry(change.getKey(), change.getValueAdded()));
} else if (change.wasRemoved()) {
appendText(textArea, "\t-" + change.getKey());
} else {
appendText(textArea, "\t+" + formatEntry(change.getKey(), change.getValueAdded()));
}
});

stage.setScene(new Scene(root));
@@ -105,16 +113,17 @@ public static void main(String[] args) {
}

private static String formatPrefs(Set<Map.Entry<String, Object>> prefs) {
String entries = prefs.stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> {
if (entry.getValue() instanceof Object[] array) {
return entry.getKey() + "=" + Arrays.toString(array);
}
return entry.getKey() + "=" + entry.getValue();
})
.collect(Collectors.joining("\r\n\t"));

return "\r\n\t" + entries;
return "\r\n\t" + prefs.stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> formatEntry(entry.getKey(), entry.getValue()))
.collect(Collectors.joining("\r\n\t"));
}

private static String formatEntry(String key, Object value) {
if (value instanceof Object[] array) {
return key + "=" + Arrays.toString(array);
}

return key + "=" + value;
}
}