GP-1981 fixed Nimbus (yet again!) to update values. This time went all

the way to re-installing the LookAndFeel
This commit is contained in:
ghidragon
2022-08-18 14:55:33 -04:00
parent bd0c1312ec
commit 3d9c5f0242
21 changed files with 1199 additions and 424 deletions
@@ -23,7 +23,6 @@ import java.util.function.Supplier;
import javax.swing.Icon;
import javax.swing.JLabel;
import docking.theme.*;
import docking.widgets.table.*;
import generic.theme.*;
import ghidra.docking.settings.Settings;
@@ -194,13 +193,13 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
return "<No Value>";
}
if (resolvedColor.refId() != null) {
return resolvedColor.refId();
return "[" + resolvedColor.refId() + "]";
}
Color color = resolvedColor.color();
String text = WebColors.toString(color, false);
String name = WebColors.toWebColorName(color);
if (name != null) {
text += " [" + name + "]";
text += " (" + name + ")";
}
return text;
}
@@ -94,7 +94,7 @@ public class ThemeDialog extends DialogComponentProvider {
if (result == OptionDialog.YES_OPTION) {
return ThemeUtils.saveThemeChanges();
}
Gui.reloadGhidraDefaults();
Gui.restoreThemeValues();
}
return true;
}
@@ -74,6 +74,19 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
return null;
}
private String getValueText(ResolvedFont resolvedFont) {
if (resolvedFont == null) {
return "<No Value>";
}
Font font = resolvedFont.font();
String fontString = ThemeWriter.fontToString(font);
if (resolvedFont.refId() != null) {
return "[" + resolvedFont.refId() + "]";//+ " [" + fontString + "]";
}
return fontString;
}
class IdColumn extends AbstractDynamicTableColumn<FontValue, String, Object> {
@Override
@@ -138,7 +151,7 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
if (v2 == null) {
return -1;
}
return v1.font().toString().compareTo(v2.font().toString());
return getValueText(v1).compareTo(getValueText(v2));
};
}
@@ -166,19 +179,6 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
return label;
}
private String getValueText(ResolvedFont resolvedFont) {
if (resolvedFont == null) {
return "<No Value>";
}
Font font = resolvedFont.font();
String fontString = ThemeWriter.fontToString(font);
if (resolvedFont.refId() != null) {
return resolvedFont.refId() + " [" + fontString + "]";
}
return fontString;
}
@Override
public String getFilterString(ResolvedFont fontValue, Settings settings) {
return getValueText(fontValue);
@@ -20,18 +20,28 @@ package generic.theme;
*/
public class ColorChangedThemeEvent extends ThemeEvent {
private final ColorValue color;
private final GThemeValueMap values;
/**
* Constructor
* @param values the set of theme values used to resolve indirect references
* @param color the new {@link ColorValue} for the color id that changed
*/
public ColorChangedThemeEvent(ColorValue color) {
public ColorChangedThemeEvent(GThemeValueMap values, ColorValue color) {
this.values = values;
this.color = color;
}
@Override
public boolean isColorChanged(String id) {
return id.equals(color.getId());
if (id.equals(color.getId())) {
return true;
}
ColorValue testValue = values.getColor(id);
if (testValue == null) {
return false;
}
return testValue.inheritsFrom(color.getId(), values);
}
@Override
@@ -41,9 +41,6 @@ public class ColorValue extends ThemeValue<Color> {
*/
public ColorValue(String id, Color value) {
super(id, getRefId(value), getRawColor(value));
if (value instanceof GColor) {
throw new IllegalArgumentException("Can't use GColor as the value!");
}
}
/**
@@ -20,18 +20,28 @@ package generic.theme;
*/
public class FontChangedThemeEvent extends ThemeEvent {
private final FontValue font;
private final GThemeValueMap values;
/**
* Constructor
* @param values the set of theme values used to resolve indirect references
* @param font the new {@link FontValue} for the font id that changed
*/
public FontChangedThemeEvent(FontValue font) {
public FontChangedThemeEvent(GThemeValueMap values, FontValue font) {
this.values = values;
this.font = font;
}
@Override
public boolean isFontChanged(String id) {
return id.equals(font.getId());
if (id.equals(font.getId())) {
return true;
}
FontValue testValue = values.getFont(id);
if (testValue == null) {
return false;
}
return testValue.inheritsFrom(font.getId(), values);
}
@Override
@@ -39,6 +39,18 @@ import utilities.util.reflection.ReflectionUtilities;
/**
* Provides a static set of methods for globally managing application themes and their values.
* <P>
* The basic idea is that all the colors, fonts, and icons used in an application should be
* accessed indirectly via an "id" string. Then the actual color, font, or icon can be changed
* without changing the source code. The default mapping of the id strings to a value is defined
* in <name>.theme.properties files which are dynamically discovered by searching the module's
* data directory. Also, these files can optionally define a dark default value for an id which
* would replace the standard default value in the event that the current theme specifies that it
* is a dark theme. Themes are used to specify the application's {@link LookAndFeel}, whether or
* not it is dark, and any customized values for colors, fonts, or icons. There are several
* "built-in" themes, one for each supported {@link LookAndFeel}, but additional themes can
* be defined and stored in the users application home directory as a <name>.theme file.
*
*/
public class Gui {
public static final String THEME_DIR = "themes";
@@ -90,7 +102,7 @@ public class Gui {
public static void reloadGhidraDefaults() {
loadThemeDefaults();
buildCurrentValues();
lookAndFeelManager.update();
lookAndFeelManager.resetAll(javaDefaults);
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
}
@@ -100,7 +112,7 @@ public class Gui {
*/
public static void restoreThemeValues() {
buildCurrentValues();
lookAndFeelManager.update();
lookAndFeelManager.resetAll(javaDefaults);
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
}
@@ -264,13 +276,13 @@ public class Gui {
updateChangedValuesMap(currentValue, newValue);
currentValues.addFont(newValue);
// all fonts are direct (there is no GFont), so to we need to update the
// UiDefaults for java fonts. Ghidra fonts are expected to be "on the fly" (they
// call Gui.getFont(id) for every use.
notifyThemeChanged(new FontChangedThemeEvent(currentValues, newValue));
// update all java LookAndFeel fonts affected by this changed
String id = newValue.getId();
boolean isJavaFont = javaDefaults.containsFont(id);
lookAndFeelManager.updateFont(id, newValue.get(currentValues), isJavaFont);
notifyThemeChanged(new FontChangedThemeEvent(newValue));
Set<String> affectedJavaFontIds = findAffectedJavaFontIds(id);
Font newFont = newValue.get(currentValues);
lookAndFeelManager.updateFonts(id, affectedJavaFontIds, newFont);
}
/**
@@ -292,12 +304,11 @@ public class Gui {
return;
}
updateChangedValuesMap(currentValue, newValue);
currentValues.addColor(newValue);
String id = newValue.getId();
boolean isJavaColor = javaDefaults.containsColor(id);
lookAndFeelManager.updateColor(id, newValue.get(currentValues), isJavaColor);
notifyThemeChanged(new ColorChangedThemeEvent(newValue));
notifyThemeChanged(new ColorChangedThemeEvent(currentValues, newValue));
// now update the ui
lookAndFeelManager.updateColors();
}
/**
@@ -321,10 +332,14 @@ public class Gui {
updateChangedValuesMap(currentValue, newValue);
currentValues.addIcon(newValue);
notifyThemeChanged(new IconChangedThemeEvent(currentValues, newValue));
// now update the ui
// update all java LookAndFeel icons affected by this changed
String id = newValue.getId();
boolean isJavaIcon = javaDefaults.containsIcon(id);
lookAndFeelManager.updateIcon(id, newValue.get(currentValues), isJavaIcon);
notifyThemeChanged(new IconChangedThemeEvent(newValue));
Set<String> affectedJavaIconIds = findAffectedJavaIconIds(id);
Icon newIcon = newValue.get(currentValues);
lookAndFeelManager.updateIcons(id, affectedJavaIconIds, newIcon);
}
/**
@@ -378,14 +393,8 @@ public class Gui {
* @return a fixed up version of the given map with relationships restored where possible
*/
public static GThemeValueMap fixupJavaDefaultsInheritence(GThemeValueMap map) {
List<ColorValue> colors = map.getColors();
JavaColorMapping mapping = new JavaColorMapping();
for (ColorValue value : colors) {
ColorValue mapped = mapping.map(map, value);
if (mapped != null) {
map.addColor(mapped);
}
}
JavaColorMapping.fixupJavaDefaultsInheritence(map);
JavaFontMapping.fixupJavaDefaultsInheritence(map);
return map;
}
@@ -717,4 +726,30 @@ public class Gui {
changedValuesMap.addIcon(currentValue);
}
}
private static Set<String> findAffectedJavaFontIds(String id) {
Set<String> affectedIds = new HashSet<>();
List<FontValue> fonts = javaDefaults.getFonts();
for (FontValue fontValue : fonts) {
String fontId = fontValue.getId();
FontValue currentFontValue = currentValues.getFont(fontId);
if (fontId.equals(id) || currentFontValue.inheritsFrom(id, currentValues)) {
affectedIds.add(fontId);
}
}
return affectedIds;
}
private static Set<String> findAffectedJavaIconIds(String id) {
Set<String> affectedIds = new HashSet<>();
List<IconValue> icons = javaDefaults.getIcons();
for (IconValue iconValue : icons) {
String iconId = iconValue.getId();
if (iconId.equals(id) || iconValue.inheritsFrom(id, currentValues)) {
affectedIds.add(iconId);
}
}
return affectedIds;
}
}
@@ -19,19 +19,28 @@ package generic.theme;
* {@link ThemeEvent} for when an icon changes for exactly one icon id.
*/
public class IconChangedThemeEvent extends ThemeEvent {
private final GThemeValueMap values;
private final IconValue icon;
/**
* Constructor
* @param icon the new {@link IconValue} for the icon id that changed
*/
public IconChangedThemeEvent(IconValue icon) {
public IconChangedThemeEvent(GThemeValueMap values, IconValue icon) {
this.values = values;
this.icon = icon;
}
@Override
public boolean isIconChanged(String id) {
return id.equals(icon.getId());
if (id.equals(icon.getId())) {
return true;
}
IconValue testValue = values.getIcon(id);
if (testValue == null) {
return false;
}
return testValue.inheritsFrom(icon.getId(), values);
}
@Override
@@ -29,7 +29,7 @@ import resources.ResourceManager;
public class IconValue extends ThemeValue<Icon> {
static final String ICON_ID_PREFIX = "icon.";
public static final String LAST_RESORT_DEFAULT = "images/bomb.gif";
public static final Icon LAST_RESORT_DEFAULT = ResourceManager.getDefaultIcon();
private static final String EXTERNAL_PREFIX = "[icon]";
@@ -42,10 +42,6 @@ public class IconValue extends ThemeValue<Icon> {
*/
public IconValue(String id, Icon icon) {
super(id, getRefId(icon), getRawIcon(icon));
if (icon instanceof GIcon) {
throw new IllegalArgumentException("Can't use GIcon as the value!");
}
}
/**
@@ -67,7 +63,7 @@ public class IconValue extends ThemeValue<Icon> {
protected Icon getUnresolvedReferenceValue(String id) {
Msg.warn(this,
"Could not resolve indirect icon path for" + id + ", using last resort default");
return ResourceManager.getDefaultIcon();
return LAST_RESORT_DEFAULT;
}
@Override
@@ -71,33 +71,63 @@ public abstract class ThemeValue<T> implements Comparable<ThemeValue<T>> {
/**
* Returns the T value for this instance, following references as needed. Uses the given
* preferredValues map to resolve references.
* @param preferredValues the {@link GThemeValueMap} used to resolve references if this
* @param values the {@link GThemeValueMap} used to resolve references if this
* instance doesn't have an actual value.
* @return the T value for this instance, following references as needed.
*/
public T get(GThemeValueMap preferredValues) {
return doGetValue(preferredValues);
}
public T get(GThemeValueMap values) {
if (value != null) {
return value;
}
private T doGetValue(GThemeValueMap values) {
ThemeValue<T> result = this;
Set<String> visitedKeys = new HashSet<>();
visitedKeys.add(id); // seed with my id, we don't want to see that key again
visitedKeys.add(id);
ThemeValue<T> parent = getReferredValue(values, refId);
// loop resolving indirect references
while (result != null) {
if (result.value != null) {
return result.value;
while (parent != null) {
if (parent.value != null) {
return parent.value;
}
if (visitedKeys.contains(result.refId)) {
visitedKeys.add(parent.id);
if (visitedKeys.contains(parent.refId)) {
Msg.warn(this, "Theme value reference loop detected for key: " + id);
return getUnresolvedReferenceValue(id);
}
result = getReferredValue(values, result.refId);
parent = getReferredValue(values, parent.refId);
}
return getUnresolvedReferenceValue(id);
}
public boolean inheritsFrom(String ancestorId, GThemeValueMap values) {
if (refId == null) {
return false;
}
if (refId.equals(ancestorId)) {
return true;
}
Set<String> visitedKeys = new HashSet<>();
visitedKeys.add(id);
ThemeValue<T> parent = getReferredValue(values, refId);
// loop resolving indirect references
while (parent != null) {
if (parent.refId == null) {
return false;
}
if (parent.refId.equals(ancestorId)) {
return true;
}
visitedKeys.add(parent.id);
if (visitedKeys.contains(parent.refId)) {
return false;
}
parent = getReferredValue(values, parent.refId);
}
return false;
}
/**
* Returns the T to be used if the indirect reference couldn't be resolved.
* @param unresolvedId the id that couldn't be resolved
@@ -15,8 +15,10 @@
*/
package generic.theme.builtin;
import static java.util.Map.*;
import java.awt.Color;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import generic.theme.ColorValue;
@@ -26,203 +28,208 @@ import generic.theme.GThemeValueMap;
* Maps Java UIDefaults color ids to parent color ids
*/
public class JavaColorMapping {
private Map<String, String> map = new HashMap<>();
public JavaColorMapping() {
// color relationships mined from BasicLookAndFeel
map.put("Button.background", "control");
map.put("Button.foreground", "controlText");
map.put("Button.shadow", "controlShadow");
map.put("Button.darkShadow", "controlDkShadow");
map.put("Button.light", "controlHighlight");
map.put("Button.highlight", "controlLtHighlight");
map.put("ToggleButton.background", "control");
map.put("ToggleButton.foreground", "controlText");
map.put("ToggleButton.shadow", "controlShadow");
map.put("ToggleButton.darkShadow", "controlDkShadow");
map.put("ToggleButton.light", "controlHighlight");
map.put("ToggleButton.highlight", "controlLtHighlight");
map.put("RadioButton.background", "control");
map.put("RadioButton.foreground", "controlText");
map.put("RadioButton.shadow", "controlShadow");
map.put("RadioButton.darkShadow", "controlDkShadow");
map.put("RadioButton.light", "controlHighlight");
map.put("RadioButton.highlight", "controlLtHighlight");
map.put("CheckBox.background", "control");
map.put("CheckBox.foreground", "controlText");
map.put("ColorChooser.background", "control");
map.put("ColorChooser.foreground", "controlText");
map.put("ColorChooser.swatchesDefaultRecentColor", "control");
map.put("ComboBox.background", "window");
map.put("ComboBox.foreground", "textText");
map.put("ComboBox.buttonBackground", "control");
map.put("ComboBox.buttonShadow", "controlShadow");
map.put("ComboBox.buttonDarkShadow", "controlDkShadow");
map.put("ComboBox.buttonHighlight", "controlLtHighlight");
map.put("ComboBox.selectionBackground", "textHighlight");
map.put("ComboBox.selectionForeground", "textHighlightText");
map.put("ComboBox.disabledBackground", "control");
map.put("ComboBox.disabledForeground", "textHInactiveText");
map.put("InternalFrame.borderColor", "control");
map.put("InternalFrame.borderShadow", "controlShadow");
map.put("InternalFrame.borderDarkShadow", "controlDkShadow");
map.put("InternalFrame.borderHighlight", "controlLtHighlight");
map.put("InternalFrame.borderLight", "controlHighlight");
map.put("InternalFrame.activeTitleBackground", "activeCaption");
map.put("InternalFrame.activeTitleForeground", "activeCaptionText");
map.put("InternalFrame.inactiveTitleBackground", "inactiveCaption");
map.put("InternalFrame.inactiveTitleForeground", "inactiveCaptionText");
map.put("Label.background", "control");
map.put("Label.foreground", "controlText");
map.put("Label.disabledShadow", "controlShadow");
map.put("List.background", "window");
map.put("List.foreground", "textText");
map.put("List.selectionBackground", "textHighlight");
map.put("List.selectionForeground", "textHighlightText");
map.put("List.dropLineColor", "controlShadow");
map.put("MenuBar.background", "menu");
map.put("MenuBar.foreground", "menuText");
map.put("MenuBar.shadow", "controlShadow");
map.put("MenuBar.highlight", "controlLtHighlight");
map.put("MenuItem.background", "menu");
map.put("MenuItem.foreground", "menuText");
map.put("MenuItem.selectionForeground", "textHighlightText");
map.put("MenuItem.selectionBackground", "textHighlight");
map.put("MenuItem.acceleratorForeground", "menuText");
map.put("MenuItem.acceleratorSelectionForeground", "textHighlightText");
map.put("RadioButtonMenuItem.background", "menu");
map.put("RadioButtonMenuItem.foreground", "menuText");
map.put("RadioButtonMenuItem.selectionForeground", "textHighlightText");
map.put("RadioButtonMenuItem.selectionBackground", "textHighlight");
map.put("RadioButtonMenuItem.acceleratorForeground", "menuText");
map.put("RadioButtonMenuItem.acceleratorSelectionForeground", "textHighlightText");
map.put("CheckBoxMenuItem.background", "menu");
map.put("CheckBoxMenuItem.foreground", "menuText");
map.put("CheckBoxMenuItem.selectionForeground", "textHighlightText");
map.put("CheckBoxMenuItem.selectionBackground", "textHighlight");
map.put("CheckBoxMenuItem.acceleratorForeground", "menuText");
map.put("CheckBoxMenuItem.acceleratorSelectionForeground", "textHighlightText");
map.put("Menu.background", "menu");
map.put("Menu.foreground", "menuText");
map.put("Menu.selectionForeground", "textHighlightText");
map.put("Menu.selectionBackground", "textHighlight");
map.put("Menu.acceleratorForeground", "menuText");
map.put("Menu.acceleratorSelectionForeground", "textHighlightText");
map.put("PopupMenu.background", "menu");
map.put("PopupMenu.foreground", "menuText");
map.put("OptionPane.background", "control");
map.put("OptionPane.foreground", "controlText");
map.put("OptionPane.messageForeground", "controlText");
map.put("Panel.background", "control");
map.put("Panel.foreground", "textText");
map.put("ProgressBar.foreground", "textHighlight");
map.put("ProgressBar.background", "control");
map.put("ProgressBar.selectionForeground", "control");
map.put("ProgressBar.selectionBackground", "textHighlight");
map.put("Separator.background", "controlLtHighlight");
map.put("Separator.foreground", "controlShadow");
map.put("ScrollBar.foreground", "control");
map.put("ScrollBar.track", "scrollbar");
map.put("ScrollBar.trackHighlight", "controlDkShadow");
map.put("ScrollBar.thumb", "control");
map.put("ScrollBar.thumbHighlight", "controlLtHighlight");
map.put("ScrollBar.thumbDarkShadow", "controlDkShadow");
map.put("ScrollBar.thumbShadow", "controlShadow");
map.put("ScrollPane.background", "control");
map.put("ScrollPane.foreground", "controlText");
map.put("Viewport.background", "control");
map.put("Viewport.foreground", "textText");
map.put("Slider.foreground", "control");
map.put("Slider.background", "control");
map.put("Slider.highlight", "controlLtHighlight");
map.put("Slider.shadow", "controlShadow");
map.put("Slider.focus", "controlDkShadow");
map.put("Spinner.background", "control");
map.put("Spinner.foreground", "control");
map.put("SplitPane.background", "control");
map.put("SplitPane.highlight", "controlLtHighlight");
map.put("SplitPane.shadow", "controlShadow");
map.put("SplitPane.darkShadow", "controlDkShadow");
map.put("TabbedPane.background", "control");
map.put("TabbedPane.foreground", "controlText");
map.put("TabbedPane.highlight", "controlLtHighlight");
map.put("TabbedPane.light", "controlHighlight");
map.put("TabbedPane.shadow", "controlShadow");
map.put("TabbedPane.darkShadow", "controlDkShadow");
map.put("TabbedPane.focus", "controlText");
map.put("Table.foreground", "controlText");
map.put("Table.background", "window");
map.put("Table.selectionForeground", "textHighlightText");
map.put("Table.selectionBackground", "textHighlight");
map.put("Table.dropLineColor", "controlShadow");
map.put("Table.focusCellBackground", "window");
map.put("Table.focusCellForeground", "controlText");
map.put("TableHeader.foreground", "controlText");
map.put("TableHeader.background", "control");
map.put("TableHeader.focusCellBackground", "text");
map.put("TextField.background", "window");
map.put("TextField.foreground", "textText");
map.put("TextField.shadow", "controlShadow");
map.put("TextField.darkShadow", "controlDkShadow");
map.put("TextField.light", "controlHighlight");
map.put("TextField.highlight", "controlLtHighlight");
map.put("TextField.inactiveForeground", "textHInactiveText");
map.put("TextField.inactiveBackground", "control");
map.put("TextField.selectionBackground", "textHighlight");
map.put("TextField.selectionForeground", "textHighlightText");
map.put("TextField.caretForeground", "textText");
map.put("FormattedTextField.background", "window");
map.put("FormattedTextField.foreground", "textText");
map.put("FormattedTextField.inactiveForeground", "textHInactiveText");
map.put("FormattedTextField.inactiveBackground", "control");
map.put("FormattedTextField.selectionBackground", "textHighlight");
map.put("FormattedTextField.selectionForeground", "textHighlightText");
map.put("FormattedTextField.caretForeground", "textText");
map.put("PasswordField.background", "window");
map.put("PasswordField.foreground", "textText");
map.put("PasswordField.inactiveForeground", "textHInactiveText");
map.put("PasswordField.inactiveBackground", "control");
map.put("PasswordField.selectionBackground", "textHighlight");
map.put("PasswordField.selectionForeground", "textHighlightText");
map.put("PasswordField.caretForeground", "textText");
map.put("TextArea.background", "window");
map.put("TextArea.foreground", "textText");
map.put("TextArea.inactiveForeground", "textHInactiveText");
map.put("TextArea.selectionBackground", "textHighlight");
map.put("TextArea.selectionForeground", "textHighlightText");
map.put("TextArea.caretForeground", "textText");
map.put("TextPane.foreground", "textText");
map.put("TextPane.selectionBackground", "textHighlight");
map.put("TextPane.selectionForeground", "textHighlightText");
map.put("TextPane.caretForeground", "textText");
map.put("TextPane.inactiveForeground", "textHInactiveText");
map.put("EditorPane.foreground", "textText");
map.put("EditorPane.selectionBackground", "textHighlight");
map.put("EditorPane.selectionForeground", "textHighlightText");
map.put("EditorPane.caretForeground", "textText");
map.put("EditorPane.inactiveForeground", "textHInactiveText");
map.put("TitledBorder.titleColor", "controlText");
map.put("ToolBar.background", "control");
map.put("ToolBar.foreground", "controlText");
map.put("ToolBar.shadow", "controlShadow");
map.put("ToolBar.darkShadow", "controlDkShadow");
map.put("ToolBar.light", "controlHighlight");
map.put("ToolBar.highlight", "controlLtHighlight");
map.put("ToolBar.dockingBackground", "control");
map.put("ToolBar.floatingBackground", "control");
map.put("ToolTip.background", "info");
map.put("ToolTip.foreground", "infoText");
map.put("Tree.background", "window");
map.put("Tree.foreground", "textText");
map.put("Tree.textForeground", "textText");
map.put("Tree.textBackground", "text");
map.put("Tree.selectionForeground", "textHighlightText");
map.put("Tree.selectionBackground", "textHighlight");
map.put("Tree.dropLineColor", "controlShadow");
private static Map<String, String> map = Map.ofEntries(
entry("Button.background", "control"),
entry("Button.foreground", "controlText"),
entry("Button.shadow", "controlShadow"),
entry("Button.darkShadow", "controlDkShadow"),
entry("Button.light", "controlHighlight"),
entry("Button.highlight", "controlLtHighlight"),
entry("ToggleButton.background", "control"),
entry("ToggleButton.foreground", "controlText"),
entry("ToggleButton.shadow", "controlShadow"),
entry("ToggleButton.darkShadow", "controlDkShadow"),
entry("ToggleButton.light", "controlHighlight"),
entry("ToggleButton.highlight", "controlLtHighlight"),
entry("RadioButton.background", "control"),
entry("RadioButton.foreground", "controlText"),
entry("RadioButton.shadow", "controlShadow"),
entry("RadioButton.darkShadow", "controlDkShadow"),
entry("RadioButton.light", "controlHighlight"),
entry("RadioButton.highlight", "controlLtHighlight"),
entry("CheckBox.background", "control"),
entry("CheckBox.foreground", "controlText"),
entry("ColorChooser.background", "control"),
entry("ColorChooser.foreground", "controlText"),
entry("ColorChooser.swatchesDefaultRecentColor", "control"),
entry("ComboBox.background", "window"),
entry("ComboBox.foreground", "textText"),
entry("ComboBox.buttonBackground", "control"),
entry("ComboBox.buttonShadow", "controlShadow"),
entry("ComboBox.buttonDarkShadow", "controlDkShadow"),
entry("ComboBox.buttonHighlight", "controlLtHighlight"),
entry("ComboBox.selectionBackground", "textHighlight"),
entry("ComboBox.selectionForeground", "textHighlightText"),
entry("ComboBox.disabledBackground", "control"),
entry("ComboBox.disabledForeground", "textHInactiveText"),
entry("InternalFrame.borderColor", "control"),
entry("InternalFrame.borderShadow", "controlShadow"),
entry("InternalFrame.borderDarkShadow", "controlDkShadow"),
entry("InternalFrame.borderHighlight", "controlLtHighlight"),
entry("InternalFrame.borderLight", "controlHighlight"),
entry("InternalFrame.activeTitleBackground", "activeCaption"),
entry("InternalFrame.activeTitleForeground", "activeCaptionText"),
entry("InternalFrame.inactiveTitleBackground", "inactiveCaption"),
entry("InternalFrame.inactiveTitleForeground", "inactiveCaptionText"),
entry("Label.background", "control"),
entry("Label.foreground", "controlText"),
entry("Label.disabledShadow", "controlShadow"),
entry("List.background", "window"),
entry("List.foreground", "textText"),
entry("List.selectionBackground", "textHighlight"),
entry("List.selectionForeground", "textHighlightText"),
entry("List.dropLineColor", "controlShadow"),
entry("MenuBar.background", "menu"),
entry("MenuBar.foreground", "menuText"),
entry("MenuBar.shadow", "controlShadow"),
entry("MenuBar.highlight", "controlLtHighlight"),
entry("MenuItem.background", "menu"),
entry("MenuItem.foreground", "menuText"),
entry("MenuItem.selectionForeground", "textHighlightText"),
entry("MenuItem.selectionBackground", "textHighlight"),
entry("MenuItem.acceleratorForeground", "menuText"),
entry("MenuItem.acceleratorSelectionForeground", "textHighlightText"),
entry("RadioButtonMenuItem.background", "menu"),
entry("RadioButtonMenuItem.foreground", "menuText"),
entry("RadioButtonMenuItem.selectionForeground", "textHighlightText"),
entry("RadioButtonMenuItem.selectionBackground", "textHighlight"),
entry("RadioButtonMenuItem.acceleratorForeground", "menuText"),
entry("RadioButtonMenuItem.acceleratorSelectionForeground", "textHighlightText"),
entry("CheckBoxMenuItem.background", "menu"),
entry("CheckBoxMenuItem.foreground", "menuText"),
entry("CheckBoxMenuItem.selectionForeground", "textHighlightText"),
entry("CheckBoxMenuItem.selectionBackground", "textHighlight"),
entry("CheckBoxMenuItem.acceleratorForeground", "menuText"),
entry("CheckBoxMenuItem.acceleratorSelectionForeground", "textHighlightText"),
entry("Menu.background", "menu"),
entry("Menu.foreground", "menuText"),
entry("Menu.selectionForeground", "textHighlightText"),
entry("Menu.selectionBackground", "textHighlight"),
entry("Menu.acceleratorForeground", "menuText"),
entry("Menu.acceleratorSelectionForeground", "textHighlightText"),
entry("PopupMenu.background", "menu"),
entry("PopupMenu.foreground", "menuText"),
entry("OptionPane.background", "control"),
entry("OptionPane.foreground", "controlText"),
entry("OptionPane.messageForeground", "controlText"),
entry("Panel.background", "control"),
entry("Panel.foreground", "textText"),
entry("ProgressBar.foreground", "textHighlight"),
entry("ProgressBar.background", "control"),
entry("ProgressBar.selectionForeground", "control"),
entry("ProgressBar.selectionBackground", "textHighlight"),
entry("Separator.background", "controlLtHighlight"),
entry("Separator.foreground", "controlShadow"),
entry("ScrollBar.foreground", "control"),
entry("ScrollBar.track", "scrollbar"),
entry("ScrollBar.trackHighlight", "controlDkShadow"),
entry("ScrollBar.thumb", "control"),
entry("ScrollBar.thumbHighlight", "controlLtHighlight"),
entry("ScrollBar.thumbDarkShadow", "controlDkShadow"),
entry("ScrollBar.thumbShadow", "controlShadow"),
entry("ScrollPane.background", "control"),
entry("ScrollPane.foreground", "controlText"),
entry("Viewport.background", "control"),
entry("Viewport.foreground", "textText"),
entry("Slider.foreground", "control"),
entry("Slider.background", "control"),
entry("Slider.highlight", "controlLtHighlight"),
entry("Slider.shadow", "controlShadow"),
entry("Slider.focus", "controlDkShadow"),
entry("Spinner.background", "control"),
entry("Spinner.foreground", "control"),
entry("SplitPane.background", "control"),
entry("SplitPane.highlight", "controlLtHighlight"),
entry("SplitPane.shadow", "controlShadow"),
entry("SplitPane.darkShadow", "controlDkShadow"),
entry("TabbedPane.background", "control"),
entry("TabbedPane.foreground", "controlText"),
entry("TabbedPane.highlight", "controlLtHighlight"),
entry("TabbedPane.light", "controlHighlight"),
entry("TabbedPane.shadow", "controlShadow"),
entry("TabbedPane.darkShadow", "controlDkShadow"),
entry("TabbedPane.focus", "controlText"),
entry("Table.foreground", "controlText"),
entry("Table.background", "window"),
entry("Table.selectionForeground", "textHighlightText"),
entry("Table.selectionBackground", "textHighlight"),
entry("Table.dropLineColor", "controlShadow"),
entry("Table.focusCellBackground", "window"),
entry("Table.focusCellForeground", "controlText"),
entry("TableHeader.foreground", "controlText"),
entry("TableHeader.background", "control"),
entry("TableHeader.focusCellBackground", "text"),
entry("TextField.background", "window"),
entry("TextField.foreground", "textText"),
entry("TextField.shadow", "controlShadow"),
entry("TextField.darkShadow", "controlDkShadow"),
entry("TextField.light", "controlHighlight"),
entry("TextField.highlight", "controlLtHighlight"),
entry("TextField.inactiveForeground", "textHInactiveText"),
entry("TextField.inactiveBackground", "control"),
entry("TextField.selectionBackground", "textHighlight"),
entry("TextField.selectionForeground", "textHighlightText"),
entry("TextField.caretForeground", "textText"),
entry("FormattedTextField.background", "window"),
entry("FormattedTextField.foreground", "textText"),
entry("FormattedTextField.inactiveForeground", "textHInactiveText"),
entry("FormattedTextField.inactiveBackground", "control"),
entry("FormattedTextField.selectionBackground", "textHighlight"),
entry("FormattedTextField.selectionForeground", "textHighlightText"),
entry("FormattedTextField.caretForeground", "textText"),
entry("PasswordField.background", "window"),
entry("PasswordField.foreground", "textText"),
entry("PasswordField.inactiveForeground", "textHInactiveText"),
entry("PasswordField.inactiveBackground", "control"),
entry("PasswordField.selectionBackground", "textHighlight"),
entry("PasswordField.selectionForeground", "textHighlightText"),
entry("PasswordField.caretForeground", "textText"),
entry("TextArea.background", "window"),
entry("TextArea.foreground", "textText"),
entry("TextArea.inactiveForeground", "textHInactiveText"),
entry("TextArea.selectionBackground", "textHighlight"),
entry("TextArea.selectionForeground", "textHighlightText"),
entry("TextArea.caretForeground", "textText"),
entry("TextPane.foreground", "textText"),
entry("TextPane.selectionBackground", "textHighlight"),
entry("TextPane.selectionForeground", "textHighlightText"),
entry("TextPane.caretForeground", "textText"),
entry("TextPane.inactiveForeground", "textHInactiveText"),
entry("EditorPane.foreground", "textText"),
entry("EditorPane.selectionBackground", "textHighlight"),
entry("EditorPane.selectionForeground", "textHighlightText"),
entry("EditorPane.caretForeground", "textText"),
entry("EditorPane.inactiveForeground", "textHInactiveText"),
entry("TitledBorder.titleColor", "controlText"),
entry("ToolBar.background", "control"),
entry("ToolBar.foreground", "controlText"),
entry("ToolBar.shadow", "controlShadow"),
entry("ToolBar.darkShadow", "controlDkShadow"),
entry("ToolBar.light", "controlHighlight"),
entry("ToolBar.highlight", "controlLtHighlight"),
entry("ToolBar.dockingBackground", "control"),
entry("ToolBar.floatingBackground", "control"),
entry("ToolTip.background", "info"),
entry("ToolTip.foreground", "infoText"),
entry("Tree.background", "window"),
entry("Tree.foreground", "textText"),
entry("Tree.textForeground", "textText"),
entry("Tree.textBackground", "text"),
entry("Tree.selectionForeground", "textHighlightText"),
entry("Tree.selectionBackground", "textHighlight"),
entry("Tree.dropLineColor", "controlShadow"));
public static void fixupJavaDefaultsInheritence(GThemeValueMap values) {
List<ColorValue> colors = values.getColors();
for (ColorValue value : colors) {
ColorValue mapped = map(values, value);
if (mapped != null) {
values.addColor(mapped);
}
}
}
public ColorValue map(GThemeValueMap values, ColorValue value) {
private static ColorValue map(GThemeValueMap values, ColorValue value) {
String id = value.getId();
String refId = map.get(id);
if (refId == null) {
@@ -0,0 +1,143 @@
/* ###
* 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.builtin;
import static java.util.Map.*;
import java.awt.Font;
import java.util.List;
import java.util.Map;
import generic.theme.FontValue;
import generic.theme.GThemeValueMap;
/**
* Maps Java UIDefaults color ids to parent color ids
*/
public class JavaFontMapping {
private final static String BUTTON_GROUP = "ButtonComponents.font";
private final static String TEXT_GROUP = "TextComponents.font";
private final static String MENU_GROUP = "MenuComponents.font";
private final static String MENU_ACCELERATOR_GROUP = "MenuComponents.acceleratorFont";
private final static String DIALOG_GROUP = "Dialogs.font";
private final static String WIDGET_GROUP = "Components.font";
private static Map<String, String> map = Map.ofEntries(
entry("ArrowButton.font", BUTTON_GROUP), // nimbus
entry("Button.font", BUTTON_GROUP),
entry("CheckBox.font", BUTTON_GROUP),
entry("RadioButton.font", BUTTON_GROUP),
entry("ToggleButton.font", BUTTON_GROUP),
entry("CheckBoxMenuItem.font", MENU_GROUP),
entry("Menu.font", MENU_GROUP),
entry("MenuBar.font", MENU_GROUP),
entry("MenuItem.font", MENU_GROUP),
entry("PopupMenu.font", MENU_GROUP),
entry("RadioButtonMenuItem.font", MENU_GROUP),
entry("CheckBoxMenuItem.acceleratorFont", MENU_ACCELERATOR_GROUP), // metal, motif
entry("Menu.acceleratorFont", MENU_ACCELERATOR_GROUP), // metal, motif
entry("MenuItem.acceleratorFont", MENU_ACCELERATOR_GROUP), // metal, motfi
entry("RadioButtonMenuItem.acceleratorFont", MENU_ACCELERATOR_GROUP), // metal
entry("EditorPane.font", TEXT_GROUP),
entry("FormattedTextField.font", TEXT_GROUP),
entry("PasswordField.font", TEXT_GROUP),
entry("TextArea.font", TEXT_GROUP),
entry("TextField.font", TEXT_GROUP),
entry("TextPane.font", TEXT_GROUP),
entry("ColorChooser.font", DIALOG_GROUP),
entry("FileChooser.font", DIALOG_GROUP), // nimbus
entry("ComboBox.font", WIDGET_GROUP),
entry("InternalFrame.titleFont", WIDGET_GROUP), // metal, motif, flat
entry("Label.font", WIDGET_GROUP),
entry("List.font", WIDGET_GROUP),
entry("OptionPane.font", DIALOG_GROUP),
entry("Panel.font", WIDGET_GROUP),
entry("ProgressBar.font", WIDGET_GROUP),
entry("RootPane.font", WIDGET_GROUP),
entry("Scrollbar.font", WIDGET_GROUP),
entry("ScrollBarThumb.font", WIDGET_GROUP), // nimbus
entry("ScrollBarTrack.font", WIDGET_GROUP), // nimbus
entry("ScrollPane.font", WIDGET_GROUP),
entry("Separator.font", WIDGET_GROUP), // nimbus
entry("Slider.font", WIDGET_GROUP),
entry("SliderThumb.font", WIDGET_GROUP), // nimbus
entry("SliderTrack.font", WIDGET_GROUP), // nimbus
entry("Spinner.font", WIDGET_GROUP),
entry("SplitPane.font", WIDGET_GROUP), // nimbus
entry("TabbedPane.font", WIDGET_GROUP),
entry("TitledBorder.font", WIDGET_GROUP),
entry("ToolBar.font", WIDGET_GROUP),
entry("ToolTip.font", TEXT_GROUP),
entry("Viewport.font", WIDGET_GROUP),
entry("Tree.font", WIDGET_GROUP),
entry("Table.font", WIDGET_GROUP),
entry("TableHeader.font", "Table.font"));
public static void fixupJavaDefaultsInheritence(GThemeValueMap values) {
createGroupDefaults(values);
List<FontValue> fonts = values.getFonts();
for (FontValue value : fonts) {
FontValue mapped = map(values, value);
if (mapped != null) {
values.addFont(mapped);
}
}
}
private static FontValue map(GThemeValueMap values, FontValue value) {
String id = value.getId();
String refId = map.get(id);
if (refId == null) {
return null;
}
FontValue refValue = values.getFont(refId);
if (refValue == null) {
return null;
}
Font originalFont = value.get(values);
Font refFont = refValue.get(values);
if (originalFont == null || refFont == null) {
return null;
}
if (originalFont.equals(refFont)) {
return new FontValue(id, refId);
}
return null;
}
public static void createGroupDefaults(GThemeValueMap valuesMap) {
addFontValue(valuesMap, BUTTON_GROUP, "Button.font");
addFontValue(valuesMap, TEXT_GROUP, "TextField.font");
addFontValue(valuesMap, MENU_GROUP, "Menu.font");
addFontValue(valuesMap, MENU_ACCELERATOR_GROUP, "Menu.acceleratorFont");
addFontValue(valuesMap, DIALOG_GROUP, "ColorChooser.font");
addFontValue(valuesMap, WIDGET_GROUP, "Label.font");
}
private static void addFontValue(GThemeValueMap valuesMap, String groupId, String exemplarId) {
FontValue font = valuesMap.getFont(exemplarId);
if (font != null) {
valuesMap.addFont(new FontValue(groupId, font.get(valuesMap)));
}
}
}
@@ -0,0 +1,94 @@
/* ###
* 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.Color;
import java.awt.Font;
import java.util.List;
import javax.swing.Icon;
import javax.swing.UIDefaults;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import generic.theme.*;
/**
* Extends the NimbusLookAndFeel to intercept the {@link #getDefaults()}. To get Nimbus
* to use our indirect values, we have to get in early.
*/
public class GNimbusLookAndFeel extends NimbusLookAndFeel {
@Override
public UIDefaults getDefaults() {
UIDefaults defaults = super.getDefaults();
GThemeValueMap javaDefaults = extractJavaDefaults(defaults);
// replace all colors with GColors
for (ColorValue colorValue : javaDefaults.getColors()) {
String id = colorValue.getId();
defaults.put(id, Gui.getGColorUiResource(id));
}
// only replace fonts that have been changed by the theme
for (FontValue fontValue : javaDefaults.getFonts()) {
String id = fontValue.getId();
Font font = Gui.getFont(id);
defaults.put(id, font);
}
// only replace icons that have been changed by the theme
for (IconValue iconValue : javaDefaults.getIcons()) {
String id = iconValue.getId();
Icon icon = Gui.getRawIcon(id, true);
defaults.put(id, icon);
}
defaults.put("Label.textForeground", Gui.getGColorUiResource("Label.foreground"));
GColor.refreshAll();
GIcon.refreshAll();
return defaults;
}
protected GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
GThemeValueMap javaDefaults = new GThemeValueMap();
List<String> colorIds =
LookAndFeelInstaller.getLookAndFeelIdsForType(defaults, Color.class);
for (String id : colorIds) {
Color color = defaults.getColor(id);
ColorValue value = new ColorValue(id, color);
javaDefaults.addColor(value);
}
List<String> fontIds =
LookAndFeelInstaller.getLookAndFeelIdsForType(defaults, Font.class);
for (String id : fontIds) {
Font font = defaults.getFont(id);
FontValue value = new FontValue(id, font);
javaDefaults.addFont(value);
}
List<String> iconIds =
LookAndFeelInstaller.getLookAndFeelIdsForType(defaults, Icon.class);
for (String id : iconIds) {
Icon icon = defaults.getIcon(id);
javaDefaults.addIcon(new IconValue(id, icon));
}
// need to set javaDefalts now to trigger building currentValues so the when
// we create GColors below, they can be resolved.
Gui.setJavaDefaults(javaDefaults);
return javaDefaults;
}
}
@@ -15,7 +15,10 @@
*/
package generic.theme.laf;
import java.awt.*;
import java.awt.Font;
import java.awt.Window;
import java.util.List;
import java.util.Set;
import javax.swing.*;
@@ -46,43 +49,69 @@ public abstract class LookAndFeelManager {
updateComponentUis();
}
public void update() {
public void resetAll(GThemeValueMap javaDefaults) {
GColor.refreshAll();
GIcon.refreshAll();
resetIcons(javaDefaults);
resetFonts(javaDefaults);
updateComponentUis();
// repaintAll();
}
public void updateColor(String id, Color color, boolean isJavaColor) {
private void resetFonts(GThemeValueMap javaDefaults) {
List<FontValue> fonts = javaDefaults.getFonts();
UIDefaults defaults = UIManager.getDefaults();
for (FontValue fontValue : fonts) {
String id = fontValue.getId();
Font correctFont = Gui.getFont(id);
Font storedFont = defaults.getFont(id);
if (correctFont != null && !correctFont.equals(storedFont)) {
defaults.put(id, correctFont);
}
}
}
private void resetIcons(GThemeValueMap javaDefaults) {
List<IconValue> icons = javaDefaults.getIcons();
UIDefaults defaults = UIManager.getDefaults();
for (IconValue iconValue : icons) {
String id = iconValue.getId();
Icon correctIcon = Gui.getRawIcon(id, false);
Icon storedIcon = defaults.getIcon(id);
if (correctIcon != null && !correctIcon.equals(storedIcon)) {
defaults.put(id, correctIcon);
}
}
}
public void updateColors() {
GColor.refreshAll();
repaintAll();
}
public void updateIcon(String id, Icon icon, boolean isJavaIcon) {
// Icons are a mixed bag. Java Icons are direct and Ghidra Icons are indirect (to support static use)
// Mainly because Nimbus is buggy and can't handle non-nimbus Icons, so we can't wrap them
// So need to update UiDefaults for java icons. For Ghidra Icons, it is sufficient to refrech
// GIcons and repaint
if (isJavaIcon) {
UIManager.getDefaults().put(id, icon);
public void updateIcons(String id, Set<String> affectedJavaIds, Icon newIcon) {
if (!affectedJavaIds.isEmpty()) {
UIDefaults defaults = UIManager.getDefaults();
for (String javaIconId : affectedJavaIds) {
defaults.put(javaIconId, newIcon);
}
updateComponentUis();
}
GIcon.refreshAll();
repaintAll();
}
public void updateFont(String id, Font font, boolean isJavaFont) {
if (isJavaFont) {
UIManager.getDefaults().put(id, font);
public void updateFonts(String id, Set<String> affectedJavaIds, Font newFont) {
if (!affectedJavaIds.isEmpty()) {
UIDefaults defaults = UIManager.getDefaults();
for (String javaFontId : affectedJavaIds) {
defaults.put(javaFontId, newFont);
}
updateComponentUis();
}
else {
repaintAll();
}
repaintAll();
}
private void updateComponentUis() {
protected void updateComponentUis() {
for (Window window : Window.getWindows()) {
SwingUtilities.updateComponentTreeUI(window);
}
@@ -15,11 +15,9 @@
*/
package generic.theme.laf;
import java.awt.*;
import java.util.List;
import java.awt.Dimension;
import javax.swing.*;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import generic.theme.*;
@@ -37,7 +35,7 @@ public class NimbusLookAndFeelInstaller extends LookAndFeelInstaller {
@Override
protected void installJavaDefaults() {
// even though java defaults have been installed, we need to fix them up now
// that nimbus has finished initializing
// that Nimbus has finished initializing
GColor.refreshAll();
Gui.setJavaDefaults(Gui.getJavaDefaults());
}
@@ -53,77 +51,4 @@ public class NimbusLookAndFeelInstaller extends LookAndFeelInstaller {
// (see NimbusDefaults for key values that can be changed here)
}
/**
* Extends the NimbusLookAndFeel to intercept the {@link #getDefaults()}. To get Nimbus
* to use our indirect values, we have to get in early.
*/
static class GNimbusLookAndFeel extends NimbusLookAndFeel {
@Override
public UIDefaults getDefaults() {
UIDefaults defaults = super.getDefaults();
GThemeValueMap javaDefaults = extractJavaDefaults(defaults);
// need to set javaDefalts now to trigger building currentValues so the when
// we create GColors below, they can be resolved.
Gui.setJavaDefaults(javaDefaults);
// replace all colors with GColors
for (ColorValue colorValue : javaDefaults.getColors()) {
String id = colorValue.getId();
defaults.put(id, Gui.getGColorUiResource(id));
}
GTheme theme = Gui.getActiveTheme();
// only replace fonts that have been changed by the theme
for (FontValue fontValue : theme.getFonts()) {
String id = fontValue.getId();
Font font = Gui.getFont(id);
defaults.put(id, font);
}
// only replace icons that have been changed by the theme
for (IconValue iconValue : theme.getIcons()) {
String id = iconValue.getId();
Icon icon = Gui.getRawIcon(id, true);
defaults.put(id, icon);
}
defaults.put("Label.textForeground", Gui.getGColorUiResource("Label.foreground"));
GColor.refreshAll();
GIcon.refreshAll();
return defaults;
}
private GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
GThemeValueMap javaDefaults = new GThemeValueMap();
List<String> colorIds =
LookAndFeelInstaller.getLookAndFeelIdsForType(defaults, Color.class);
for (String id : colorIds) {
Color color = defaults.getColor(id);
ColorValue value = new ColorValue(id, color);
javaDefaults.addColor(value);
}
List<String> fontIds =
LookAndFeelInstaller.getLookAndFeelIdsForType(defaults, Font.class);
for (String id : fontIds) {
Font font = defaults.getFont(id);
FontValue value = new FontValue(id, font);
javaDefaults.addFont(value);
}
List<String> iconIds =
LookAndFeelInstaller.getLookAndFeelIdsForType(defaults, Icon.class);
for (String id : iconIds) {
Icon icon = defaults.getIcon(id);
javaDefaults.addIcon(new IconValue(id, icon));
}
return javaDefaults;
}
}
}
@@ -15,14 +15,15 @@
*/
package generic.theme.laf;
import java.awt.*;
import java.awt.Font;
import java.util.Set;
import javax.swing.*;
import generic.theme.LafType;
import generic.theme.*;
import ghidra.util.exception.AssertException;
public class NimbusLookAndFeelManager extends LookAndFeelManager {
private UIDefaults overrides = new UIDefaults();
public NimbusLookAndFeelManager() {
super(LafType.NIMBUS);
@@ -34,69 +35,39 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
}
@Override
public void updateColor(String id, Color color, boolean isJavaColor) {
super.updateColor(id, color, isJavaColor);
public void resetAll(GThemeValueMap javaDefaults) {
GColor.refreshAll();
GIcon.refreshAll();
reinstallNimubus();
}
@Override
public void updateFont(String id, Font font, boolean isJavaFont) {
if (isJavaFont) {
overrides.put(id, font);
updateNimbusOverrides();
public void updateFonts(String id, Set<String> affectedJavaIds, Font newFont) {
if (!affectedJavaIds.isEmpty()) {
reinstallNimubus();
}
repaintAll();
}
@Override
public void updateIcon(String id, Icon icon, boolean isJavaIcon) {
if (isJavaIcon) {
overrides.put(id, icon);
updateNimbusOverrides();
public void updateIcons(String id, Set<String> affectedJavaIds, Icon newIcon) {
if (!affectedJavaIds.isEmpty()) {
reinstallNimubus();
}
GIcon.refreshAll();
repaintAll();
}
private void updateNimbusOverrides() {
UIDefaults defaults = getNimbusOverrides();
for (Window window : Window.getWindows()) {
updateNimbusUI(window, defaults);
private void reinstallNimubus() {
try {
UIManager.setLookAndFeel(new GNimbusLookAndFeel() {
protected GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
return Gui.getJavaDefaults();
}
});
}
}
private void updateNimbusUI(Component c, UIDefaults defaults) {
updateNimbusUIComp(c, defaults);
c.invalidate();
c.validate();
c.repaint();
}
private UIDefaults getNimbusOverrides() {
UIDefaults defaults = new UIDefaults();
defaults.putAll(overrides);
return defaults;
}
private void updateNimbusUIComp(Component c, UIDefaults defaults) {
if (c instanceof JComponent) {
JComponent jc = (JComponent) c;
jc.putClientProperty("Nimbus.Overrides", defaults);
JPopupMenu jpm = jc.getComponentPopupMenu();
if (jpm != null) {
updateNimbusUI(jpm, defaults);
}
}
Component[] children = null;
if (c instanceof JMenu) {
children = ((JMenu) c).getMenuComponents();
}
else if (c instanceof Container) {
children = ((Container) c).getComponents();
}
if (children != null) {
for (Component child : children) {
updateNimbusUIComp(child, defaults);
}
catch (UnsupportedLookAndFeelException e) {
throw new AssertException("This can't happen, we are just re-installing the same L&F");
}
updateComponentUis();
}
}
@@ -0,0 +1,135 @@
/* ###
* 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;
import static org.junit.Assert.*;
import java.awt.Color;
import org.junit.Before;
import org.junit.Test;
public class ColorValueTest {
private GThemeValueMap values;
@Before
public void setup() {
values = new GThemeValueMap();
}
@Test
public void testDirectValue() {
ColorValue value = new ColorValue("color.test", Color.RED);
values.addColor(value);
assertEquals("color.test", value.getId());
assertEquals(Color.RED, value.getRawValue());
assertNull(value.getReferenceId());
assertEquals(Color.RED, value.get(values));
}
@Test
public void testIndirectValue() {
values.addColor(new ColorValue("color.parent", Color.RED));
ColorValue value = new ColorValue("color.test", "color.parent");
values.addColor(value);
assertEquals("color.test", value.getId());
assertNull(value.getRawValue());
assertEquals("color.parent", value.getReferenceId());
assertEquals(Color.RED, value.get(values));
}
@Test
public void TestIndirectMultiHopValue() {
values.addColor(new ColorValue("color.grandparent", Color.RED));
values.addColor(new ColorValue("color.parent", "color.grandparent"));
ColorValue value = new ColorValue("color.test", "color.parent");
values.addColor(value);
assertNull(value.getRawValue());
assertEquals("color.parent", value.getReferenceId());
assertEquals(Color.RED, value.get(values));
}
@Test
public void TestUnresolvedIndirectValue() {
ColorValue value = new ColorValue("color.test", "color.parent");
values.addColor(value);
assertNull(value.getRawValue());
assertEquals("color.parent", value.getReferenceId());
assertEquals(ColorValue.LAST_RESORT_DEFAULT, value.get(values));
}
@Test
public void testReferenceLoop() {
values.addColor(new ColorValue("color.grandparent", "color.test"));
values.addColor(new ColorValue("color.parent", "color.grandparent"));
ColorValue value = new ColorValue("color.test", "color.parent");
assertEquals(ColorValue.LAST_RESORT_DEFAULT, value.get(values));
}
@Test
public void testToExernalId() {
ColorValue value = new ColorValue("color.test", Color.BLUE);
assertEquals("color.test", value.toExternalId("color.test"));
assertEquals("[color]foo.bar", value.toExternalId("foo.bar"));
}
@Test
public void testFromExternalId() {
ColorValue value = new ColorValue("color.test", Color.BLUE);
assertEquals("color.test", value.fromExternalId("color.test"));
assertEquals("foo.bar", value.fromExternalId("[color]foo.bar"));
}
@Test
public void testIsColorKey() {
assertTrue(ColorValue.isColorKey("color.a.b.c"));
assertTrue(ColorValue.isColorKey("[color]a.b.c"));
assertFalse(ColorValue.isColorKey("a.b.c"));
}
@Test
public void testInheritsFrom() {
ColorValue grandParent = new ColorValue("color.grandparent", Color.RED);
values.addColor(grandParent);
ColorValue parent = new ColorValue("color.parent", "color.grandparent");
values.addColor(parent);
ColorValue value = new ColorValue("color.test", "color.parent");
values.addColor(value);
assertTrue(value.inheritsFrom("color.parent", values));
assertTrue(value.inheritsFrom("color.grandparent", values));
assertTrue(parent.inheritsFrom("color.grandparent", values));
assertFalse(value.inheritsFrom("color.test", values));
assertFalse(parent.inheritsFrom("color.test", values));
assertFalse(grandParent.inheritsFrom("color.test", values));
}
@Test
public void testCreatingValueFromGColor() {
ColorValue parent = new ColorValue("color.parent", Color.RED);
values.addColor(parent);
Color gColor = new GColor("color.parent");
ColorValue value = new ColorValue("color.value", gColor);
assertEquals("color.parent", value.getReferenceId());
assertNull(value.getRawValue());
}
}
@@ -0,0 +1,129 @@
/* ###
* 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;
import static org.junit.Assert.*;
import java.awt.Font;
import org.junit.Before;
import org.junit.Test;
import generic.theme.FontValue;
import generic.theme.GThemeValueMap;
public class FontValueTest {
private static Font FONT = new Font("Dialog", 12, Font.PLAIN);
private GThemeValueMap values;
@Before
public void setup() {
values = new GThemeValueMap();
}
@Test
public void testDirectValue() {
FontValue value = new FontValue("font.test", FONT);
values.addFont(value);
assertEquals("font.test", value.getId());
assertEquals(FONT, value.getRawValue());
assertNull(value.getReferenceId());
assertEquals(FONT, value.get(values));
}
@Test
public void testIndirectValue() {
values.addFont(new FontValue("font.parent", FONT));
FontValue value = new FontValue("font.test", "font.parent");
values.addFont(value);
assertEquals("font.test", value.getId());
assertNull(value.getRawValue());
assertEquals("font.parent", value.getReferenceId());
assertEquals(FONT, value.get(values));
}
@Test
public void TestIndirectMultiHopValue() {
values.addFont(new FontValue("font.grandparent", FONT));
values.addFont(new FontValue("font.parent", "font.grandparent"));
FontValue value = new FontValue("font.test", "font.parent");
values.addFont(value);
assertNull(value.getRawValue());
assertEquals("font.parent", value.getReferenceId());
assertEquals(FONT, value.get(values));
}
@Test
public void TestUnresolvedIndirectValue() {
FontValue value = new FontValue("font.test", "font.parent");
values.addFont(value);
assertNull(value.getRawValue());
assertEquals("font.parent", value.getReferenceId());
assertEquals(FontValue.LAST_RESORT_DEFAULT, value.get(values));
}
@Test
public void testReferenceLoop() {
values.addFont(new FontValue("font.grandparent", "font.test"));
values.addFont(new FontValue("font.parent", "font.grandparent"));
FontValue value = new FontValue("font.test", "font.parent");
assertEquals(FontValue.LAST_RESORT_DEFAULT, value.get(values));
}
@Test
public void testToExernalId() {
FontValue value = new FontValue("font.test", FONT);
assertEquals("font.test", value.toExternalId("font.test"));
assertEquals("[font]foo.bar", value.toExternalId("foo.bar"));
}
@Test
public void testFromExternalId() {
FontValue value = new FontValue("font.test", FONT);
assertEquals("font.test", value.fromExternalId("font.test"));
assertEquals("foo.bar", value.fromExternalId("[font]foo.bar"));
}
@Test
public void testIsFontKey() {
assertTrue(FontValue.isFontKey("font.a.b.c"));
assertTrue(FontValue.isFontKey("[font]a.b.c"));
assertFalse(FontValue.isFontKey("a.b.c"));
}
@Test
public void testInheritsFrom() {
FontValue grandParent = new FontValue("font.grandparent", FONT);
values.addFont(grandParent);
FontValue parent = new FontValue("font.parent", "font.grandparent");
values.addFont(parent);
FontValue value = new FontValue("font.test", "font.parent");
values.addFont(value);
assertTrue(value.inheritsFrom("font.parent", values));
assertTrue(value.inheritsFrom("font.grandparent", values));
assertTrue(parent.inheritsFrom("font.grandparent", values));
assertFalse(value.inheritsFrom("font.test", values));
assertFalse(parent.inheritsFrom("font.test", values));
assertFalse(grandParent.inheritsFrom("font.test", values));
}
}
@@ -13,20 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.theme;
package generic.theme;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.map.HashedMap;
import org.junit.Before;
import docking.test.AbstractDockingTest;
import generic.theme.*;
public class GuiTest {
public class GuiTest extends AbstractDockingTest {
private Map<String, List<String>> aliasMap = new HashedMap<>();
private GThemeValueMap darkValues = new GThemeValueMap();
@Before
@@ -0,0 +1,137 @@
/* ###
* 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;
import static org.junit.Assert.*;
import javax.swing.Icon;
import org.junit.Before;
import org.junit.Test;
import resources.ResourceManager;
public class IconValueTest {
private static Icon ICON1 = ResourceManager.getDefaultIcon();
private GThemeValueMap values;
@Before
public void setup() {
values = new GThemeValueMap();
}
@Test
public void testDirectValue() {
IconValue value = new IconValue("icon.test", ICON1);
values.addIcon(value);
assertEquals("icon.test", value.getId());
assertEquals(ICON1, value.getRawValue());
assertNull(value.getReferenceId());
assertEquals(ICON1, value.get(values));
}
@Test
public void testIndirectValue() {
values.addIcon(new IconValue("icon.parent", ICON1));
IconValue value = new IconValue("icon.test", "icon.parent");
values.addIcon(value);
assertEquals("icon.test", value.getId());
assertNull(value.getRawValue());
assertEquals("icon.parent", value.getReferenceId());
assertEquals(ICON1, value.get(values));
}
@Test
public void TestIndirectMultiHopValue() {
values.addIcon(new IconValue("icon.grandparent", ICON1));
values.addIcon(new IconValue("icon.parent", "icon.grandparent"));
IconValue value = new IconValue("icon.test", "icon.parent");
values.addIcon(value);
assertNull(value.getRawValue());
assertEquals("icon.parent", value.getReferenceId());
assertEquals(ICON1, value.get(values));
}
@Test
public void TestUnresolvedIndirectValue() {
IconValue value = new IconValue("icon.test", "icon.parent");
values.addIcon(value);
assertNull(value.getRawValue());
assertEquals("icon.parent", value.getReferenceId());
assertEquals(IconValue.LAST_RESORT_DEFAULT, value.get(values));
}
@Test
public void testReferenceLoop() {
values.addIcon(new IconValue("icon.grandparent", "icon.test"));
values.addIcon(new IconValue("icon.parent", "icon.grandparent"));
IconValue value = new IconValue("icon.test", "icon.parent");
assertEquals(IconValue.LAST_RESORT_DEFAULT, value.get(values));
}
@Test
public void testToExernalId() {
IconValue value = new IconValue("icon.test", ICON1);
assertEquals("icon.test", value.toExternalId("icon.test"));
assertEquals("[icon]foo.bar", value.toExternalId("foo.bar"));
}
@Test
public void testFromExternalId() {
IconValue value = new IconValue("icon.test", ICON1);
assertEquals("icon.test", value.fromExternalId("icon.test"));
assertEquals("foo.bar", value.fromExternalId("[icon]foo.bar"));
}
@Test
public void testIsIconKey() {
assertTrue(IconValue.isIconKey("icon.a.b.c"));
assertTrue(IconValue.isIconKey("[icon]a.b.c"));
assertFalse(IconValue.isIconKey("a.b.c"));
}
@Test
public void testInheritsFrom() {
IconValue grandParent = new IconValue("icon.grandparent", ICON1);
values.addIcon(grandParent);
IconValue parent = new IconValue("icon.parent", "icon.grandparent");
values.addIcon(parent);
IconValue value = new IconValue("icon.test", "icon.parent");
values.addIcon(value);
assertTrue(value.inheritsFrom("icon.parent", values));
assertTrue(value.inheritsFrom("icon.grandparent", values));
assertTrue(parent.inheritsFrom("icon.grandparent", values));
assertFalse(value.inheritsFrom("icon.test", values));
assertFalse(parent.inheritsFrom("icon.test", values));
assertFalse(grandParent.inheritsFrom("icon.test", values));
}
@Test
public void testCreatingValueFromGIcon() {
IconValue parent = new IconValue("icon.parent", ICON1);
values.addIcon(parent);
Icon gIcon = new GIcon("icon.parent");
IconValue value = new IconValue("icon.value", gIcon);
assertEquals("icon.parent", value.getReferenceId());
assertNull(value.getRawValue());
}
}
@@ -0,0 +1,127 @@
/* ###
* 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;
import static org.junit.Assert.*;
import java.awt.Color;
import java.awt.Font;
import javax.swing.Icon;
import org.junit.Before;
import org.junit.Test;
import resources.ResourceManager;
public class ThemeEventTest {
private static Font FONT1 = new Font("Dialog", 12, Font.PLAIN);
private static Font FONT2 = new Font("Dialog", 14, Font.PLAIN);
private static Icon ICON1 = ResourceManager.loadImage("images/flag.png");
private static Icon ICON2 = ResourceManager.loadImage("images/exec.png");
private GThemeValueMap values;
@Before
public void setup() {
values = new GThemeValueMap();
}
@Test
public void testIsColorChangedDirect() {
ColorValue value = new ColorValue("color.value", Color.RED);
values.addColor(value);
ColorValue newValue = new ColorValue("color.value", Color.BLUE);
values.addColor(value);
ColorChangedThemeEvent event = new ColorChangedThemeEvent(values, newValue);
assertTrue(event.isColorChanged("color.value"));
assertFalse(event.isColorChanged("color.othervalue"));
}
@Test
public void testIsColorChangedIndirect() {
ColorValue parent = new ColorValue("color.parent", Color.RED);
values.addColor(parent);
ColorValue value = new ColorValue("color.value", "color.parent");
values.addColor(value);
ColorValue newValue = new ColorValue("color.parent", Color.BLUE);
values.addColor(value);
ColorChangedThemeEvent event = new ColorChangedThemeEvent(values, newValue);
assertTrue(event.isColorChanged("color.parent"));
assertTrue(event.isColorChanged("color.value"));
assertFalse(event.isColorChanged("color.othervalue"));
}
@Test
public void testIsFontChangedDirect() {
FontValue value = new FontValue("font.value", FONT1);
values.addFont(value);
FontValue newValue = new FontValue("font.value", FONT2);
values.addFont(value);
FontChangedThemeEvent event = new FontChangedThemeEvent(values, newValue);
assertTrue(event.isFontChanged("font.value"));
assertFalse(event.isFontChanged("font.othervalue"));
}
@Test
public void testIsFontChangedIndirect() {
FontValue parent = new FontValue("font.parent", FONT1);
values.addFont(parent);
FontValue value = new FontValue("font.value", "font.parent");
values.addFont(value);
FontValue newValue = new FontValue("font.parent", FONT2);
values.addFont(value);
FontChangedThemeEvent event = new FontChangedThemeEvent(values, newValue);
assertTrue(event.isFontChanged("font.parent"));
assertTrue(event.isFontChanged("font.value"));
assertFalse(event.isFontChanged("font.othervalue"));
}
@Test
public void testIsIconChangedDirect() {
IconValue value = new IconValue("ICON.value", ICON1);
values.addIcon(value);
IconValue newValue = new IconValue("icon.value", ICON2);
values.addIcon(value);
IconChangedThemeEvent event = new IconChangedThemeEvent(values, newValue);
assertTrue(event.isIconChanged("icon.value"));
assertFalse(event.isIconChanged("icon.othervalue"));
}
@Test
public void testIsIconChangedIndirect() {
IconValue parent = new IconValue("icon.parent", ICON1);
values.addIcon(parent);
IconValue value = new IconValue("icon.value", "icon.parent");
values.addIcon(value);
IconValue newValue = new IconValue("icon.parent", ICON2);
values.addIcon(value);
IconChangedThemeEvent event = new IconChangedThemeEvent(values, newValue);
assertTrue(event.isIconChanged("icon.parent"));
assertTrue(event.isIconChanged("icon.value"));
assertFalse(event.isIconChanged("icon.othervalue"));
}
}