GP-1981 Added Gui.registerFont() and more java docs

This commit is contained in:
ghidragon
2022-08-19 21:09:00 -04:00
parent b837bd3aa3
commit 3f8096014f
25 changed files with 289 additions and 50 deletions
@@ -18,11 +18,7 @@ package docking.theme.gui;
import java.awt.Font;
import java.beans.PropertyChangeListener;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.UIResource;
import docking.options.editor.FontPropertyEditor;
import docking.theme.*;
import generic.theme.*;
/**
@@ -45,12 +41,6 @@ public class FontValueEditor extends ThemeValueEditor<Font> {
@Override
protected ThemeValue<Font> createNewThemeValue(String id, Font font) {
// We need user changed values to be UIResources, otherwise the next time they change
// the value, it won't be honored because setting UIDefaults only affects components
// that have current UIResource fonts (So that programatic settings won't be overridden)
if (!(font instanceof UIResource)) {
font = new FontUIResource(font);
}
return new FontValue(id, font);
}
@@ -15,10 +15,10 @@
*/
package generic.theme;
import java.awt.Color;
import java.awt.Font;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
@@ -280,9 +280,9 @@ public class Gui {
// update all java LookAndFeel fonts affected by this changed
String id = newValue.getId();
Set<String> affectedJavaFontIds = findAffectedJavaFontIds(id);
Set<String> changedFontIds = findChangedJavaFontIds(id);
Font newFont = newValue.get(currentValues);
lookAndFeelManager.updateFonts(id, affectedJavaFontIds, newFont);
lookAndFeelManager.fontsChanged(changedFontIds, newFont);
}
/**
@@ -308,7 +308,7 @@ public class Gui {
notifyThemeChanged(new ColorChangedThemeEvent(currentValues, newValue));
// now update the ui
lookAndFeelManager.updateColors();
lookAndFeelManager.colorsChanged();
}
/**
@@ -337,9 +337,9 @@ public class Gui {
// now update the ui
// update all java LookAndFeel icons affected by this changed
String id = newValue.getId();
Set<String> affectedJavaIconIds = findAffectedJavaIconIds(id);
Set<String> changedIconIds = findChangedJavaIconIds(id);
Icon newIcon = newValue.get(currentValues);
lookAndFeelManager.updateIcons(id, affectedJavaIconIds, newIcon);
lookAndFeelManager.iconsChanged(changedIconIds, newIcon);
}
/**
@@ -574,6 +574,16 @@ public class Gui {
themePropertiesLoader = loader;
}
/**
* Binds the component to the font identified by the given font id. Whenever the font for
* the font id changes, the component will updated with the new font.
* @param component the component to set/update the font
* @param fontId the id of the font to register with the given component
*/
public static void registerFont(Component component, String fontId) {
lookAndFeelManager.registerFont(component, fontId);
}
private static void installFlatLookAndFeels() {
UIManager.installLookAndFeel(LafType.FLAT_LIGHT.getName(), FlatLightLaf.class.getName());
UIManager.installLookAndFeel(LafType.FLAT_DARK.getName(), FlatDarkLaf.class.getName());
@@ -727,7 +737,7 @@ public class Gui {
}
}
private static Set<String> findAffectedJavaFontIds(String id) {
private static Set<String> findChangedJavaFontIds(String id) {
Set<String> affectedIds = new HashSet<>();
List<FontValue> fonts = javaDefaults.getFonts();
for (FontValue fontValue : fonts) {
@@ -740,7 +750,7 @@ public class Gui {
return affectedIds;
}
private static Set<String> findAffectedJavaIconIds(String id) {
private static Set<String> findChangedJavaIconIds(String id) {
Set<String> affectedIds = new HashSet<>();
List<IconValue> icons = javaDefaults.getIcons();
for (IconValue iconValue : icons) {
@@ -99,6 +99,13 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
return getUnresolvedReferenceValue(id);
}
/**
* Returns true if this ThemeValue derives its value from the given ancestorId.
* @param ancestorId the id to test if this Theme value inherits from
* @param values the set of values used to resolve indirect references to attempt to trace
* back to the given ancestor id
* @return true if this ThemeValue derives its value from the given ancestorId.
*/
public boolean inheritsFrom(String ancestorId, GThemeValueMap values) {
if (refId == null) {
return false;
@@ -15,9 +15,15 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the Motif {@link LookAndFeel} and the standard (light)
* application defaults.
*/
public class CDEMotifTheme extends DiscoverableGTheme {
public CDEMotifTheme() {
@@ -15,9 +15,14 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the FlatDarcula {@link LookAndFeel} and the dark application defaults.
*/
public class FlatDarculaTheme extends DiscoverableGTheme {
public FlatDarculaTheme() {
super("Flat Darcula Theme", LafType.FLAT_DARCULA, true);
@@ -15,9 +15,14 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the FlatDark {@link LookAndFeel} and the dark application defaults.
*/
public class FlatDarkTheme extends DiscoverableGTheme {
public FlatDarkTheme() {
super("Flat Dark Theme", LafType.FLAT_DARK, true);
@@ -15,9 +15,14 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the FlatLight {@link LookAndFeel} and the dark application defaults.
*/
public class FlatLightTheme extends DiscoverableGTheme {
public FlatLightTheme() {
@@ -15,9 +15,15 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the GTK+ {@link LookAndFeel} and the standard (light)
* application defaults.
*/
public class GTKTheme extends DiscoverableGTheme {
public GTKTheme() {
@@ -25,7 +25,7 @@ import generic.theme.ColorValue;
import generic.theme.GThemeValueMap;
/**
* Maps Java UIDefaults color ids to parent color ids
* Maps Java UIDefaults color ids relationships to an id it inherits from
*/
public class JavaColorMapping {
private static Map<String, String> map = Map.ofEntries(
@@ -25,7 +25,7 @@ import generic.theme.FontValue;
import generic.theme.GThemeValueMap;
/**
* Maps Java UIDefaults color ids to parent color ids
* Maps Java UIDefaults font ids relationships to an id it inherits from
*/
public class JavaFontMapping {
private final static String BUTTON_GROUP = "ButtonComponents.font";
@@ -15,9 +15,15 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the Aqua {@link LookAndFeel} and the standard (light)
* application defaults.
*/
public class MacTheme extends DiscoverableGTheme {
public MacTheme() {
@@ -15,9 +15,15 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the Metal {@link LookAndFeel} and the standard (light)
* application defaults.
*/
public class MetalTheme extends DiscoverableGTheme {
public MetalTheme() {
@@ -15,9 +15,15 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the Nimbus {@link LookAndFeel} and the standard (light)
* application defaults.
*/
public class NimbusTheme extends DiscoverableGTheme {
public NimbusTheme() {
@@ -15,9 +15,15 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the Windows Classic {@link LookAndFeel} and the standard (light)
* application defaults.
*/
public class WindowsClassicTheme extends DiscoverableGTheme {
public WindowsClassicTheme() {
@@ -15,9 +15,15 @@
*/
package generic.theme.builtin;
import javax.swing.LookAndFeel;
import generic.theme.DiscoverableGTheme;
import generic.theme.LafType;
/**
* Built-in GTheme that uses the Windows {@link LookAndFeel} and the standard (light)
* application defaults.
*/
public class WindowsTheme extends DiscoverableGTheme {
public WindowsTheme() {
@@ -0,0 +1,60 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.theme.laf;
import java.awt.Component;
import java.awt.Font;
import generic.theme.Gui;
import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
/**
* Maintains a week set of components associated with a given font id. Whenever the font changes
* for the font id, this class will update the component's font to the new value.
*/
public class ComponentFontRegistry {
private WeakSet<Component> components = WeakDataStructureFactory.createCopyOnReadWeakSet();
private String fontId;
/**
* Constructs a registry for components bound to the given font id
* @param fontId the id of the font to update the containing components
*/
public ComponentFontRegistry(String fontId) {
this.fontId = fontId;
}
/**
* Adds a {@link Component} to the weak set of components whose font should be updated when
* the underlying font changes for this registry's font id.
* @param component the component to add
*/
public void addComponent(Component component) {
component.setFont(Gui.getFont(fontId));
components.add(component);
}
/**
* Updates the font for all components bound to this registry's font id.
*/
public void updateComponentFonts() {
Font font = Gui.getFont(fontId);
for (Component component : components) {
component.setFont(font);
}
}
}
@@ -19,6 +19,9 @@ import javax.swing.UIManager;
import generic.theme.LafType;
/**
* Common {@link LookAndFeelInstaller} for any of the "Flat" lookAndFeels
*/
public class FlatLookAndFeelInstaller extends LookAndFeelInstaller {
public FlatLookAndFeelInstaller(LafType lookAndFeelType) {
@@ -0,0 +1,28 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.theme.laf;
import java.awt.Font;
/**
* Font subclass for creating an non UIResource Font from a FontUIResource. (The Font constructor
* that takes a font was protected)
*/
public class FontNonUiResource extends Font {
public FontNonUiResource(Font font) {
super(font);
}
}
@@ -21,6 +21,7 @@ import java.util.List;
import javax.swing.Icon;
import javax.swing.UIDefaults;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import generic.theme.*;
@@ -42,16 +43,18 @@ public class GNimbusLookAndFeel extends NimbusLookAndFeel {
defaults.put(id, Gui.getGColorUiResource(id));
}
// only replace fonts that have been changed by the theme
// put fonts back into defaults in case they have been changed by the current theme
for (FontValue fontValue : javaDefaults.getFonts()) {
String id = fontValue.getId();
Font font = Gui.getFont(id);
defaults.put(id, font);
defaults.put(id, new FontUIResource(font));
}
// only replace icons that have been changed by the theme
// put icons back into defaults in case they have been changed by the current theme
for (IconValue iconValue : javaDefaults.getIcons()) {
String id = iconValue.getId();
// because some icons are weird, put raw icons into defaults, only use GIcons for
// setting Icons explicitly on components
Icon icon = Gui.getRawIcon(id, true);
defaults.put(id, icon);
}
@@ -76,7 +79,7 @@ public class GNimbusLookAndFeel extends NimbusLookAndFeel {
LookAndFeelInstaller.getLookAndFeelIdsForType(defaults, Font.class);
for (String id : fontIds) {
Font font = defaults.getFont(id);
FontValue value = new FontValue(id, font);
FontValue value = new FontValue(id, LookAndFeelInstaller.fromUiResource(font));
javaDefaults.addFont(value);
}
List<String> iconIds =
@@ -18,7 +18,7 @@ package generic.theme.laf;
import generic.theme.LafType;
/**
* Common {@link LookAndFeelInstaller} for any of the "Flat" lookAndFeels
* Common {@link LookAndFeelManager} for any of the "Flat" lookAndFeels
*/
public class GenericFlatLookAndFeelManager extends LookAndFeelManager {
@@ -15,10 +15,14 @@
*/
package generic.theme.laf;
import javax.swing.LookAndFeel;
import javax.swing.UnsupportedLookAndFeelException;
import generic.theme.LafType;
/**
* LookAndFeelInstaller for the GTK {@link LookAndFeel}
*/
public class GtkLookAndFeelInstaller extends LookAndFeelInstaller {
public GtkLookAndFeelInstaller() {
@@ -17,6 +17,9 @@ package generic.theme.laf;
import generic.theme.LafType;
/**
* {@link LookAndFeelManager} for GTK
*/
public class GtkLookAndFeelManager extends LookAndFeelManager {
public GtkLookAndFeelManager() {
@@ -22,6 +22,8 @@ import java.util.Map.Entry;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.UIResource;
import org.apache.commons.collections4.IteratorUtils;
@@ -107,24 +109,26 @@ public class LookAndFeelInstaller {
// allow being wrapped like colors do.
for (ColorValue colorValue : javaDefaults.getColors()) {
String id = colorValue.getId();
GColorUIResource gColor = Gui.getGColorUiResource(id);
defaults.put(id, gColor);
defaults.put(id, Gui.getGColorUiResource(id));
}
// For fonts and icons we only want to install values that have been changed by
// the theme
// put fonts back into defaults in case they have been changed by the current theme
for (FontValue fontValue : javaDefaults.getFonts()) {
String id = fontValue.getId();
FontValue themeValue = theme.getFont(id);
if (themeValue != null) {
Font font = Gui.getFont(id);
defaults.put(id, font);
defaults.put(id, new FontUIResource(font));
}
}
// put icons back into defaults in case they have been changed by the current theme
for (IconValue iconValue : javaDefaults.getIcons()) {
String id = iconValue.getId();
IconValue themeValue = theme.getIcon(id);
if (themeValue != null) {
// because some icons are weird, put raw icons into defaults, only use GIcons for
// setting Icons explicitly on components
Icon icon = Gui.getRawIcon(id, true);
defaults.put(id, icon);
}
@@ -135,19 +139,20 @@ public class LookAndFeelInstaller {
return extractJavaDefaults(UIManager.getDefaults());
}
protected static GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
protected GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
GThemeValueMap values = new GThemeValueMap();
// for now, just doing color properties.
List<String> ids = getLookAndFeelIdsForType(defaults, Color.class);
for (String id : ids) {
// only use standard java colors here to avoid weird issues (such as GColor not
// resolving or ColorUIResource not being honored. Later we will go back
// and fix up the java defaults to use standard java color indirection
values.addColor(new ColorValue(id, getNormalizedColor(UIManager.getColor(id))));
// convert UIResource color to regular colors so if used, they don't get wiped
// out when we update the UIs
values.addColor(new ColorValue(id, fromUiResource(UIManager.getColor(id))));
}
ids = getLookAndFeelIdsForType(defaults, Font.class);
for (String id : ids) {
values.addFont(new FontValue(id, UIManager.getFont(id)));
// convert UIResource fonts to regular fonts so if used, they don't get wiped
// out when we update UIs
values.addFont(new FontValue(id, fromUiResource(UIManager.getFont(id))));
}
ids = getLookAndFeelIdsForType(defaults, Icon.class);
for (String id : ids) {
@@ -286,13 +291,20 @@ public class LookAndFeelInstaller {
installPopupMenuSettingsOverride();
}
private static Color getNormalizedColor(Color color) {
public static Color fromUiResource(Color color) {
if (color.getClass() == Color.class) {
return color;
}
return new Color(color.getRGB(), true);
}
public static Font fromUiResource(Font font) {
if (font instanceof UIResource) {
return new FontNonUiResource(font);
}
return font;
}
private void cleanUiDefaults() {
GThemeValueMap javaDefaults = Gui.getJavaDefaults();
if (javaDefaults == null) {
@@ -15,12 +15,12 @@
*/
package generic.theme.laf;
import java.awt.Font;
import java.awt.Window;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import generic.theme.*;
@@ -30,6 +30,7 @@ import generic.theme.*;
public abstract class LookAndFeelManager {
private LafType laf;
private Map<String, ComponentFontRegistry> fontRegistryMap = new HashMap<>();
protected LookAndFeelManager(LafType laf) {
this.laf = laf;
@@ -37,10 +38,24 @@ public abstract class LookAndFeelManager {
protected abstract LookAndFeelInstaller getLookAndFeelInstaller();
/**
* Returns the {@link LafType} managed by this manager.
* @return the {@link LafType}
*/
public LafType getLookAndFeelType() {
return laf;
}
/**
* Installs the {@link LookAndFeel}
* @throws ClassNotFoundException if the <code>LookAndFeel</code>
* class could not be found
* @throws InstantiationException if a new instance of the class
* couldn't be created
* @throws IllegalAccessException if the class or initializer isn't accessible
* @throws UnsupportedLookAndFeelException if
* <code>lnf.isSupportedLookAndFeel()</code> is false
*/
public void installLookAndFeel() throws ClassNotFoundException, InstantiationException,
IllegalAccessException, UnsupportedLookAndFeelException {
@@ -49,14 +64,26 @@ public abstract class LookAndFeelManager {
updateComponentUis();
}
/**
* Called when all colors, fonts, and icons may have changed
* @param javaDefaults the current set of java defaults so that those ids can be updated
* special as needed by the current {@link LookAndFeel}
*/
public void resetAll(GThemeValueMap javaDefaults) {
GColor.refreshAll();
GIcon.refreshAll();
resetIcons(javaDefaults);
resetFonts(javaDefaults);
updateAllRegisteredComponentFonts();
updateComponentUis();
}
private void updateAllRegisteredComponentFonts() {
for (ComponentFontRegistry register : fontRegistryMap.values()) {
register.updateComponentFonts();
}
}
private void resetFonts(GThemeValueMap javaDefaults) {
List<FontValue> fonts = javaDefaults.getFonts();
UIDefaults defaults = UIManager.getDefaults();
@@ -83,15 +110,24 @@ public abstract class LookAndFeelManager {
}
}
public void updateColors() {
/**
* Called when one or more colors have changed.
*/
public void colorsChanged() {
GColor.refreshAll();
repaintAll();
}
public void updateIcons(String id, Set<String> affectedJavaIds, Icon newIcon) {
if (!affectedJavaIds.isEmpty()) {
/**
* Called when one or more icons have changed.
* @param id the id of primary icon that changed
* @param changedIconIds
* @param newIcon
*/
public void iconsChanged(Set<String> changedIconIds, Icon newIcon) {
if (!changedIconIds.isEmpty()) {
UIDefaults defaults = UIManager.getDefaults();
for (String javaIconId : affectedJavaIds) {
for (String javaIconId : changedIconIds) {
defaults.put(javaIconId, newIcon);
}
updateComponentUis();
@@ -100,17 +136,33 @@ public abstract class LookAndFeelManager {
repaintAll();
}
public void updateFonts(String id, Set<String> affectedJavaIds, Font newFont) {
if (!affectedJavaIds.isEmpty()) {
/**
* Called when one or more fonts have changed.
* @param changedJavaFontIds the set of Java Font ids that are affected by this change
* @param newFont the new font for the given ids
*/
public void fontsChanged(Set<String> changedJavaFontIds, Font newFont) {
if (!changedJavaFontIds.isEmpty()) {
UIDefaults defaults = UIManager.getDefaults();
for (String javaFontId : affectedJavaIds) {
newFont = new FontUIResource(newFont);
for (String javaFontId : changedJavaFontIds) {
defaults.put(javaFontId, newFont);
}
updateComponentFonts(changedJavaFontIds);
updateComponentUis();
}
repaintAll();
}
protected void updateComponentFonts(Set<String> changedFontIds) {
for (String javaFontId : changedFontIds) {
ComponentFontRegistry register = fontRegistryMap.get(javaFontId);
if (register != null) {
register.updateComponentFonts();
}
}
}
protected void updateComponentUis() {
for (Window window : Window.getWindows()) {
SwingUtilities.updateComponentTreeUI(window);
@@ -123,4 +175,11 @@ public abstract class LookAndFeelManager {
}
}
public void registerFont(Component c, String fontId) {
ComponentFontRegistry register =
fontRegistryMap.computeIfAbsent(fontId, id -> new ComponentFontRegistry(id));
register.addComponent(c);
}
}
@@ -41,14 +41,17 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
reinstallNimubus();
}
public void updateFonts(String id, Set<String> affectedJavaIds, Font newFont) {
@Override
public void fontsChanged(Set<String> affectedJavaIds, Font newFont) {
if (!affectedJavaIds.isEmpty()) {
reinstallNimubus();
updateComponentFonts(affectedJavaIds);
}
repaintAll();
}
public void updateIcons(String id, Set<String> affectedJavaIds, Icon newIcon) {
@Override
public void iconsChanged(Set<String> affectedJavaIds, Icon newIcon) {
if (!affectedJavaIds.isEmpty()) {
reinstallNimubus();
}