GP-2995 creating a better mapping of look and feel values to more standard ids and easier ways to override values for a specific LaF

This commit is contained in:
ghidragon 2023-01-13 17:58:27 -05:00
parent 898f12cc12
commit e5f1563c08
60 changed files with 1939 additions and 1283 deletions

View file

@ -5,12 +5,12 @@ color.bg.currentline.listing = color.bg.currentline
color.bg.selection.listing = color.bg.selection
color.bg.highlight.listing = color.bg.highlight
color.bg.listing.tabs.selected = [color]textHighlight
color.bg.listing.tabs.unselected = system.color.bg.application
color.bg.listing.tabs.selected = [color]system.color.bg.selected.view
color.bg.listing.tabs.unselected = [color]system.color.bg.control
color.bg.listing.tabs.highlighted = #ABC8FF
color.bg.listing.tabs.list = rgb(255, 255, 230)
color.bg.listing.tabs.more.tabs.hover = rgb(255, 226, 213)
color.fg.listing.tabs.text.selected = [color]textHighlightText
color.fg.listing.tabs.text.selected = [color]system.color.fg.selected.view
color.fg.listing.tabs.text.unselected = color.fg
color.fg.listing.tabs.list = black

View file

@ -6,8 +6,8 @@ color.flowtype.fall.through = red
color.flowtype.jump.conditional = #007C00 // dark green
color.flowtype.jump.unconditional = blue
color.bg.table.selection.bundle = [color]textHighlight
color.fg.table.selection.bundle = [color]textHighlightText
color.bg.table.selection.bundle = [color]system.color.bg.selected.view
color.fg.table.selection.bundle = [color]system.color.fg.selected.view
color.fg.table.bundle.disabled = darkGray
color.fg.table.bundle.busy = gray
color.fg.table.bundle.inactive = black
@ -75,7 +75,7 @@ color.bg.plugin.datamgr.icon.highlight = rgb(204, 204, 255)
color.fg.plugin.disassembledview.address = color.fg
color.bg.plugin.editors.compositeeditor.text = color.fg
color.bg.plugin.editors.compositeeditor.line = system.color.border
color.bg.plugin.editors.compositeeditor.line = [color]system.color.bg.border
color.bg.plugin.editors.compositeeditor.line.interior = #D4D4D4
color.bg.plugin.editors.compositeeditor.byte.header = #DFDFDF
color.bg.plugin.editors.compositeeditor.bit.undefined = #F8F8F8
@ -182,8 +182,6 @@ color.flowtype.fall.through = rgb(164, 66, 66)
color.flowtype.jump.conditional = rgb(95, 129, 157)
color.flowtype.jump.unconditional = rgb(140, 148, 64)
color.bg.table.selection.bundle = [color]textHighlight
color.fg.table.selection.bundle = [color]textHighlightText
color.fg.table.bundle.disabled = lightGray
color.fg.table.bundle.busy = gray
color.fg.table.bundle.inactive = lightGray
@ -211,7 +209,7 @@ color.bg.plugin.datamgr.edge.default = deepskyblue
color.bg.plugin.datamgr.edge.composite = plum
color.bg.plugin.datamgr.edge.reference = deepskyblue
color.bg.plugin.editors.compositeeditor.byte.header = [color]text
color.bg.plugin.editors.compositeeditor.byte.header = [color]system.color.bg.view
color.fg.plugin.equate.enum = deepskyblue

View file

@ -46,7 +46,7 @@ import ghidra.util.bean.opteditor.OptionsVetoException;
public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
private static final int WINDOW_OFFSET = 50;
private static final Color BACKGROUND_COLOR = Colors.BACKGROUND_TOOLTIP;
private static final Color BACKGROUND_COLOR = Colors.BG_TOOLTIP;
private static final Color FG_COLOR_NOT_IN_MEMORY = new GColor("color.fg.hint");
private CodeFormatService codeFormatService;
@ -128,8 +128,7 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
String widthOptionName = optionName + Options.DELIMITER + "Dialog Width";
String heightOptionName = optionName + Options.DELIMITER + "Dialog Height";
if (optionName.equals(widthOptionName) ||
optionName.equals(heightOptionName)) {
if (optionName.equals(widthOptionName) || optionName.equals(heightOptionName)) {
int dialogWidth = options.getInt(widthOptionName, 600);
if (dialogWidth <= 0) {
throw new OptionsVetoException(

View file

@ -38,7 +38,7 @@ import ghidra.app.plugin.core.console.CodeCompletion;
public class CodeCompletionWindow extends JDialog {
private static final long serialVersionUID = 1L;
/* from ReferenceHoverPlugin */
private static final Color BACKGROUND_COLOR = Colors.BACKGROUND_TOOLTIP;
private static final Color BACKGROUND_COLOR = Colors.BG_TOOLTIP;
protected final InterpreterPanel console;
protected final JTextPane outputTextField;

View file

@ -36,6 +36,7 @@ import resources.ResourceManager;
class DnDTreeCellRenderer extends DefaultTreeCellRenderer {
private static final Color BACKGROUND_UNSELECTED = new GColor("color.bg.tree");
private static final Color BACKGROUND_SELECTED = new GColor("color.bg.tree.selected");
private static final Color FOREGROUND_SELECTED = new GColor("color.fg.tree.selected");
private static final String DISABLED_DOCS = "DisabledDocument.gif";
private static final String DISABLED_FRAGMENT = "DisabledFragment";
@ -68,6 +69,7 @@ class DnDTreeCellRenderer extends DefaultTreeCellRenderer {
private Color defaultNonSelectionColor;
private Color selectionForDragColor;
private Color nonSelectionForDragColor;
private Color defaultTextSelectionColor;
private int rowForFeedback;
/**
@ -77,6 +79,7 @@ class DnDTreeCellRenderer extends DefaultTreeCellRenderer {
super();
defaultNonSelectionColor = BACKGROUND_UNSELECTED;
defaultSelectionColor = BACKGROUND_SELECTED;
defaultTextSelectionColor = FOREGROUND_SELECTED;
rowForFeedback = -1;
// disable HTML rendering
@ -168,12 +171,14 @@ class DnDTreeCellRenderer extends DefaultTreeCellRenderer {
}
else {
setBackgroundSelectionColor(defaultSelectionColor);
setTextSelectionColor(defaultTextSelectionColor);
setBackgroundNonSelectionColor(defaultNonSelectionColor);
}
setToolTipText(null);
}
else {
setBackgroundSelectionColor(defaultSelectionColor);
setTextSelectionColor(defaultTextSelectionColor);
setBackgroundNonSelectionColor(defaultNonSelectionColor);
setToolTipText(dtree.getToolTipText(node));
}
@ -315,11 +320,9 @@ class DnDTreeCellRenderer extends DefaultTreeCellRenderer {
private void loadImages() {
// try to load icon images
iconMap = new HashMap<>();
String[] iconIds =
{ DOCS, FRAGMENT, EMPTY_FRAGMENT, VIEWED_FRAGMENT, VIEWED_EMPTY_FRAGMENT,
VIEWED_CLOSED_FOLDER, VIEWED_OPEN_FOLDER, VIEWED_CLOSED_FOLDER_WITH_DESC,
CLOSED_FOLDER, OPEN_FOLDER,
};
String[] iconIds = { DOCS, FRAGMENT, EMPTY_FRAGMENT, VIEWED_FRAGMENT, VIEWED_EMPTY_FRAGMENT,
VIEWED_CLOSED_FOLDER, VIEWED_OPEN_FOLDER, VIEWED_CLOSED_FOLDER_WITH_DESC, CLOSED_FOLDER,
OPEN_FOLDER, };
String[] disabledNames = { DISABLED_DOCS, DISABLED_FRAGMENT, DISABLED_EMPTY_FRAGMENT,
DISABLED_VIEWED_EMPTY_FRAGMENT, DISABLED_VIEWED_FRAGMENT, DISABLED_VIEWED_CLOSED_FOLDER,
DISABLED_VIEWED_OPEN_FOLDER, DISABLED_VIEWED_CLOSED_FOLDER_WITH_DESC,

View file

@ -40,7 +40,7 @@ import ghidra.program.util.ProgramSelection;
*/
public interface FGVertex extends VisualVertex {
static final Color TOOLTIP_BACKGROUND_COLOR = Colors.BACKGROUND_TOOLTIP;
static final Color TOOLTIP_BACKGROUND_COLOR = Colors.BG_TOOLTIP;
public FGVertex cloneVertex(FGController newController);

View file

@ -4,9 +4,9 @@
color.bg.splashscreen = black
color.fg.splashscreen = gray
color.bg.header.active = [color]textHighlight
color.bg.header.active = [color]system.color.bg.selected.view
color.bg.header.inactive = #A1A1A1
color.fg.header.active = [color]textHighlightText
color.fg.header.active = [color]system.color.fg.selected.view
color.fg.header.inactive = black
color.header.drag.cursor = black
@ -170,7 +170,7 @@ color.bg.fieldpanel.selection.and.highlight = #344028 // yellow greenish
// docking buttons
color.fg.button = darkGray
color.bg.filechooser.shortcut = system.color.bg.widget
color.bg.filechooser.shortcut = [color]system.color.bg.view

View file

@ -133,7 +133,7 @@ public class EditWindow extends JWindow {
private void create() {
textField = new JTextField(" ");
JPanel panel = new JPanel(new BorderLayout());
Color bgColor = Colors.BACKGROUND_TOOLTIP;
Color bgColor = Colors.BG_TOOLTIP;
panel.setBackground(bgColor);
panel.add(textField, BorderLayout.CENTER);

View file

@ -40,7 +40,7 @@ public class SplitPanel extends JPanel {
this.rightComp = rightComp;
this.isHorizontal = isHorizontal;
divider = new Divider();
divider.setBackground(new GColor("SplitPane.background"));
divider.setBackground(new GColor("laf.color.SplitPane.background"));
add(leftComp);
add(divider);
add(rightComp);

View file

@ -0,0 +1,80 @@
/* ###
* 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 docking.theme.gui;
import generic.theme.GThemeValueMap;
import generic.theme.ThemeManager;
/**
* Shares values for the three theme value tables so they all don't have their own copies
*/
public class GThemeValuesCache {
private ThemeManager themeManager;
private GThemeValueMap currentValues;
private GThemeValueMap themeValues;
private GThemeValueMap defaultValues;
private GThemeValueMap lightValues;
private GThemeValueMap darkValues;
public GThemeValuesCache(ThemeManager themeManager) {
this.themeManager = themeManager;
}
public void clear() {
currentValues = null;
themeValues = null;
defaultValues = null;
lightValues = null;
darkValues = null;
}
public GThemeValueMap getCurrentValues() {
if (currentValues == null) {
currentValues = themeManager.getCurrentValues();
}
return currentValues;
}
public GThemeValueMap getThemeValues() {
if (themeValues == null) {
themeValues = themeManager.getThemeValues();
}
return themeValues;
}
public GThemeValueMap getDefaultValues() {
if (defaultValues == null) {
defaultValues = themeManager.getDefaults();
}
return defaultValues;
}
public GThemeValueMap getLightValues() {
if (lightValues == null) {
lightValues = themeManager.getApplicationLightDefaults();
}
return lightValues;
}
public GThemeValueMap getDarkValues() {
if (darkValues == null) {
darkValues = themeManager.getApplicationDarkDefaults();
}
return darkValues;
}
}

View file

@ -43,14 +43,14 @@ public class ThemeColorTable extends JPanel implements ActionContextProvider {
private GFilterTable<ColorValue> filterTable;
private ThemeManager themeManager;
public ThemeColorTable(ThemeManager themeManager) {
public ThemeColorTable(ThemeManager themeManager, GThemeValuesCache valuesProvider) {
super(new BorderLayout());
this.themeManager = themeManager;
colorTableModel = new ThemeColorTableModel(themeManager);
colorTableModel = new ThemeColorTableModel(valuesProvider);
filterTable = new GFilterTable<>(colorTableModel);
table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
table.addKeyListener(new KeyAdapter() {
@Override

View file

@ -43,11 +43,11 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
private GThemeValueMap defaultValues;
private GThemeValueMap lightDefaultValues;
private GThemeValueMap darkDefaultValues;
private ThemeManager themeManager;
private GThemeValuesCache valuesCache;
public ThemeColorTableModel(ThemeManager themeManager) {
public ThemeColorTableModel(GThemeValuesCache valuesProvider) {
super(new ServiceProviderStub());
this.themeManager = themeManager;
this.valuesCache = valuesProvider;
load();
}
@ -55,7 +55,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
* Reloads the just the current values shown in the table. Called whenever a color changes.
*/
public void reloadCurrent() {
currentValues = themeManager.getCurrentValues();
currentValues = valuesCache.getCurrentValues();
colors = currentValues.getColors();
fireTableDataChanged();
}
@ -74,12 +74,12 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
}
private void load() {
currentValues = themeManager.getCurrentValues();
currentValues = valuesCache.getCurrentValues();
colors = currentValues.getColors();
themeValues = themeManager.getThemeValues();
defaultValues = themeManager.getDefaults();
lightDefaultValues = themeManager.getApplicationLightDefaults();
darkDefaultValues = themeManager.getApplicationDarkDefaults();
themeValues = valuesCache.getThemeValues();
defaultValues = valuesCache.getDefaultValues();
lightDefaultValues = valuesCache.getLightValues();
darkDefaultValues = valuesCache.getDarkValues();
}
@ -110,7 +110,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
return null;
}
class IdColumn extends AbstractDynamicTableColumn<ColorValue, String, Object> {
private class IdColumn extends AbstractDynamicTableColumn<ColorValue, String, Object> {
@Override
public String getColumnName() {
@ -129,7 +129,8 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
}
}
class ValueColumn extends AbstractDynamicTableColumn<ColorValue, ResolvedColor, Object> {
private class ValueColumn
extends AbstractDynamicTableColumn<ColorValue, ResolvedColor, Object> {
private ThemeColorRenderer renderer;
private String name;
private Supplier<GThemeValueMap> valueSupplier;
@ -236,7 +237,7 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
}
static class SwatchIcon implements Icon {
private static class SwatchIcon implements Icon {
private Color color;
private Color border;
@ -264,8 +265,5 @@ public class ThemeColorTableModel extends GDynamicColumnTableModel<ColorValue, O
}
}
record ResolvedColor(String id, String refId, Color color) {
//
}
private record ResolvedColor(String id, String refId, Color color) { /**/ }
}

View file

@ -51,6 +51,8 @@ public class ThemeDialog extends DialogComponentProvider {
private ThemeManager themeManager;
private GThemeValuesCache valuesCache;
public ThemeDialog(ThemeManager themeManager) {
super("Theme Dialog", false);
this.themeManager = themeManager;
@ -227,12 +229,17 @@ public class ThemeDialog extends DialogComponentProvider {
private Component buildTabedTables() {
tabbedPane = new JTabbedPane();
colorTable = new ThemeColorTable(themeManager);
fontTable = new ThemeFontTable(themeManager);
iconTable = new ThemeIconTable(themeManager);
valuesCache = new GThemeValuesCache(themeManager);
colorTable = new ThemeColorTable(themeManager, valuesCache);
iconTable = new ThemeIconTable(themeManager, valuesCache);
fontTable = new ThemeFontTable(themeManager, valuesCache);
tabbedPane.add("Colors", colorTable);
tabbedPane.add("Fonts", fontTable);
tabbedPane.add("Icons", iconTable);
return tabbedPane;
}
@ -284,6 +291,7 @@ public class ThemeDialog extends DialogComponentProvider {
private class DialogThemeListener implements ThemeListener {
@Override
public void themeChanged(ThemeEvent event) {
valuesCache.clear();
if (event.haveAllValuesChanged()) {
reset();
return;

View file

@ -43,11 +43,11 @@ public class ThemeFontTable extends JPanel implements ActionContextProvider {
private GFilterTable<FontValue> filterTable;
private ThemeManager themeManager;
public ThemeFontTable(ThemeManager themeManager) {
public ThemeFontTable(ThemeManager themeManager, GThemeValuesCache valuesProvider) {
super(new BorderLayout());
this.themeManager = themeManager;
fontTableModel = new ThemeFontTableModel(themeManager);
fontTableModel = new ThemeFontTableModel(valuesProvider);
filterTable = new GFilterTable<>(fontTableModel);
table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

View file

@ -24,7 +24,8 @@ import java.util.function.Supplier;
import javax.swing.JLabel;
import docking.widgets.table.*;
import generic.theme.*;
import generic.theme.FontValue;
import generic.theme.GThemeValueMap;
import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.plugintool.ServiceProviderStub;
@ -39,11 +40,11 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
private GThemeValueMap currentValues;
private GThemeValueMap themeValues;
private GThemeValueMap defaultValues;
private ThemeManager themeManager;
private GThemeValuesCache valuesProvider;
public ThemeFontTableModel(ThemeManager themeManager) {
public ThemeFontTableModel(GThemeValuesCache valuesProvider) {
super(new ServiceProviderStub());
this.themeManager = themeManager;
this.valuesProvider = valuesProvider;
load();
}
@ -51,7 +52,7 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
* Reloads the just the current values shown in the table. Called whenever a font changes.
*/
public void reloadCurrent() {
currentValues = themeManager.getCurrentValues();
currentValues = valuesProvider.getCurrentValues();
fonts = currentValues.getFonts();
fireTableDataChanged();
}
@ -66,10 +67,10 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
}
private void load() {
currentValues = themeManager.getCurrentValues();
currentValues = valuesProvider.getCurrentValues();
fonts = currentValues.getFonts();
themeValues = themeManager.getThemeValues();
defaultValues = themeManager.getDefaults();
themeValues = valuesProvider.getThemeValues();
defaultValues = valuesProvider.getDefaultValues();
}
@Override
@ -97,19 +98,28 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
return null;
}
private String getValueText(FontValue fontValue) {
if (fontValue == null) {
/**
* Returns the original value for the id as defined by the current theme
* @param id the resource id to get a font value for
* @return the original value for the id as defined by the current theme
*/
public FontValue getThemeValue(String id) {
return themeValues.getFont(id);
}
private String getValueText(ResolvedFont resolvedFont) {
if (resolvedFont == null) {
return "<No Value>";
}
if (fontValue.getReferenceId() != null) {
return "[" + fontValue.getReferenceId() + "]";
if (resolvedFont.refId() != null) {
return "[" + resolvedFont.refId() + "]";
}
Font font = fontValue.getRawValue();
Font font = resolvedFont.font();
return FontValue.fontToString(font);
}
class IdColumn extends AbstractDynamicTableColumn<FontValue, String, Object> {
private class IdColumn extends AbstractDynamicTableColumn<FontValue, String, Object> {
@Override
public String getColumnName() {
@ -128,7 +138,8 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
}
}
class FontValueColumn extends AbstractDynamicTableColumn<FontValue, FontValue, Object> {
private class FontValueColumn
extends AbstractDynamicTableColumn<FontValue, ResolvedFont, Object> {
private ThemeFontRenderer renderer;
private String name;
private Supplier<GThemeValueMap> valueSupplier;
@ -145,19 +156,23 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
}
@Override
public FontValue getValue(FontValue fontValue, Settings settings, Object data,
public ResolvedFont getValue(FontValue themeFont, Settings settings, Object data,
ServiceProvider provider) throws IllegalArgumentException {
GThemeValueMap valueMap = valueSupplier.get();
String id = fontValue.getId();
return valueMap.getFont(id);
String id = themeFont.getId();
FontValue fontValue = valueMap.getFont(id);
if (fontValue == null) {
return null;
}
return new ResolvedFont(id, fontValue.getReferenceId(), valueMap.getResolvedFont(id));
}
@Override
public GColumnRenderer<FontValue> getColumnRenderer() {
public GColumnRenderer<ResolvedFont> getColumnRenderer() {
return renderer;
}
public Comparator<FontValue> getComparator() {
public Comparator<ResolvedFont> getComparator() {
return (v1, v2) -> {
if (v1 == null && v2 == null) {
return 0;
@ -179,33 +194,31 @@ public class ThemeFontTableModel extends GDynamicColumnTableModel<FontValue, Obj
}
private class ThemeFontRenderer extends AbstractGColumnRenderer<FontValue> {
private class ThemeFontRenderer extends AbstractGColumnRenderer<ResolvedFont> {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
FontValue fontValue = (FontValue) data.getValue();
ResolvedFont resolvedFont = (ResolvedFont) data.getValue();
String text = getValueText(fontValue);
String text = getValueText(resolvedFont);
label.setText(text);
label.setOpaque(true);
Font font = resolvedFont.font();
if (font != null) {
setToolTipText(FontValue.fontToString(font));
}
return label;
}
@Override
public String getFilterString(FontValue fontValue, Settings settings) {
return getValueText(fontValue);
public String getFilterString(ResolvedFont resolvedFont, Settings settings) {
return getValueText(resolvedFont);
}
}
/**
* Returns the original value for the id as defined by the current theme
* @param id the resource id to get a font value for
* @return the original value for the id as defined by the current theme
*/
public FontValue getThemeValue(String id) {
return themeValues.getFont(id);
}
private record ResolvedFont(String id, String refId, Font font) {/**/}
}

View file

@ -41,10 +41,10 @@ public class ThemeIconTable extends JPanel implements ActionContextProvider {
private GFilterTable<IconValue> filterTable;
private ThemeManager themeManager;
public ThemeIconTable(ThemeManager themeManager) {
public ThemeIconTable(ThemeManager themeManager, GThemeValuesCache valuesProvider) {
super(new BorderLayout());
this.themeManager = themeManager;
iconTableModel = new ThemeIconTableModel(themeManager);
iconTableModel = new ThemeIconTableModel(valuesProvider);
filterTable = new GFilterTable<>(iconTableModel);
table = filterTable.getTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

View file

@ -39,11 +39,11 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
private GThemeValueMap currentValues;
private GThemeValueMap themeValues;
private GThemeValueMap defaultValues;
private ThemeManager themeManager;
private GThemeValuesCache valuesProvider;
public ThemeIconTableModel(ThemeManager themeManager) {
public ThemeIconTableModel(GThemeValuesCache valuesProvider) {
super(new ServiceProviderStub());
this.themeManager = themeManager;
this.valuesProvider = valuesProvider;
load();
}
@ -51,7 +51,7 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
* Reloads the just the current values shown in the table. Called whenever an icon changes.
*/
public void reloadCurrent() {
currentValues = themeManager.getCurrentValues();
currentValues = valuesProvider.getCurrentValues();
icons = currentValues.getIcons();
fireTableDataChanged();
}
@ -66,10 +66,10 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
}
private void load() {
currentValues = themeManager.getCurrentValues();
currentValues = valuesProvider.getCurrentValues();
icons = currentValues.getIcons();
themeValues = themeManager.getThemeValues();
defaultValues = themeManager.getDefaults();
themeValues = valuesProvider.getThemeValues();
defaultValues = valuesProvider.getDefaultValues();
}
@Override
@ -97,7 +97,16 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
return null;
}
class IdColumn extends AbstractDynamicTableColumn<IconValue, String, Object> {
/**
* Returns the original value for the id as defined by the current theme
* @param id the resource id to get a font value for
* @return the original value for the id as defined by the current theme
*/
public IconValue getThemeValue(String id) {
return themeValues.getIcon(id);
}
private class IdColumn extends AbstractDynamicTableColumn<IconValue, String, Object> {
@Override
public String getColumnName() {
@ -116,7 +125,8 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
}
}
class IconValueColumn extends AbstractDynamicTableColumn<IconValue, ResolvedIcon, Object> {
private class IconValueColumn
extends AbstractDynamicTableColumn<IconValue, ResolvedIcon, Object> {
private ThemeIconRenderer renderer;
private String name;
private Supplier<GThemeValueMap> valueSupplier;
@ -232,14 +242,6 @@ public class ThemeIconTableModel extends GDynamicColumnTableModel<IconValue, Obj
}
}
record ResolvedIcon(String id, String refId, Icon icon) {/**/}
private record ResolvedIcon(String id, String refId, Icon icon) {/**/}
/**
* Returns the original value for the id as defined by the current theme
* @param id the resource id to get a font value for
* @return the original value for the id as defined by the current theme
*/
public IconValue getThemeValue(String id) {
return themeValues.getIcon(id);
}
}

View file

@ -34,6 +34,7 @@ public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent
private static final int DEFAULT_MIN_ICON_WIDTH = 22;
private static final Color BACKGROUND_UNSELECTED = new GColor("color.bg.tree");
private static final Color BACKGROUND_SELECTED = new GColor("color.bg.tree.selected");
private static final Color FOREGROUND_SELECTED = new GColor("color.fg.tree.selected");
private Object dropTarget;
private boolean paintDropTarget;
@ -46,6 +47,7 @@ public class GTreeRenderer extends DefaultTreeCellRenderer implements GComponent
setHTMLRenderingEnabled(false);
setBackgroundNonSelectionColor(BACKGROUND_UNSELECTED);
setBackgroundSelectionColor(BACKGROUND_SELECTED);
setTextSelectionColor(FOREGROUND_SELECTED);
}
@Override

View file

@ -8,7 +8,6 @@
##MODULE IP: Tango Icons - Public Domain
.classpath||GHIDRA||||END|
Module.manifest||GHIDRA||||END|
data/gui.laf.overrides.theme.properties||GHIDRA||||END|
data/gui.palette.theme.properties||GHIDRA||||END|
data/gui.theme.properties||GHIDRA||||END|
src/main/java/ghidra/framework/options/package.html||GHIDRA||||END|

View file

@ -1,55 +0,0 @@
[Defaults]
// TODO these are only here because they are used in the dark section; remove from here if done in
// source code instead; otherwise, update error reporting to not require an entry in the default
// section for values defined in specific LaF sections
color.flat.text.original.bg = white // default 'text' value in Flat Light
color.flat.selection.inactive.bg = #d3d3d3 // default value in Flat Light
[Dark Defaults]
[Flat Dark]
// the default inactive selection color is too close to the bg color to be easily visible
color.flat.selection.inactive.bg = #0F4C6A
//
// We would like widgets to use a bg color. By default widgets and some other items are all mapped
// to 'text'. The easiest way to change all widgets is to change the value of 'text'. Then, for
// any values that should still use the old value, we need to remap those. (The foreground colors
// for these are mapped to 'textText'. For now, that value seems good enough that we do not need
// to change it.)
//
color.flat.text.original.bg = #46494b // default 'text' value
[color]text = color.bg
[color]desktop = color.flat.text.original.bg
[color]TableHeader = color.flat.text.original.bg
[color]Checkbox.icon.background = color.flat.text.original.bg
[color]Checkbox.icon.selectedBackground = color.flat.text.original.bg
[color]CheckBox.icon[filled].checkmarkColor = color.flat.text.original.bg
[color]ComboBox.buttonBackground = color.flat.text.original.bg
[color]Spinner.background = color.flat.text.original.bg
[color]TextArea.background = color.bg
[color]TextArea.foreground = color.fg
[color]TextPane.background = color.bg
[color]TextPane.foreground = color.fg
[Nimbus]
// Nimbus uses the ToolTipPainter class to paint tooltips. That class does not read the property
// we use for tooltips, which is 'ToolTip.background', even though that is defined by the LaF.
// Setting the 'info' value here to changes the key the painter users to paint tooltips. The info
// value seems to only be used for tooltips, which means this change affects no other components.
[color]info = [color]ToolTip.background

View file

@ -1,7 +1,8 @@
[Defaults]
color.bg = white // note: this is the text/widget bg color
color.fg = black
// we define these as shortcuts for the view foreground, background
color.bg = [color]system.color.bg.view
color.fg = [color]system.color.fg.view
// On some LaFs the tables and tree use the bg color we define. Make that consistent for all LaFs.
[color]Viewport.background = color.bg
@ -12,12 +13,10 @@ color.cursor.unfocused = pink
color.fg.error = red
color.bg.error = lightcoral
color.fg.disabled = lightGray
color.bg.uneditable = system.color.bg.application // TODO see if there exists an LaF setting for this
color.bg.uneditable = [color]system.color.bg.control
color.bg.filtered = yellow
color.fg.hint = gray
color.bg.tooltip = [color]ToolTip.background
color.fg.messages.hint = color.fg.hint
color.fg.messages.alert = orange
color.fg.messages.error = color.fg.error
@ -35,14 +34,12 @@ color.fg.table.unselected = white
color.fg.error.table.unselected = color.fg.error
color.fg.error.table.selected = lightpink
color.bg.tree = color.bg
color.bg.tree.selected = [color]Tree.selectionBackground
color.bg.tree = [color]system.color.bg.view
color.bg.tree.selected = [color]system.color.bg.selected.view
color.fg.tree.selected = [color]system.color.fg.selected.view
// Fonts
font.standard = [font]Panel.font
font.bold = font.standard[bold]
font.italic = font.standard[italic]
font.bold.italic = font.standard[bold][italic]
font.standard = [font]system.font.control
font.monospaced = monospaced-PLAIN-12
// Icons files
@ -109,9 +106,6 @@ icon.arrow.up.left = viewmagfit.png[rotate(275)]
[Dark Defaults]
color.fg = lightgray
color.bg = #1c1d1e
color.fg.error = indianRed
color.fg.disabled = gray
color.bg.filtered = beige
@ -125,15 +119,9 @@ color.fg.table.uneditable.selected = lemonchiffon
color.fg.table.uneditable.unselected = lightgray
color.bg.tree = color.bg
color.bg.tree.selected = [color]Tree.selectionBackground
color.bg.tree.selected = [color]laf.color.Tree.selectionBackground
// this looks different than the light mode version; good enough until we make a better icon
icon.make.selection = stack.png
[CDE/Motif]
color.bg = [color]window // gray for motif
color.fg = [color]textText

View file

@ -16,28 +16,27 @@
package generic.theme;
/**
* Loads all the system theme.property files that contain all the default color, font, and
* icon values.
* Provides theme default values, such as those loaded from {@code *.theme.property} files.
*/
public interface ThemeDefaultsProvider {
public interface ApplicationThemeDefaults {
/**
* Returns the standard defaults {@link GThemeValueMap}
* @return the standard defaults {@link GThemeValueMap}
* Returns the light default {@link GThemeValueMap}
* @return the light default {@link GThemeValueMap}
*/
public GThemeValueMap getDefaults();
public GThemeValueMap getLightValues();
/**
* Returns the dark defaults {@link GThemeValueMap}
* @return the dark defaults {@link GThemeValueMap}
* Returns the dark default {@link GThemeValueMap}
* @return the dark default {@link GThemeValueMap}
*/
public GThemeValueMap getDarkDefaults();
public GThemeValueMap getDarkValues();
/**
* Returns the defaults specific to a given Look and Feel
* Returns the default values specific to a given Look and Feel
* @param lafType the Look and Feel type
* @return the defaults specific to a given Look and Feel
* @return the default values specific to a given Look and Feel
*/
public GThemeValueMap getLookAndFeelDefaults(LafType lafType);
public GThemeValueMap getLookAndFeelValues(LafType lafType);
}

View file

@ -73,7 +73,7 @@ public class ApplicationThemeManager extends ThemeManager {
@Override
public void reloadApplicationDefaults() {
themeDefaultsProvider = getThemeDefaultsProvider();
applicationDefaults = getApplicationDefaults();
buildCurrentValues();
lookAndFeelManager.resetAll(javaDefaults);
notifyThemeChanged(new AllValuesChangedThemeEvent(false));
@ -127,6 +127,7 @@ public class ApplicationThemeManager extends ThemeManager {
if (theme.hasSupportedLookAndFeel()) {
activeTheme = theme;
LafType lafType = theme.getLookAndFeelType();
cleanUiDefaults(); // clear out any values previous themes may have installed
lookAndFeelManager = lafType.getLookAndFeelManager(this);
try {
lookAndFeelManager.installLookAndFeel();
@ -247,20 +248,6 @@ public class ApplicationThemeManager extends ThemeManager {
return gColor;
}
/**
* Sets specially defined system UI values. These values are created by the application as a
* convenience for mapping generic concepts to values that differ by Look and Feel. This allows
* clients to use 'system' properties without knowing the actual Look and Feel terms.
*
* <p>For example, 'system.color.border' defaults to 'controlShadow', but maps to 'nimbusBorder'
* in the Nimbus Look and Feel.
*
* @param map the map
*/
public void setSystemDefaults(GThemeValueMap map) {
systemValues = map;
}
/**
* Sets the map of Java default UI values. These are the UI values defined by the current Java
* Look and Feel.
@ -409,4 +396,9 @@ public class ApplicationThemeManager extends ThemeManager {
GColor.refreshAll(currentValues);
GIcon.refreshAll(currentValues);
}
private void cleanUiDefaults() {
UIDefaults defaults = UIManager.getDefaults();
defaults.clear();
}
}

View file

@ -30,7 +30,6 @@ import utilities.util.reflection.ReflectionUtilities;
public class ColorValue extends ThemeValue<Color> {
private static final String COLOR_ID_PREFIX = "color.";
private static final String EXTERNAL_PREFIX = "[color]";
private static final String SYSTEM_COLOR_PREFIX = "system.color";
public static final Color LAST_RESORT_DEFAULT = new Color(128, 128, 128);
@ -72,8 +71,7 @@ public class ColorValue extends ThemeValue<Color> {
* @return true if the given key string is a valid external key for a color value
*/
public static boolean isColorKey(String key) {
return key.startsWith(COLOR_ID_PREFIX) || key.startsWith(EXTERNAL_PREFIX) ||
key.startsWith(SYSTEM_COLOR_PREFIX);
return key.startsWith(COLOR_ID_PREFIX) || key.startsWith(EXTERNAL_PREFIX);
}
/**
@ -107,15 +105,14 @@ public class ColorValue extends ThemeValue<Color> {
"Application", "ghidra.GhidraRun", "java.lang.Class", "java.lang.Thread");
t.setStackTrace(filtered);
Msg.error(this,
"Could not resolve indirect color path for \"" + unresolvedId +
"\" for primary id \"" + primaryId + "\", using last resort default",
t);
Msg.error(this, "Could not resolve indirect color path for \"" + unresolvedId +
"\" for primary id \"" + primaryId + "\", using last resort default", t);
return LAST_RESORT_DEFAULT;
}
private static String toExternalId(String internalId) {
if (internalId.startsWith(COLOR_ID_PREFIX) || internalId.startsWith(SYSTEM_COLOR_PREFIX)) {
if (internalId.startsWith(COLOR_ID_PREFIX)) {
return internalId;
}
return EXTERNAL_PREFIX + internalId;

View file

@ -37,6 +37,8 @@ import ghidra.util.datastruct.WeakStore;
* set the default value by adding this line "color.mywidget.bg = white".
*/
public class GColor extends Color {
private static final int MISSING_COLOR_RGB = 0x808080;
// keeps a weak reference to all uses of GColor, so their cached color value can be refreshed
private static WeakStore<GColor> inUseColors = new WeakStore<>();
@ -50,11 +52,22 @@ public class GColor extends Color {
* @param id the id used to lookup the current value for this color
*/
public GColor(String id) {
super(0x808080);
super(MISSING_COLOR_RGB);
this.id = id;
delegate = Gui.getColor(id);
inUseColors.add(this);
}
/**
* Copy constructor. Used primarily to convert a GColorUiResource to a GColor without having to
* lookup the color which can cause errors during theme transitions.
* @param gColor the gColor to copy
*/
protected GColor(GColor gColor) {
super(MISSING_COLOR_RGB);
this.id = gColor.id;
delegate = gColor.delegate;
inUseColors.add(this);
}
private GColor(String id, int alpha) {

View file

@ -15,8 +15,6 @@
*/
package generic.theme;
import java.awt.Color;
import javax.swing.UIDefaults;
import javax.swing.plaf.UIResource;
@ -36,8 +34,8 @@ public class GColorUIResource extends GColor implements UIResource {
* Returns a non-UIResource GColor for this GColorUiResource's id
* @return a non-UIResource GColor for this GColorUiResource's id
*/
public Color toGColor() {
return new GColor(getId());
public GColor toGColor() {
return new GColor(this);
}
}

View file

@ -15,6 +15,8 @@
*/
package generic.theme;
import static generic.theme.SystemThemeIds.*;
import java.awt.Color;
/** TODO doc how clients should use this in their code, with
@ -24,18 +26,9 @@ import java.awt.Color;
* Colors.Java.BORDER
*/
public class GThemeDefaults {
public static class Ids {
public static class Java {
public static final String BORDER = "system.color.border";
}
public static class Fonts {
public static final String STANDARD = "font.standard";
public static final String BOLD = "font.bold";
public static final String ITALIC = "font.italic";
public static final String BOLD_ITALIC = "font.bold.italic";
public static final String MONOSPACED = "font.monospaced";
}
}
@ -44,11 +37,23 @@ public class GThemeDefaults {
* Colors mapped to system values
*/
public static class Colors {
//@formatter:off
// standard color concepts defined by LookAndFeel
public static final GColor BG_CONTROL = new GColor(BG_CONTROL_ID);
public static final GColor BG_VIEW = new GColor(BG_VIEW_ID);
public static final GColor BG_TOOLTIP = new GColor(BG_TOOLTIP_ID);
public static final GColor BG_VIEW_SELECTED = new GColor(BG_VIEW_SELECTED_ID);
public static final GColor BG_BORDER = new GColor(BG_BORDER_ID);
public static final GColor FG_CONTROL = new GColor(FG_CONTROL_ID);
public static final GColor FG_VIEW = new GColor(FG_VIEW_ID);
public static final GColor FG_TOOLTIP = new GColor(FG_TOOLTIP_ID);
public static final GColor FG_VIEW_SELECTED = new GColor(FG_VIEW_SELECTED_ID);
public static final GColor FG_DISABLED = new GColor(FG_DISABLED_ID);
// generic color concepts
//@formatter:off
public static final GColor BACKGROUND = new GColor("color.bg");
public static final GColor BACKGROUND_TOOLTIP = new GColor("color.bg.tooltip");
public static final GColor CURSOR = new GColor("color.cursor.focused");
public static final GColor DISABLED = new GColor("color.palette.disabled");
public static final GColor ERROR = new GColor("color.fg.error"); // TODO replace most uses of this with Messages.ERROR
@ -57,7 +62,7 @@ public class GThemeDefaults {
//@formatter:on
public static class Java {
public static final GColor BORDER = new GColor(Ids.Java.BORDER);
public static final GColor BORDER = BG_BORDER;
}
public static class Tables {

View file

@ -15,6 +15,8 @@
*/
package generic.theme;
import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.net.URL;
import java.util.*;
@ -340,4 +342,50 @@ public class GThemeValueMap {
public Set<String> getIconIds() {
return iconMap.keySet();
}
/**
* Returns the resolved color, following indirections as need to get the color ultimately
* assigned to the given id.
* @param id the id for which to get a color
* @return the resolved color, following indirections as need to get the color ultimately
* assigned to the given id.
*/
public Color getResolvedColor(String id) {
ColorValue colorValue = colorMap.get(id);
if (colorValue != null) {
return colorValue.get(this);
}
return null;
}
/**
* Returns the resolved font, following indirections as need to get the font ultimately
* assigned to the given id.
* @param id the id for which to get a font
* @return the resolved font, following indirections as need to get the font ultimately
* assigned to the given id
*/
public Font getResolvedFont(String id) {
FontValue fontValue = fontMap.get(id);
if (fontValue != null) {
return fontValue.get(this);
}
return null;
}
/**
* Returns the resolved icon, following indirections as need to get the icon ultimately
* assigned to the given id.
* @param id the id for which to get an icon
* @return the resolved icon, following indirections as need to get the icon ultimately
* assigned to the given id
*/
public Icon getResolvedIcon(String id) {
IconValue iconValue = iconMap.get(id);
if (iconValue != null) {
return iconValue.get(this);
}
return null;
}
}

View file

@ -26,13 +26,13 @@ import ghidra.util.Msg;
* Loads all the system theme.property files that contain all the default color, font, and
* icon values.
*/
public class ApplicationThemeDefaultsProvider implements ThemeDefaultsProvider {
public class PropertyFileThemeDefaults implements ApplicationThemeDefaults {
private GThemeValueMap defaults = new GThemeValueMap();
private GThemeValueMap darkDefaults = new GThemeValueMap();
private Map<LafType, GThemeValueMap> lafDefaultsMap = new HashMap<>();
ApplicationThemeDefaultsProvider() {
PropertyFileThemeDefaults() {
loadThemeDefaultFiles();
}
@ -69,17 +69,17 @@ public class ApplicationThemeDefaultsProvider implements ThemeDefaultsProvider {
}
@Override
public GThemeValueMap getDefaults() {
public GThemeValueMap getLightValues() {
return defaults;
}
@Override
public GThemeValueMap getDarkDefaults() {
public GThemeValueMap getDarkValues() {
return darkDefaults;
}
@Override
public GThemeValueMap getLookAndFeelDefaults(LafType lafType) {
public GThemeValueMap getLookAndFeelValues(LafType lafType) {
return lafDefaultsMap.get(lafType);
}

View file

@ -215,21 +215,21 @@ public class StubThemeManager extends ThemeManager {
}
@Override
protected ThemeDefaultsProvider getThemeDefaultsProvider() {
return new ThemeDefaultsProvider() {
protected ApplicationThemeDefaults getApplicationDefaults() {
return new ApplicationThemeDefaults() {
@Override
public GThemeValueMap getDefaults() {
public GThemeValueMap getLightValues() {
return null;
}
@Override
public GThemeValueMap getDarkDefaults() {
public GThemeValueMap getDarkValues() {
return null;
}
@Override
public GThemeValueMap getLookAndFeelDefaults(LafType lafType) {
public GThemeValueMap getLookAndFeelValues(LafType lafType) {
return null;
}

View file

@ -0,0 +1,68 @@
/* ###
* 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 generic.theme.laf.UiDefaultsMapper;
/**
* These are the standard system ids defined to represent general LookAndFeel color and font
* concepts. Various LaF have different names for these concepts and even defines additional
* concepts. These are the ones we use regardless of the LookAndFeel being used. When we
* load a specific LookAndFeel, a {@link UiDefaultsMapper}, specific to that LaF is used to map
* its system ids to our standard system ids. Also, {@link GThemeDefaults} uses these system
* ids to define colors that can be used throughout the application without using these ids
* directly.
* <P>
* The ids are assigned to categories as follows:
* <UL>
* <LI>CONTROL- these ids are used for colors and fonts for general system components such as
* Buttons, Checkboxes, or anything that doesn't fit into one of the other areas</LI>
* <LI>VIEW - these ids are used for the colors and fonts used for widgets that display data
* such as Trees, Tables, TextFieds, and Lists</LI>
* <LI>MENU - these ids are used by menu components such as Menus and MenuItems.</LI>
* <LI>TOOLTIP - these ids are used just by the tooltip component
* </UL>
* <P>
* For each of those categories the ids specify a specific property for those components.
* <UL>
* <LI> BG - the background color
* <LI> FG - the foreground color
* <LI> BG_SELECTED - the background color when the component is selected
* <LI> FG_SELECTED - the foreground color when the component is selected
* <LI> FG_DISABLED - the foreground color when the component is disabled
* <LI> BG_BORDER - the border color
* <LI> FONT - the font
*
* </UL>
*/
public class SystemThemeIds {
public static final String FONT_CONTROL_ID = "system.font.control";
public static final String FONT_VIEW_ID = "system.font.view";
public static final String FONT_MENU_ID = "system.font.menu";
public static final String BG_CONTROL_ID = "system.color.bg.control";
public static final String BG_VIEW_ID = "system.color.bg.view";
public static final String BG_TOOLTIP_ID = "system.color.bg.tooltip";
public static final String BG_VIEW_SELECTED_ID = "system.color.bg.selected.view";
public static final String BG_BORDER_ID = "system.color.bg.border";
public static final String FG_CONTROL_ID = "system.color.fg.control";
public static final String FG_VIEW_ID = "system.color.fg.view";
public static final String FG_TOOLTIP_ID = "system.color.fg.tooltip";
public static final String FG_VIEW_SELECTED_ID = "system.color.fg.selected.view";
public static final String FG_DISABLED_ID = "system.color.fg.disabled";
}

View file

@ -30,7 +30,6 @@ import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
import resources.ResourceManager;
import utilities.util.reflection.ReflectionUtilities;
import utility.function.Callback;
/**
* This class manages application themes and their values. The ThemeManager is an abstract
@ -67,10 +66,9 @@ public abstract class ThemeManager {
protected GTheme activeTheme = getDefaultTheme();
protected GThemeValueMap javaDefaults = new GThemeValueMap();
protected GThemeValueMap systemValues = new GThemeValueMap();
protected GThemeValueMap currentValues = new GThemeValueMap();
protected ThemeDefaultsProvider themeDefaultsProvider;
protected ApplicationThemeDefaults applicationDefaults;
// these notifications are only when the user is manipulating theme values, so rare and at
// user speed, so using copy on read
@ -86,11 +84,11 @@ public abstract class ThemeManager {
// default behavior is only install to INSTANCE if first time
INSTANCE = this;
}
themeDefaultsProvider = getThemeDefaultsProvider();
applicationDefaults = getApplicationDefaults();
}
protected ThemeDefaultsProvider getThemeDefaultsProvider() {
return new ApplicationThemeDefaultsProvider();
protected ApplicationThemeDefaults getApplicationDefaults() {
return new PropertyFileThemeDefaults();
}
protected void installInGui() {
@ -101,12 +99,11 @@ public abstract class ThemeManager {
GThemeValueMap map = new GThemeValueMap();
map.load(javaDefaults);
map.load(systemValues);
map.load(themeDefaultsProvider.getDefaults());
map.load(applicationDefaults.getLightValues());
if (activeTheme.useDarkDefaults()) {
map.load(themeDefaultsProvider.getDarkDefaults());
map.load(applicationDefaults.getDarkValues());
}
map.load(themeDefaultsProvider.getLookAndFeelDefaults(getLookAndFeelType()));
map.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
map.load(activeTheme);
currentValues = map;
}
@ -270,12 +267,11 @@ public abstract class ThemeManager {
public GThemeValueMap getThemeValues() {
GThemeValueMap map = new GThemeValueMap();
map.load(javaDefaults);
map.load(systemValues);
map.load(themeDefaultsProvider.getDefaults());
map.load(applicationDefaults.getLightValues());
if (activeTheme.useDarkDefaults()) {
map.load(themeDefaultsProvider.getDarkDefaults());
map.load(applicationDefaults.getDarkValues());
}
map.load(themeDefaultsProvider.getLookAndFeelDefaults(getLookAndFeelType()));
map.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
map.load(activeTheme);
return map;
}
@ -418,9 +414,9 @@ public abstract class ThemeManager {
* theme.properties files
*/
public GThemeValueMap getApplicationDarkDefaults() {
GThemeValueMap map = new GThemeValueMap(themeDefaultsProvider.getDefaults());
map.load(themeDefaultsProvider.getDarkDefaults());
map.load(themeDefaultsProvider.getLookAndFeelDefaults(getLookAndFeelType()));
GThemeValueMap map = new GThemeValueMap(applicationDefaults.getLightValues());
map.load(applicationDefaults.getDarkValues());
map.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
return map;
}
@ -431,10 +427,25 @@ public abstract class ThemeManager {
* theme.properties files
*/
public GThemeValueMap getApplicationLightDefaults() {
GThemeValueMap map = new GThemeValueMap(themeDefaultsProvider.getDefaults());
GThemeValueMap map = new GThemeValueMap(applicationDefaults.getLightValues());
map.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
return map;
}
/**
* Returns application defaults values (does not include java default values)
* @return application defaults values (does not include java default values)
*/
public GThemeValueMap getApplicationOverrides() {
GThemeValueMap currentDefaults = new GThemeValueMap();
currentDefaults.load(applicationDefaults.getLightValues());
if (activeTheme.useDarkDefaults()) {
currentDefaults.load(applicationDefaults.getDarkValues());
}
currentDefaults.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
return currentDefaults;
}
/**
* Returns a {@link GThemeValueMap} containing all default values for the current theme. It
* is a combination of application defined defaults and java {@link LookAndFeel} defaults.
@ -442,12 +453,11 @@ public abstract class ThemeManager {
*/
public GThemeValueMap getDefaults() {
GThemeValueMap currentDefaults = new GThemeValueMap(javaDefaults);
currentDefaults.load(systemValues);
currentDefaults.load(themeDefaultsProvider.getDefaults());
currentDefaults.load(applicationDefaults.getLightValues());
if (activeTheme.useDarkDefaults()) {
currentDefaults.load(themeDefaultsProvider.getDarkDefaults());
currentDefaults.load(applicationDefaults.getDarkValues());
}
currentDefaults.load(themeDefaultsProvider.getLookAndFeelDefaults(getLookAndFeelType()));
currentDefaults.load(applicationDefaults.getLookAndFeelValues(getLookAndFeelType()));
return currentDefaults;
}

View file

@ -0,0 +1,85 @@
/* ###
* 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.util.Map;
import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import generic.theme.ApplicationThemeManager;
import generic.theme.GThemeValueMap;
import generic.theme.laf.nimbus.SelectedTreePainter;
/**
* Extends the {@link NimbusLookAndFeel} (Nimbus) to intercept {@link #getDefaults()}. Nimbus
* does not honor changes to the UIDefaults after it is installed as the active
* {@link LookAndFeel}, so we have to make the changes at the time the UIDefaults are installed.
* <P>
* To get around this issue, we extend Nimbus so that we can install our GColors and
* overridden properties as Nimbus is being installed, specifically during the call to the
* getDefaults() method. For all other Look And Feels, the GColors and overridden properties are
* changed in the UIDefaults after the Look And Feel is installed, so they don't need to extend the
* Look and Feel class.
* <P>
* Also, unlike other LaFs, Nimbus needs to be reinstalled every time we need to make a change to
* any of the UIDefaults values, since it does not respond to changes other than when first
* installed.
*/
public class CustomNimbusLookAndFeel extends NimbusLookAndFeel {
private ApplicationThemeManager themeManager;
private Map<String, String> normalizedIdToLafIdMap;
CustomNimbusLookAndFeel(ApplicationThemeManager themeManager) {
this.themeManager = themeManager;
}
@Override
public UIDefaults getDefaults() {
UIDefaults defaults = super.getDefaults();
installCustomPainters(defaults);
// normally all of this wiring is handled by the LookAndFeelManager (see above)
UiDefaultsMapper uiDefaultsMapper = new NimbusUiDefaultsMapper(defaults);
installJavaDefaultsIntoThemeManager(uiDefaultsMapper);
uiDefaultsMapper.installValuesIntoUIDefaults(getApplicationOverrides());
normalizedIdToLafIdMap = uiDefaultsMapper.getNormalizedIdToLafIdMap();
return defaults;
}
protected void installJavaDefaultsIntoThemeManager(UiDefaultsMapper uiDefaultsMapper) {
GThemeValueMap javaDefaults = uiDefaultsMapper.getJavaDefaults();
themeManager.setJavaDefaults(javaDefaults);
}
private void installCustomPainters(UIDefaults defaults) {
defaults.put("Tree:TreeCell[Enabled+Selected].backgroundPainter",
new SelectedTreePainter());
defaults.put("Tree:TreeCell[Focused+Selected].backgroundPainter",
new SelectedTreePainter());
}
public Map<String, String> getNormalizedIdToLafIdMap() {
return normalizedIdToLafIdMap;
}
protected GThemeValueMap getApplicationOverrides() {
return themeManager.getApplicationOverrides();
}
}

View file

@ -0,0 +1,68 @@
/* ###
* 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 static generic.theme.SystemThemeIds.*;
import java.awt.Color;
import javax.swing.UIDefaults;
import ghidra.util.WebColors;
public class FlatDarkUiDefaultsMapper extends FlatUiDefaultsMapper {
protected FlatDarkUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
@Override
protected void assignSystemColorValues() {
super.assignSystemColorValues();
// We don't think the FlatDark LaF's view background (Trees, Tables, Lists) is dark
// enough, so we are overriding the view group background and foreground colors
assignSystemColorDirect(BG_VIEW_ID, new Color(0x1c1d1e));
assignSystemColorDirect(FG_VIEW_ID, WebColors.LIGHT_GRAY);
}
@Override
protected void assignNormalizedColorValues() {
super.assignNormalizedColorValues();
//
// These components are initialized to "text", but we want them mapped to use
// our view background color so that they look like normal editable widgets
//
overrideColor("ComboBox.background", BG_VIEW_ID);
overrideColor("ComboBox.background", BG_VIEW_ID);
overrideColor("EditorPane.background", BG_VIEW_ID);
overrideColor("FormattedTextField.background", BG_VIEW_ID);
overrideColor("List.background", BG_VIEW_ID);
overrideColor("PasswordField.background", BG_VIEW_ID);
overrideColor("Table.background", BG_VIEW_ID);
overrideColor("Table.focusCellBackground", BG_VIEW_ID);
overrideColor("TableHeader.focusCellBackground", BG_VIEW_ID);
overrideColor("TextField.background", BG_VIEW_ID);
overrideColor("Tree.background", BG_VIEW_ID);
overrideColor("Tree.textBackground", BG_VIEW_ID);
overrideColor("TextArea.background", BG_VIEW_ID);
overrideColor("TextArea.foreground", BG_VIEW_ID);
overrideColor("TextPane.background", BG_VIEW_ID);
overrideColor("TextPane.foreground", BG_VIEW_ID);
}
}

View file

@ -15,18 +15,16 @@
*/
package generic.theme.laf;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import generic.theme.*;
import generic.theme.ApplicationThemeManager;
import generic.theme.LafType;
public class FlatLookAndFeelManager extends LookAndFeelManager {
public FlatLookAndFeelManager(LafType laf, ApplicationThemeManager themeManager) {
super(laf, themeManager);
// establish system color to LookAndFeel colors
systemToLafMap.addColor(new ColorValue(SYSTEM_WIDGET_BACKGROUND_COLOR_ID, "text"));
systemToLafMap.addColor(new ColorValue(SYSTEM_TOOLTIP_BACKGROUND_COLOR_ID, "info"));
}
@Override
@ -39,7 +37,10 @@ public class FlatLookAndFeelManager extends LookAndFeelManager {
}
@Override
protected ThemeGrouper getThemeGrouper() {
return new FlatThemeGrouper();
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
if (getLookAndFeelType() == LafType.FLAT_DARK) {
return new FlatDarkUiDefaultsMapper(defaults);
}
return new FlatUiDefaultsMapper(defaults);
}
}

View file

@ -1,62 +0,0 @@
/* ###
* 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 generic.theme.GThemeValueMap;
/**
* Adds specialized groupings unique to the Flat LookAndFeels
*/
public class FlatThemeGrouper extends ThemeGrouper {
@Override
public void group(GThemeValueMap values) {
// @formatter:off
defineCustomColorGroup("color.flat.menu.hover.bg", "MenuBar.hoverBackground", values);
defineCustomColorGroup("color.flat.button.hover.bg", "Button.hoverBackground", values);
defineCustomColorGroup("color.flat.button.selected.bg", "Button.selectedBackground",values);
defineCustomColorGroup("color.flat.button.toolbar.hover.bg", "Button.toolbar.hoverBackground",values);
defineCustomColorGroup("color.flat.button.toolbar.pressed.bg", "Button.toolbar.pressedBackground",values);
defineCustomColorGroup("color.flat.checkbox.icon.focus.border", "CheckBox.icon.focusedBorderColor",values);
defineCustomColorGroup("color.flat.menu.accelerator.fg", "Menu.acceleratorForeground",values);
defineCustomColorGroup("color.flat.focus.border", "Button.focusedBorderColor", values);
defineCustomColorGroup("color.flat.focus", "Component.focusColor", values);
defineCustomColorGroup("color.flat.focus.bg", "Button.focusedBackground", values);
defineCustomColorGroup("color.flat.checkmark", "CheckBox.icon.checkmarkColor", values);
defineCustomColorGroup("color.flat.disabled", "Button.disabledBorderColor", values);
defineCustomColorGroup("color.flat.disabled.selected", "Button.disabledSelectedBackground",values);
defineCustomColorGroup("color.flat.arrow", "Spinner.buttonArrowColor",values);
defineCustomColorGroup("color.flat.arrow.disabled", "Spinner.buttonDisabledArrowColor",values);
defineCustomColorGroup("color.flat.arrow.hover", "Spinner.buttonHoverArrowColor",values);
defineCustomColorGroup("color.flat.arrow.pressed", "Spinner.buttonPressedArrowColor",values);
defineCustomColorGroup("color.flat.dropcell.bg", "List.dropCellBackground",values);
defineCustomColorGroup("color.flat.dropline", "List.dropLineColor",values);
defineCustomColorGroup("color.flat.underline", "MenuItem.underlineSelectionColor",values);
defineCustomColorGroup("color.flat.docking.bg", "ToolBar.dockingBackground",values);
defineCustomColorGroup("color.flat.progressbar.bg", "ProgressBar.background",values);
defineCustomColorGroup("color.flat.progressbar.fg", "ProgressBar.foreground",values);
defineCustomColorGroup("color.flat.icon.bg", "Tree.icon.openColor",values);
defineCustomColorGroup("color.flat.selection.inactive", "Tree.selectionInactiveBackground",values);
// @formatter:on
super.group(values);
}
}

View file

@ -0,0 +1,65 @@
/* ###
* 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 javax.swing.UIDefaults;
public class FlatUiDefaultsMapper extends UiDefaultsMapper {
protected FlatUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
@Override
protected void registerIgnoredLafIds() {
super.registerIgnoredLafIds();
ignoredLafIds.add("Actions.Blue");
ignoredLafIds.add("Actions.Green");
ignoredLafIds.add("Actions.Grey");
ignoredLafIds.add("Actions.Greyinline");
ignoredLafIds.add("Actions.Red");
ignoredLafIds.add("Actions.Yellow");
ignoredLafIds.add("Objects.BlackText");
ignoredLafIds.add("Objects.Blue");
ignoredLafIds.add("Objects.Green");
ignoredLafIds.add("Objects.GreenAndroid");
ignoredLafIds.add("Objects.Grey");
ignoredLafIds.add("Objects.Pink");
ignoredLafIds.add("Objects.Purple");
ignoredLafIds.add("Objects.Red");
ignoredLafIds.add("Objects.RedStatus");
ignoredLafIds.add("Objects.Yellow");
ignoredLafIds.add("Objects.YellowDark");
ignoredLafIds.add("h0.font");
ignoredLafIds.add("h00.font");
ignoredLafIds.add("h1.font");
ignoredLafIds.add("h1.regular.font");
ignoredLafIds.add("h2.font");
ignoredLafIds.add("h2.regular.font");
ignoredLafIds.add("h3.font");
ignoredLafIds.add("h3.regular.font");
ignoredLafIds.add("h4.font");
ignoredLafIds.add("large.font");
ignoredLafIds.add("light.font");
ignoredLafIds.add("medium.font");
ignoredLafIds.add("mini.font");
ignoredLafIds.add("monospaced.font");
ignoredLafIds.add("small.font");
}
}

View file

@ -1,120 +0,0 @@
/* ###
* 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.*;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import generic.theme.*;
import generic.theme.laf.nimbus.SelectedTreePainter;
/**
* Extends the {@link NimbusLookAndFeel} to intercept the {@link #getDefaults()}. Nimbus does
* not honor changes to the UIDefaults after it is installed as the active
* {@link LookAndFeel}, so we have to make the changes at the time the UIDefaults are installed.
*
* To get around this issue, we extend the NimbusLookAndFeel
* so that we can install our GColors and overridden properties as Nimbus is being installed,
* specifically during the call to the getDefaults() method. For all other Look And Feels, the
* GColors and overridden properties are changed in the UIDefaults after the Look And Feel is
* installed, so they don't need to extends the Look and Feel class.
*
* Also, note that Nimbus needs to be reinstalled every time we need to make a change to any of the
* UIDefaults values, since it does not respond to changes other than when first installed.
*/
public class GNimbusLookAndFeel extends NimbusLookAndFeel {
private ApplicationThemeManager themeManager;
GNimbusLookAndFeel(ApplicationThemeManager themeManager) {
this.themeManager = themeManager;
}
@Override
public UIDefaults getDefaults() {
UIDefaults defaults = super.getDefaults();
installCustomPainters(defaults);
GThemeValueMap javaDefaults = extractJavaDefaults(defaults);
// replace all colors with GColors
for (ColorValue colorValue : javaDefaults.getColors()) {
String id = colorValue.getId();
defaults.put(id, themeManager.getGColorUiResource(id));
}
// 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 = themeManager.getFont(id);
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();
// because some icons are weird, put raw icons into defaults, only use GIcons for
// setting Icons explicitly on components
Icon icon = themeManager.getIcon(id);
defaults.put(id, icon);
}
defaults.put("Label.textForeground", themeManager.getGColorUiResource("Label.foreground"));
themeManager.refreshGThemeValues();
return defaults;
}
private void installCustomPainters(UIDefaults defaults) {
defaults.put("Tree:TreeCell[Enabled+Selected].backgroundPainter",
new SelectedTreePainter());
defaults.put("Tree:TreeCell[Focused+Selected].backgroundPainter",
new SelectedTreePainter());
}
protected GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
GThemeValueMap javaDefaults = new GThemeValueMap();
List<String> colorIds =
LookAndFeelManager.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 =
LookAndFeelManager.getLookAndFeelIdsForType(defaults, Font.class);
for (String id : fontIds) {
Font font = defaults.getFont(id);
FontValue value = new FontValue(id, LookAndFeelManager.fromUiResource(font));
javaDefaults.addFont(value);
}
List<String> iconIds =
LookAndFeelManager.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.
themeManager.setJavaDefaults(javaDefaults);
return javaDefaults;
}
}

View file

@ -15,6 +15,8 @@
*/
package generic.theme.laf;
import javax.swing.UIDefaults;
import generic.theme.ApplicationThemeManager;
import generic.theme.LafType;
@ -26,4 +28,9 @@ public class GtkLookAndFeelManager extends LookAndFeelManager {
public GtkLookAndFeelManager(ApplicationThemeManager themeManager) {
super(LafType.GTK, themeManager);
}
@Override
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
return new GtkUiDefaultsMapper(defaults);
}
}

View file

@ -0,0 +1,41 @@
/* ###
* 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 static generic.theme.SystemThemeIds.*;
import javax.swing.UIDefaults;
public class GtkUiDefaultsMapper extends UiDefaultsMapper {
protected GtkUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
@Override
protected void registerIgnoredLafIds() {
super.registerIgnoredLafIds();
ignoredLafIds.add("textInactiveText");
}
@Override
protected void assignColorMatchersToComponentIds() {
super.assignColorMatchersToComponentIds();
assignSystemColorFromLafId(FG_VIEW_ID, "windowText");
assignSystemColorFromLafId(FG_DISABLED_ID, "Label.disabledForeground");
}
}

View file

@ -36,32 +36,14 @@ import ghidra.util.SystemUtilities;
*/
public abstract class LookAndFeelManager {
/**
* These are color ids (see {@link GColor} used to represent general concepts that
* application developers can use to get the color for that concept as defined by
* a specific {@link LookAndFeel}. This class will define some standard default
* mappings in the constructor, but it is expected that each specific LookAndFeelManager
* will override these mappings with values appropriate for that LookAndFeel.
*/
protected static final String SYSTEM_APP_BACKGROUND_COLOR_ID = "system.color.bg.application";
protected static final String SYSTEM_WIDGET_BACKGROUND_COLOR_ID = "system.color.bg.widget";
protected static final String SYSTEM_TOOLTIP_BACKGROUND_COLOR_ID = "system.color.bg.tooltip";
protected static final String SYSTEM_BORDER_COLOR_ID = "system.color.border";
private LafType laf;
private Map<String, ComponentFontRegistry> fontRegistryMap = new HashMap<>();
protected GThemeValueMap systemToLafMap = new GThemeValueMap();
protected ApplicationThemeManager themeManager;
protected Map<String, String> normalizedIdToLafIdMap;
protected LookAndFeelManager(LafType laf, ApplicationThemeManager themeManager) {
this.laf = laf;
this.themeManager = themeManager;
// establish system color to LookAndFeel colors
systemToLafMap.addColor(new ColorValue(SYSTEM_APP_BACKGROUND_COLOR_ID, "control"));
systemToLafMap.addColor(new ColorValue(SYSTEM_WIDGET_BACKGROUND_COLOR_ID, "control"));
systemToLafMap.addColor(new ColorValue(SYSTEM_TOOLTIP_BACKGROUND_COLOR_ID, "control"));
systemToLafMap.addColor(new ColorValue(SYSTEM_BORDER_COLOR_ID, "controlShadow"));
}
/**
@ -85,10 +67,8 @@ public abstract class LookAndFeelManager {
public void installLookAndFeel() throws ClassNotFoundException, InstantiationException,
IllegalAccessException, UnsupportedLookAndFeelException {
cleanUiDefaults();
themeManager.setSystemDefaults(systemToLafMap);
doInstallLookAndFeel();
installJavaDefaults();
processJavaDefaults();
fixupLookAndFeelIssues();
installGlobalProperties();
installCustomLookAndFeelActions();
@ -119,10 +99,14 @@ public abstract class LookAndFeelManager {
UIDefaults defaults = UIManager.getDefaults();
for (FontValue fontValue : fonts) {
String id = fontValue.getId();
String lafId = normalizedIdToLafIdMap.get(id);
if (lafId == null) {
continue;
}
Font correctFont = Gui.getFont(id);
Font storedFont = defaults.getFont(id);
if (correctFont != null && !correctFont.equals(storedFont)) {
defaults.put(id, correctFont);
defaults.put(lafId, toUiResource(correctFont));
}
}
}
@ -132,10 +116,11 @@ public abstract class LookAndFeelManager {
UIDefaults defaults = UIManager.getDefaults();
for (IconValue iconValue : icons) {
String id = iconValue.getId();
String lafId = normalizedIdToLafIdMap.get(id);
Icon correctIcon = Gui.getIcon(id);
Icon storedIcon = defaults.getIcon(id);
if (correctIcon != null && !correctIcon.equals(storedIcon)) {
defaults.put(id, correctIcon);
defaults.put(lafId, correctIcon);
}
}
}
@ -154,14 +139,16 @@ public abstract class LookAndFeelManager {
* @param newIcon the new icon to use for the given set of icon ids
*/
public void iconsChanged(Set<String> changedIconIds, Icon newIcon) {
UIDefaults defaults = UIManager.getDefaults();
if (!(newIcon instanceof UIResource)) {
newIcon = new IconUIResource(newIcon);
}
for (String changedIconId : changedIconIds) {
String lafIconId = normalizedIdToLafIdMap.get(changedIconId);
defaults.put(lafIconId, newIcon);
}
if (!changedIconIds.isEmpty()) {
UIDefaults defaults = UIManager.getDefaults();
for (String javaIconId : changedIconIds) {
defaults.put(javaIconId, newIcon);
}
updateComponentUis();
}
themeManager.refreshGThemeValues();
@ -173,16 +160,22 @@ public abstract class LookAndFeelManager {
* @param changedJavaFontIds the set of Java Font ids that are affected by this change
*/
public void fontsChanged(Set<String> changedJavaFontIds) {
if (!changedJavaFontIds.isEmpty()) {
UIDefaults defaults = UIManager.getDefaults();
for (String javaFontId : changedJavaFontIds) {
// even though all these derive from the new font, they might be different
// because of FontModifiers.
Font font = Gui.getFont(javaFontId);
defaults.put(javaFontId, new FontUIResource(font));
UIDefaults defaults = UIManager.getDefaults();
for (String changedFontId : changedJavaFontIds) {
// even though all these derive from the new font, they might be different
// because of FontModifiers.
Font font = Gui.getFont(changedFontId);
String lafFontId = normalizedIdToLafIdMap.get(changedFontId);
if (lafFontId != null) {
// lafFontId is null for group ids
defaults.put(lafFontId, new FontUIResource(font));
}
}
if (!changedJavaFontIds.isEmpty()) {
updateComponentUis();
}
updateAllRegisteredComponentFonts();
repaintAll();
}
@ -212,26 +205,9 @@ public abstract class LookAndFeelManager {
register.addComponent(component);
}
/**
* Returns a color that is not a {@link UIResource}.
* @param color the color to return an non UIResource color for
* @return a color that is not a {@link UIResource}.
*/
public static Color fromUiResource(Color color) {
if (color.getClass() == Color.class) {
return color;
}
return new Color(color.getRGB(), true);
}
/**
* Returns a font that is not a {@link UIResource}.
* @param font the font to return an non UIResource font for
* @return a font that is not a {@link UIResource}.
*/
public static Font fromUiResource(Font font) {
if (font instanceof UIResource) {
return new FontNonUiResource(font);
private Font toUiResource(Font font) {
if (!(font instanceof UIResource)) {
return new FontUIResource(font);
}
return font;
}
@ -261,80 +237,21 @@ public abstract class LookAndFeelManager {
}
/**
* Extracts java default colors, fonts, and icons and stores them in {@link Gui}.
* Extracts java default colors, fonts, and icons and stores them in the
* {@link ThemeManager} and updates the {@link UIDefaults} by installing GColors for all
* color values and installing any overridden fonts or icons.
*/
private void installJavaDefaults() {
GThemeValueMap javaDefaults = extractJavaDefaults();
ThemeGrouper grouper = getThemeGrouper();
grouper.group(javaDefaults);
protected void processJavaDefaults() {
UIDefaults defaults = UIManager.getDefaults();
UiDefaultsMapper uiDefaultsMapper = getUiDefaultsMapper(defaults);
GThemeValueMap javaDefaults = uiDefaultsMapper.getJavaDefaults();
themeManager.setJavaDefaults(javaDefaults);
installPropertiesBackIntoUiDefaults(javaDefaults);
uiDefaultsMapper.installValuesIntoUIDefaults(themeManager.getApplicationOverrides());
normalizedIdToLafIdMap = uiDefaultsMapper.getNormalizedIdToLafIdMap();
}
protected ThemeGrouper getThemeGrouper() {
return new ThemeGrouper();
}
protected void installPropertiesBackIntoUiDefaults(GThemeValueMap javaDefaults) {
UIDefaults defaults = UIManager.getDefaults();
GTheme theme = themeManager.getActiveTheme();
// we replace java default colors with GColor equivalents so that we
// can change colors without having to reinstall ui on each component
// This trick only works for colors. Fonts and icons don't universally
// allow being wrapped like colors do.
for (ColorValue colorValue : javaDefaults.getColors()) {
String id = colorValue.getId();
defaults.put(id, themeManager.getGColorUiResource(id));
}
// 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, 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.getIcon(id);
defaults.put(id, icon);
}
}
}
protected GThemeValueMap extractJavaDefaults() {
UIDefaults defaults = UIManager.getDefaults();
GThemeValueMap values = new GThemeValueMap();
// for now, just doing color properties.
List<String> ids = getLookAndFeelIdsForType(defaults, Color.class);
for (String id : ids) {
// 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) {
// 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) {
Icon icon = UIManager.getIcon(id);
values.addIcon(new IconValue(id, icon));
}
return values;
}
protected abstract UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults);
protected String findLookAndFeelClassName(String lookAndFeelName) {
LookAndFeelInfo[] installedLookAndFeels = UIManager.getInstalledLookAndFeels();
@ -463,26 +380,6 @@ public abstract class LookAndFeelManager {
installPopupMenuSettingsOverride();
}
private void cleanUiDefaults() {
GThemeValueMap javaDefaults = themeManager.getJavaDefaults();
if (javaDefaults == null) {
return;
}
UIDefaults defaults = UIManager.getDefaults();
for (ColorValue colorValue : javaDefaults.getColors()) {
String id = colorValue.getId();
defaults.put(id, null);
}
for (FontValue fontValue : javaDefaults.getFonts()) {
String id = fontValue.getId();
defaults.put(id, null);
}
for (IconValue iconValue : javaDefaults.getIcons()) {
String id = iconValue.getId();
defaults.put(id, null);
}
}
/**
* Searches the given UIDefaults for ids whose value matches the given class
* @param defaults the UIDefaults to search

View file

@ -15,6 +15,8 @@
*/
package generic.theme.laf;
import javax.swing.UIDefaults;
import generic.theme.ApplicationThemeManager;
import generic.theme.LafType;
@ -25,7 +27,7 @@ public class MacLookAndFeelManager extends LookAndFeelManager {
}
@Override
protected ThemeGrouper getThemeGrouper() {
return new MacThemeGrouper();
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
return new MacUiDefaultsMapper(defaults);
}
}

View file

@ -1,38 +0,0 @@
/* ###
* 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 generic.theme.GThemeValueMap;
/**
* Adds specialized groupings unique to the Mac LookAndFeel
*/
public class MacThemeGrouper extends ThemeGrouper {
@Override
public void group(GThemeValueMap values) {
// @formatter:off
defineCustomColorGroup("color.mac.disabled.fg", "Menu.disabledForeground", values);
defineCustomColorGroup("color.mac.button.select", "Button.select", values);
defineCustomColorGroup("color.mac.menu.select", "Menu.selectionBackground",values);
defineCustomColorGroup("color.mac.seletion.inactive.bg", "List.selectionInactiveBackground",values);//d4d4d4
defineCustomFontGroup("font.mac.small.font", "IconButton.font", values);
// @formatter:on
super.group(values);
}
}

View file

@ -0,0 +1,25 @@
/* ###
* 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 javax.swing.UIDefaults;
public class MacUiDefaultsMapper extends UiDefaultsMapper {
protected MacUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
}

View file

@ -15,6 +15,8 @@
*/
package generic.theme.laf;
import javax.swing.UIDefaults;
import generic.theme.ApplicationThemeManager;
import generic.theme.LafType;
@ -23,4 +25,9 @@ public class MetalLookAndFeelManager extends LookAndFeelManager {
public MetalLookAndFeelManager(ApplicationThemeManager themeManager) {
super(LafType.METAL, themeManager);
}
@Override
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
return new MetalUiDefaultsMapper(defaults);
}
}

View file

@ -0,0 +1,41 @@
/* ###
* 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 static generic.theme.SystemThemeIds.*;
import javax.swing.UIDefaults;
public class MetalUiDefaultsMapper extends UiDefaultsMapper {
protected MetalUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
@Override
protected void registerIgnoredLafIds() {
super.registerIgnoredLafIds();
ignoredLafIds.add("textInactiveText");
}
@Override
protected void assignSystemColorValues() {
super.assignSystemColorValues();
assignSystemColorFromLafId(FG_DISABLED_ID, "Label.disabledForeground");
assignSystemColorFromLafId(FG_VIEW_ID, "windowText");
}
}

View file

@ -15,7 +15,10 @@
*/
package generic.theme.laf;
import generic.theme.*;
import javax.swing.UIDefaults;
import generic.theme.ApplicationThemeManager;
import generic.theme.LafType;
/**
* Motif {@link LookAndFeelManager}. Specialized so that it can return the Motif installer
@ -24,11 +27,11 @@ public class MotifLookAndFeelManager extends LookAndFeelManager {
public MotifLookAndFeelManager(ApplicationThemeManager themeManager) {
super(LafType.MOTIF, themeManager);
// establish system color to LookAndFeel colors
systemToLafMap.addColor(new ColorValue(SYSTEM_APP_BACKGROUND_COLOR_ID, "control"));
systemToLafMap.addColor(new ColorValue(SYSTEM_WIDGET_BACKGROUND_COLOR_ID, "window"));
systemToLafMap.addColor(new ColorValue(SYSTEM_TOOLTIP_BACKGROUND_COLOR_ID, "info"));
systemToLafMap.addColor(new ColorValue(SYSTEM_BORDER_COLOR_ID, "activeCaptionBorder"));
}
@Override
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
return new MotifUiDefaultsMapper(defaults);
}
@Override
@ -46,10 +49,4 @@ public class MotifLookAndFeelManager extends LookAndFeelManager {
setKeyBinding("PASTE", "ctrl V", UIPrefixValues);
setKeyBinding("CUT", "ctrl X", UIPrefixValues);
}
@Override
protected ThemeGrouper getThemeGrouper() {
return new MotifThemeGrouper();
}
}

View file

@ -15,21 +15,18 @@
*/
package generic.theme.laf;
import generic.theme.GThemeValueMap;
import javax.swing.UIDefaults;
/**
* Adds specialized groupings unique to the Motif LookAndFeel
*/
public class MotifThemeGrouper extends ThemeGrouper {
public MotifThemeGrouper() {
public class MotifUiDefaultsMapper extends UiDefaultsMapper {
protected MotifUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
@Override
public void group(GThemeValueMap values) {
defineCustomFontGroup("font.monospaced", "Spinner.font", values);
super.group(values);
protected void registerIgnoredLafIds() {
super.registerIgnoredLafIds();
ignoredLafIds.add("controlLightShadow");
}
}

View file

@ -32,9 +32,6 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
public NimbusLookAndFeelManager(ApplicationThemeManager themeManager) {
super(LafType.NIMBUS, themeManager);
// establish system color specific to Nimbus
systemToLafMap.addColor(new ColorValue(SYSTEM_BORDER_COLOR_ID, "nimbusBorder"));
}
@Override
@ -63,11 +60,30 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
private void reinstallNimubus() {
try {
UIManager.setLookAndFeel(new GNimbusLookAndFeel(themeManager) {
/**
* In order to get Nimbus to honor changes to fonts and icons in the UiDefaults,
* we have to reinstall nimbus. Reinstalling nimbus is a bit different that the first
* install. First, we don't want to re-install the java defaults, the current ones are
* fine and we don't want loose any current theme values changes. Second, when we
* get font and theme value overrides, we want to use all the current values as they
* may include additional overrides than just the original values from theme.property
* files.
*/
UIManager.setLookAndFeel(new CustomNimbusLookAndFeel(themeManager) {
@Override
protected GThemeValueMap extractJavaDefaults(UIDefaults defaults) {
return themeManager.getJavaDefaults();
protected void installJavaDefaultsIntoThemeManager(
UiDefaultsMapper uiDefaultsMapper) {
// as explained above, don't change the java defaults in the theme manager
// on a reinstall
}
@Override
protected GThemeValueMap getApplicationOverrides() {
// on a reinstall, we may also have overrides in the current values and not
// just the theme.property files
return themeManager.getCurrentValues();
}
});
}
catch (UnsupportedLookAndFeelException e) {
@ -78,18 +94,15 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
@Override
protected void doInstallLookAndFeel() throws UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(new GNimbusLookAndFeel(themeManager));
CustomNimbusLookAndFeel nimbusLookAndFeel = new CustomNimbusLookAndFeel(themeManager);
UIManager.setLookAndFeel(nimbusLookAndFeel);
normalizedIdToLafIdMap = nimbusLookAndFeel.getNormalizedIdToLafIdMap();
}
@Override
protected GThemeValueMap extractJavaDefaults() {
protected void processJavaDefaults() {
// The GNimbusLookAndFeel already extracted the java defaults and installed them in the Gui
return themeManager.getJavaDefaults();
}
@Override
protected ThemeGrouper getThemeGrouper() {
return new NimbusThemeGrouper();
}
@Override
@ -105,8 +118,7 @@ public class NimbusLookAndFeelManager extends LookAndFeelManager {
}
@Override
protected void installPropertiesBackIntoUiDefaults(GThemeValueMap javaDefaults) {
// do nothing, this was handled when we overrode the getDefaults() method in the
// GNimubusLookAndFeel
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
return new NimbusUiDefaultsMapper(defaults);
}
}

View file

@ -1,43 +0,0 @@
/* ###
* 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 generic.theme.GThemeValueMap;
/**
* Adds specialized groupings unique to the Nimbus LookAndFeel
*/
public class NimbusThemeGrouper extends ThemeGrouper {
public NimbusThemeGrouper() {
// Nimbus defines a new type of button
buttonGroup.addComponents("ArrowButton");
// Nimbus defines some other color sources
colorSourceProperties.add("nimbusFocus");
colorSourceProperties.add("nimbusOrange");
colorSourceProperties.add("nimbusBorder");
}
@Override
public void group(GThemeValueMap values) {
defineCustomColorGroup("color.nimbus.text.alt", "Menu.foreground", values);
defineCustomFontGroup("font.titledborder", "TitledBorder.font", values);
super.group(values);
}
}

View file

@ -0,0 +1,96 @@
/* ###
* 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 static generic.theme.SystemThemeIds.*;
import javax.swing.UIDefaults;
import generic.theme.*;
public class NimbusUiDefaultsMapper extends UiDefaultsMapper {
protected NimbusUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
@Override
protected void registerIgnoredLafIds() {
super.registerIgnoredLafIds();
ignoredLafIds.add("background");
ignoredLafIds.add("controlLHighlight");
ignoredLafIds.add("nimbusAlertYellow");
ignoredLafIds.add("nimbusBase");
ignoredLafIds.add("nimbusBlueGrey");
ignoredLafIds.add("nimbusDisabledText");
ignoredLafIds.add("nimbusFocus");
ignoredLafIds.add("nimbusGreen");
ignoredLafIds.add("nimbusInfoBlue");
ignoredLafIds.add("nimbusOrange");
ignoredLafIds.add("nimbusRed");
ignoredLafIds.add("nimbusSelectedText");
ignoredLafIds.add("nimbusSelection");
ignoredLafIds.add("nimbusSelectionBackground");
ignoredLafIds.add("defaultFont");
}
@Override
protected void assignSystemColorValues() {
// different from base class
assignSystemColorFromLafId(BG_CONTROL_ID, "Button.background");
assignSystemColorFromLafId(FG_CONTROL_ID, "Button.foreground");
assignSystemColorFromLafId(BG_BORDER_ID, "nimbusBorder");
assignSystemColorFromLafId(BG_VIEW_ID, "nimbusLightBackground");
assignSystemColorFromLafId(FG_VIEW_ID, "controlText");
// the following are the same as the base class (we can't just call super because
// it will report errors for missing lafIds such as "window"
assignSystemColorFromLafId(BG_VIEW_SELECTED_ID, "textHighlight");
assignSystemColorFromLafId(FG_VIEW_SELECTED_ID, "textHighlightText");
assignSystemColorFromLafId(FG_DISABLED_ID, "textInactiveText");
assignSystemColorFromLafId(BG_TOOLTIP_ID, "info");
assignSystemColorFromLafId(FG_TOOLTIP_ID, "infoText");
}
@Override
protected GThemeValueMap extractColorFontAndIconValuesFromDefaults() {
// Nimbus always uses "info" to paint its tooltip and it appears they forgot to update
// that value to the value they assigned to the "ToolTip.background. So we fix it here
// before extracting the values from the UIDefaults
defaults.put("info", defaults.getColor("ToolTip.background"));
return super.extractColorFontAndIconValuesFromDefaults();
}
@Override
protected void installGColorsIntoUIDefaults() {
super.installGColorsIntoUIDefaults();
// The Nimbus selected text field color is not honored if the value is a ColorUIResource.
// We install GColorUIResources by default. Thus, our setting for this particular
// attribute was being ignored. We set it here to be a GColor, which causes Nimbus
// to honor the value. We may need to add more entries here as they are discovered.
defaults.put("TextField.selectionForeground",
new GColor(SystemThemeIds.FG_VIEW_SELECTED_ID));
}
}

View file

@ -1,452 +0,0 @@
/* ###
* 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.*;
import javax.swing.LookAndFeel;
import javax.swing.plaf.basic.BasicLookAndFeel;
import generic.theme.*;
/**
* Organizes UIDefaults color and font properties into groups so that every property doesn't
* have its own direct value. The idea is that users can affect many properties that have the
* same value by just changing the value for the group. For colors, the {@link LookAndFeel}s
* organize the properties internally and this class attempts to restore that organization
* as much as possible by using the values defined in the {@link BasicLookAndFeel} such as
* "control", "window", "controlShadlow", etc. The fonts don't appear to have any such internal
* organization, so we created our own groups and used a lookAndFeel property to initialize each
* group source value. Then whenever the font matched a group source value, the font is replace
* with an indirect reference to the group source font value.
* <p>
* This class is sometimes sub-classed for a particular {@link LookAndFeel}. The subclass can
* create new groups and mappings that are unique to that LookAndFeel.
*
* Often, many of the various group source ids have the same color value. To try to group
* properties as defined in BasicLookAndFeel, the preferred source ids are
* defined for each group. These will be tried first, but if a match isn't found among the
* preferred sources, then all the sources will be searched for a match
*/
public class ThemeGrouper {
private static String DEFAULT_FONT_GROUP_ID = "font.default";
private static String BUTTON_FONT_GROUP_ID = "font.button";
private static String TEXT_FONT_GROUP_ID = "font.text";
private static String WIDGET_FONT_GROUP_ID = "font.widget";
private static String COMPONENT_FONT_GROUP_ID = "font.component";
private static String MENU_FONT_GROUP_ID = "font.menu";
private static String MENU_ACCELERATOR_FONT_GROUP_ID = "font.menu.accelerator";
static List<String> DEFAULT_FONT_SOURCE_PROPERTIES = List.of(
DEFAULT_FONT_GROUP_ID,
COMPONENT_FONT_GROUP_ID,
WIDGET_FONT_GROUP_ID,
TEXT_FONT_GROUP_ID,
BUTTON_FONT_GROUP_ID,
MENU_FONT_GROUP_ID,
MENU_ACCELERATOR_FONT_GROUP_ID);
// The list of color properties (defined in BasicLookAndFeel) that are used to populate
// other component specific colors.
// The order is important. If any have the same color value, the one higher in the list is used.
// Individual groups (buttons, menus, etc.) may define a different order that is more specific
// to that group
public static List<String> DEFAULT_COLOR_SOURCE_PROPERTIES = List.of(
"control",
"window",
"activeCaption",
"activeCaptionBorder",
"activeCaptionText",
"controlDkShadow",
"controlHighlight",
"controlLtHighlight",
"controlShadow",
"controlText",
"desktp",
"inactiveCaption",
"inactiveCaptionBorder",
"inactiveCaptionText",
"info",
"infoText",
"menu",
"menuText",
"scrollbar",
"scrollBarTrack",
"text",
"textHighlight",
"textHighlightText",
"textInactiveText",
"textText",
"windowBorder",
"windowText");
private static final String[] BUTTON_GROUP = {
"Button",
"ToggleButton",
"RadioButton",
"CheckBox"
};
private static final String[] MENU_GROUP = {
"Menu",
"MenuBar",
"MenuItem",
"PopupMenu",
"RadioButtonMenuItem",
"CheckBoxMenuItem"
};
private static final String[] TEXT_GROUP = {
"TextField",
"FormattedTextField",
"PasswordField",
"TextArea",
"TextPane",
"EditorPane"
};
private static final String[] WIDGET_GROUP = {
"FileChooser",
"ColorChooser",
"ComboBox",
"List",
"Table",
"Tree"
};
private static final String[] COMPONENT_GROUP = {
"Desktop",
"Panel",
"InternalFrame",
"Label",
"OptionPane",
"ProgressBar",
"Separator",
"ScrollBar",
"ScrollPane",
"Viewport",
"Slider",
"Spinner",
"SplitPane",
"TabbedPane",
"TableHeader",
"TitledBorder",
"ToolBar",
"ToolTip"
};
private static final String[] BUTTON_PREFERRED_SOURCES = {
"control",
"controlText",
"controlShadow",
"controlDkShadow",
"controlHighlight",
"controlLtHighlight"
};
private static final String[] MENU_PREFERRED_SOURCES = {
"menu",
"menuText",
"textHighlightText",
"textHighlight",
"controlShadow",
"controlDkShadow",
"controlHighlight",
"controlLtHighlight"
};
private static final String[] TEXT_PREFERRED_SOURCES = {
"window",
"text",
"textText",
"textInactiveText",
"textHighlight",
"textHighlightText",
"controlShadow",
"controlDkShadow",
"controlHighlight",
"controlLtHighlight"
};
private static final String[] WIDGET_PREFERRED_SOURCES = {
"window",
"textText",
"textHighlight",
"textHighlightText",
"control",
"controlShadow",
"controlDkShadow",
"controlHighlight",
"controlLtHighlight"
};
private static final String[] COMPONENT_PREFERRED_SOURCES = {
"control",
"controlText",
"controlShadow",
"controlDkShadow",
"controlHighlight",
"controlLtHighlight",
"textText",
"textHighlight"
};
protected List<String> colorSourceProperties;
protected List<String> fontSourceProperties;
protected Set<PropertyGroup> groups;
protected PropertyGroup buttonGroup = new PropertyGroup(BUTTON_GROUP, BUTTON_PREFERRED_SOURCES);
protected PropertyGroup menuGroup = new PropertyGroup(MENU_GROUP, MENU_PREFERRED_SOURCES);
protected PropertyGroup widgetGroup = new PropertyGroup(WIDGET_GROUP, WIDGET_PREFERRED_SOURCES);
protected PropertyGroup textGroup = new PropertyGroup(TEXT_GROUP, TEXT_PREFERRED_SOURCES);
protected PropertyGroup componentGroup =
new PropertyGroup(COMPONENT_GROUP, COMPONENT_PREFERRED_SOURCES);
public ThemeGrouper() {
colorSourceProperties = new ArrayList<>(DEFAULT_COLOR_SOURCE_PROPERTIES);
fontSourceProperties = new ArrayList<>(DEFAULT_FONT_SOURCE_PROPERTIES);
groups = getPropertyGroups();
}
/**
* Replaces direct property values in the given GThemeValueMap with indirect references
* using the values from match source ids.
* @param values the values to search and replace source matches
*/
public void group(GThemeValueMap values) {
initialize(values);
Map<String, PropertyGroup> groupMap = buildGroupMap(values);
groupColors(values, groupMap);
groupFonts(values, groupMap);
}
/**
* Defines a new color id that will be used as the reference value for any specific color ids that
* have the same color value. This will allow all those specific colors to be changed at once.
* @param customGroupColorName name of a higher level group color id that will be used as the
* value for more specific color ids defined by the lookAndFeel.
* @param lookAndFeelSourceId the lookAndFeel color id whose value will be used as the value
* for the new custom group color id
* @param values the map where we store the default theme value mappings
*/
protected void defineCustomColorGroup(String customGroupColorName, String lookAndFeelSourceId,
GThemeValueMap values) {
colorSourceProperties.add(customGroupColorName);
ColorValue colorValue = values.getColor(lookAndFeelSourceId);
if (colorValue != null) {
Color color = colorValue.get(values);
values.addColor(new ColorValue(customGroupColorName, color));
}
}
/**
* Defines a new font id that will be used as the reference value for any specific font ids that
* have the same font value. This will allow all those specific fonts to be changed at once.
* @param customGroupFontName name of a higher level group font id that will be used as the
* value of more specific font ids defined by the lookAndFeel.
* @param lookAndFeelSourceId the lookAndFeel font id whose value will be used as the value
* for the new custom group font id
* @param values the map where we store the default theme value mappings
*/
protected void defineCustomFontGroup(String customGroupFontName, String lookAndFeelSourceId,
GThemeValueMap values) {
fontSourceProperties.add(customGroupFontName);
FontValue fontValue = values.getFont(lookAndFeelSourceId);
if (fontValue != null) {
Font font = fontValue.get(values);
values.addFont(new FontValue(customGroupFontName, font));
}
}
private void groupColors(GThemeValueMap values, Map<String, PropertyGroup> groupMap) {
Set<String> skip = new HashSet<>(colorSourceProperties); // we don't want to map sources
Map<Integer, String> defaultColorMapping = buildColorToSourceMap(values);
// try to map each color property to a source property (e.g., Button.background -> control)
for (ColorValue colorValue : values.getColors()) {
String id = colorValue.getId();
if (colorValue.isIndirect() || skip.contains(id)) {
continue;
}
PropertyGroup group = groupMap.get(getComponentName(id));
int rgb = colorValue.getRawValue().getRGB();
String sourceProperty = group == null ? null : group.getSourceProperty(rgb);
if (sourceProperty == null) {
sourceProperty = defaultColorMapping.get(rgb);
}
if (sourceProperty != null) {
values.addColor(new ColorValue(id, sourceProperty));
}
}
}
private void groupFonts(GThemeValueMap values, Map<String, PropertyGroup> groupMap) {
Set<String> skip = new HashSet<>(fontSourceProperties); // we don't want to map sources
Map<Font, String> defaultFontMapping = buildFontToSourceMap(values);
// try to map each color property to a source property (e.g., Button.background -> control)
for (FontValue fontValue : values.getFonts()) {
String id = fontValue.getId();
if (fontValue.isIndirect() || skip.contains(id)) {
continue;
}
Font font = fontValue.getRawValue();
PropertyGroup group = groupMap.get(getComponentName(id));
String sourceProperty = group == null ? null : group.getSourceProperty(font);
if (sourceProperty == null) {
sourceProperty = defaultFontMapping.get(font);
}
if (sourceProperty != null) {
values.addFont(new FontValue(id, sourceProperty));
}
}
}
private void initialize(GThemeValueMap values) {
// initialized default font to the Panel's font
FontValue defaultFontValue = values.getFont("Panel.font");
if (defaultFontValue != null) {
values.addFont(new FontValue(DEFAULT_FONT_GROUP_ID, defaultFontValue.get(values)));
}
// initialize the default group fonts to a font from an exemplar property in that group
initializeFontGroup(buttonGroup, BUTTON_FONT_GROUP_ID, "Button.font", values);
initializeFontGroup(textGroup, TEXT_FONT_GROUP_ID, "TextField.font", values);
initializeFontGroup(widgetGroup, WIDGET_FONT_GROUP_ID, "Table.font", values);
initializeFontGroup(componentGroup, COMPONENT_FONT_GROUP_ID, "Panel.font", values);
initializeFontGroup(menuGroup, MENU_FONT_GROUP_ID, "Menu.font", values);
initializeFontGroup(menuGroup, MENU_ACCELERATOR_FONT_GROUP_ID, "Menu.acceleratorFont",
values);
}
private void initializeFontGroup(PropertyGroup group, String fontGroupId, String exemplarId,
GThemeValueMap values) {
FontValue fontValue = values.getFont(exemplarId);
if (fontValue != null) {
Font font = fontValue.getRawValue();
values.addFont(new FontValue(fontGroupId, font));
group.addFontMapping(font, fontGroupId);
}
}
private Set<PropertyGroup> getPropertyGroups() {
Set<PropertyGroup> set = new HashSet<>();
set.add(buttonGroup);
set.add(menuGroup);
set.add(textGroup);
set.add(widgetGroup);
set.add(componentGroup);
return set;
}
private Map<String, PropertyGroup> buildGroupMap(GThemeValueMap values) {
Map<String, PropertyGroup> map = new HashMap<>();
for (PropertyGroup group : groups) {
group.initialize(values);
group.populateGroupMap(map);
}
return map;
}
private String getComponentName(String id) {
int dotIndex = id.indexOf(".");
if (dotIndex < 0) {
return id;
}
return id.substring(0, dotIndex);
}
private Map<Integer, String> buildColorToSourceMap(GThemeValueMap values) {
Map<Integer, String> colorMapping = new HashMap<>();
ArrayList<String> reversed = new ArrayList<>(colorSourceProperties);
Collections.reverse(reversed);
// go through in reverse order so that values at the top of the list have precedence
// if multiple propertyBases have the save value.
for (String propertyBase : reversed) {
ColorValue colorValue = values.getColor(propertyBase);
if (colorValue != null) {
Color color = colorValue.get(values);
colorMapping.put(color.getRGB(), propertyBase);
}
}
return colorMapping;
}
private Map<Font, String> buildFontToSourceMap(GThemeValueMap values) {
Map<Font, String> fontMapping = new HashMap<>();
ArrayList<String> reversed = new ArrayList<>(fontSourceProperties);
Collections.reverse(reversed);
// go through in reverse order so that values at the top of the list have precedence
// if multiple propertyBases have the save value.
for (String propertyBase : reversed) {
FontValue fontValue = values.getFont(propertyBase);
if (fontValue != null) {
Font font = fontValue.get(values);
fontMapping.put(font, propertyBase);
}
}
return fontMapping;
}
static class PropertyGroup {
private Set<String> groupComponents = new HashSet<>();
private List<String> preferredPropertyColorSources = new ArrayList<>();
private Map<Integer, String> colorMapping;
private Map<Font, String> fontMapping = new HashMap<>();
PropertyGroup(String[] components, String[] perferredSources) {
addComponents(components);
addPreferredColorSources(perferredSources);
}
String getSourceProperty(int rgb) {
return colorMapping.get(rgb);
}
String getSourceProperty(Font font) {
return fontMapping.get(font);
}
void populateGroupMap(Map<String, PropertyGroup> groupMap) {
for (String component : groupComponents) {
groupMap.put(component, this);
}
}
void addPreferredColorSources(String... preferedColorSources) {
this.preferredPropertyColorSources.addAll(Arrays.asList(preferedColorSources));
}
void addComponents(String... properties) {
groupComponents.addAll(Arrays.asList(properties));
}
void addFontMapping(Font font, String sourceId) {
fontMapping.put(font, sourceId);
}
private Map<Integer, String> initialize(GThemeValueMap values) {
colorMapping = new HashMap<>();
ArrayList<String> reversed = new ArrayList<>(preferredPropertyColorSources);
Collections.reverse(reversed);
// go through in reverse order so that values at the top of the list have precedence
// if multiple propertyBases have the save value.
for (String propertyBase : reversed) {
ColorValue colorValue = values.getColor(propertyBase);
if (colorValue != null) {
Color color = colorValue.get(values);
colorMapping.put(color.getRGB(), propertyBase);
}
}
return colorMapping;
}
}
}

View file

@ -0,0 +1,716 @@
/* ###
* 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 static generic.theme.SystemThemeIds.*;
import java.awt.Color;
import java.awt.Font;
import java.util.*;
import java.util.Map.Entry;
import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import org.apache.commons.collections4.IteratorUtils;
import generic.theme.*;
import ghidra.util.Msg;
/**
* The purpose of this class is to introduce multiple levels of indirection into the Java
* {@code LookAndFeel} (LaF), which allows the user to change these values. Further, when
* introducing this indirection we combine the Java settings into user-friendly system ids to make
* changing these values easier.
* <P>
* This class defines these user-friendly groups. The default system assignments are based on the
* {@link BasicLookAndFeel} values.
* <P>
* Subclasses can override the mapping of these standard system values for particular LaFs that have
* different keys or color assignments.
* <P>
* Some basic concepts:
* <UL>
* <LI>UI Defaults - key-value pairs defined by the Java LaF; there are 2 key types, widget
* keys and Java group/reusable keys (e.g., Button.background; control)
* <LI>UI Indirection - UI Defaults values are changed to point to custom terms we created to
* allow for indirection (e.g., Button.background -> laf.color.Button.background)
* <LI>Normalized Keys - keys we created to facilitate the UI Indirection, based upon the Java
* keys (e.g., laf.color.Button.background)
* <LI>System Color/Font Keys - user facing terms for common color or font concepts into an
* easy-to-change setting (e.g., system.color.fg.text)
* <LI>Palette Keys - dynamically generated color palette keys based on the LaF for any colors
* and fonts that were not mapped into an system color or font (e.g.,
* laf.palette.color.01)
* </UL>
*
* <P>
* The mapper performs the following operations:
* <OL>
* <LI>Extracts all color, font, and icon values from the UI Defaults.</LI>
* <LI>Use the current LaF values to populate the pre-defined system colors and fonts.</LI>
* <LI>Any UI Defaults values not assigned in the previous step will be assigned to a dynamic shared
* palette color or font.
* <LI>Update Java UI Defaults to use our indirection and system values.</LI>
* </OL>
*
*/
public class UiDefaultsMapper {
public static final String LAF_COLOR_ID_PREFIX = "laf.color.";
public static final String LAF_FONT_ID_PREFIX = "laf.font.";
public static final String LAF_ICON_ID_PREFIX = "laf.icon.";
private static final String LAF_COLOR_PALETTE_PREFIX = "laf.palette.color.";
private static final String LAF_FONT_PALETTE_PREFIX = "laf.palette.font.";
private static final String[] MENU_COMPONENTS =
{ "Menu", "MenuBar", "MenuItem", "PopupMenu", "RadioButtonMenuItem", "CheckBoxMenuItem" };
private static final String[] VIEW_COMPONENTS =
{ "FileChooser", "ColorChooser", "ComboBox", "List", "Table", "Tree", "TextField",
"FormattedTextField", "PasswordField", "TextArea", "TextPane", "EditorPane" };
private static final String[] TOOLTIP_COMPONENTS = { "ToolTip" };
protected UIDefaults defaults;
private GThemeValueMap extractedValues;
private GThemeValueMap normalizedValues = new GThemeValueMap();
private Map<String, String> lafIdToNormalizedIdMap = new HashMap<>();
protected Set<String> ignoredLafIds = new HashSet<>();
private Map<String, ColorMatcher> componentToColorMatcherMap = new HashMap<>();
private Map<String, FontMatcher> componentToFontMatcherMap = new HashMap<>();
// @formatter:off
protected ColorMatcher viewColorMatcher = new ColorMatcher(BG_VIEW_ID,
FG_VIEW_ID,
BG_VIEW_SELECTED_ID,
FG_VIEW_SELECTED_ID);
protected ColorMatcher tooltipColorMatcher = new ColorMatcher(BG_TOOLTIP_ID,
FG_TOOLTIP_ID);
protected ColorMatcher defaultColorMatcher = new ColorMatcher(BG_CONTROL_ID,
FG_CONTROL_ID,
BG_VIEW_ID,
FG_VIEW_ID,
FG_DISABLED_ID,
BG_VIEW_SELECTED_ID,
FG_VIEW_SELECTED_ID,
BG_TOOLTIP_ID,
BG_BORDER_ID);
protected FontMatcher menuFontMatcher = new FontMatcher(FONT_MENU_ID);
protected FontMatcher viewFontMatcher = new FontMatcher(FONT_VIEW_ID);
protected FontMatcher defaultFontMatcher = new FontMatcher(FONT_CONTROL_ID,
FONT_VIEW_ID,
FONT_MENU_ID);
//@formatter:on
private Map<Color, String> lafColorPaletteMap = new HashMap<>();
private Map<Font, String> lafFontPaletteMap = new HashMap<>();
private int nextColorPaletteId;
private int nextFontPaletteId;
protected UiDefaultsMapper(UIDefaults defaults) {
this.defaults = defaults;
this.extractedValues = extractColorFontAndIconValuesFromDefaults();
assignSystemColorValues();
assignSystemFontValues();
registerIgnoredLafIds();
assignColorMatchersToComponentIds();
assignFontMatchersToComponentIds();
assignNormalizedColorValues();
assignNormalizedFontValues();
assignNormalizedIconValues();
}
/**
* Returns the normalized id to value map that will be installed into the
* ApplicationThemeManager to be the user changeable values for affecting the Java
* LookAndFeel colors, fonts, and icons
* @return a map of changeable values that affect java LookAndFeel values
*/
public GThemeValueMap getJavaDefaults() {
return normalizedValues;
}
/**
* Updates the UIDefaults file with indirect colors (GColors) and any overridden font or icon
* values as defined in theme.properites files
* @param overrides a Map that contains possible LookAndFeel value overridess
*/
public void installValuesIntoUIDefaults(GThemeValueMap overrides) {
//
// In the UI Defaults, colors use indirect values and fonts and icons use direct values.
// Here we install our GColors for the indirect colors. Then we set any font and icon
// values that are different than the defaults.
//
installGColorsIntoUIDefaults();
installOverriddenFontsIntoUIDefaults(overrides);
installOverriddenIconsIntoUIDefaults(overrides);
}
/**
* Returns a mapping of normalized LaF Ids so that when fonts and icons get changed using the
* normalized ids that are presented to the user, we know which LaF ids need to be updated in
* the UiDefaults so that the LookAndFeel will pick up and use the changes.
* @return a mapping of normalized LaF ids to original LaF ids.
*/
public Map<String, String> getNormalizedIdToLafIdMap() {
Map<String, String> map = new HashMap<>();
for (Entry<String, String> entry : lafIdToNormalizedIdMap.entrySet()) {
String lafId = entry.getKey();
String standardId = entry.getValue();
map.put(standardId, lafId);
}
return map;
}
/**
* Registers any {@link LookAndFeel} ids that are not used directly (e.g. "control", "text",
* etc.) so that these values won't get mapped to any normalized id. There is no need for these
* values to show up in the theme values, since changing them will have no effect. They are
* used to seed the values for the system color and fonts. Subclasses should
* override this method to add additional ids so they won't show up in the theme values.
*/
protected void registerIgnoredLafIds() {
ignoredLafIds.add("desktop");
ignoredLafIds.add("activeCaption");
ignoredLafIds.add("activeCaptionText");
ignoredLafIds.add("activeCaptionBorder");
ignoredLafIds.add("inactiveCaption");
ignoredLafIds.add("inactiveCaptionText");
ignoredLafIds.add("inactiveCaptionBorder");
ignoredLafIds.add("window");
ignoredLafIds.add("windowBorder");
ignoredLafIds.add("windowText");
ignoredLafIds.add("menu");
ignoredLafIds.add("menuText");
ignoredLafIds.add("text");
ignoredLafIds.add("textText");
ignoredLafIds.add("textHighlight");
ignoredLafIds.add("textHighightText");
ignoredLafIds.add("textInactiveText");
ignoredLafIds.add("control");
ignoredLafIds.add("controlText");
ignoredLafIds.add("controlHighlight");
ignoredLafIds.add("controlLtHighlight");
ignoredLafIds.add("controlShadow");
ignoredLafIds.add("controlDkShadow");
ignoredLafIds.add("info");
ignoredLafIds.add("infoText");
ignoredLafIds.add("scrollbar");
}
/**
* Defines the values to assign to all the system color ids based on the system ids
* defined in the {@link BasicLookAndFeel}
*/
protected void assignSystemColorValues() {
assignSystemColorFromLafId(BG_CONTROL_ID, "control");
assignSystemColorFromLafId(FG_CONTROL_ID, "controlText");
assignSystemColorFromLafId(BG_BORDER_ID, "controlShadow");
assignSystemColorFromLafId(BG_VIEW_ID, "window");
assignSystemColorFromLafId(FG_VIEW_ID, "windowText");
assignSystemColorFromLafId(BG_VIEW_SELECTED_ID, "textHighlight");
assignSystemColorFromLafId(FG_VIEW_SELECTED_ID, "textHighlightText");
assignSystemColorFromLafId(FG_DISABLED_ID, "textInactiveText");
assignSystemColorFromLafId(BG_TOOLTIP_ID, "info");
assignSystemColorFromLafId(FG_TOOLTIP_ID, "infoText");
}
/**
* Assigns the system color id to a color value from the UiDefaults map.
* @param systemColorId the system color id to get a value for
* @param lafId the LaF key to use to retrieve a color from the UiDefaults
*/
protected void assignSystemColorFromLafId(String systemColorId, String lafId) {
Color lafColor = defaults.getColor(lafId);
if (lafColor == null) {
Msg.debug(this, "Missing value for system color: \"" + systemColorId +
"\". No value for laf id: \"" + lafId + "\".");
return;
}
normalizedValues.addColor(new ColorValue(systemColorId, lafColor));
}
/**
* Assigns the system color id to a directly specified color and does not use the LaF to populate
* the system color.
* @param systemColorId the system color id to assign the given color
* @param color the color to be assigned to the system color id
*/
protected void assignSystemColorDirect(String systemColorId, Color color) {
normalizedValues.addColor(new ColorValue(systemColorId, color));
}
/**
* Assigns the system font id a directly specified font and does not use the LaF to populate
* the system font.
* @param systemFontId the system font id to assign the given font
* @param font the font to be assigned to the system font id
*/
protected void assignSystemFontDirect(String systemFontId, Font font) {
normalizedValues.addFont(new FontValue(systemFontId, font));
}
/**
* Defines the values to use for the system fonts.
*/
protected void assignSystemFontValues() {
assignSystemFontFromLafId(FONT_CONTROL_ID, "Button.font");
assignSystemFontFromLafId(FONT_VIEW_ID, "Table.font");
assignSystemFontFromLafId(FONT_MENU_ID, "Menu.font");
}
private void assignSystemFontFromLafId(String systemFontId, String lafId) {
Font lafFont = extractedValues.getResolvedFont(lafId);
if (lafFont == null) {
Msg.debug(this, "Missing value for system font: \"" + systemFontId +
"\". No value for laf id: \"" + lafId + "\".");
return;
}
normalizedValues.addFont(new FontValue(systemFontId, fromUiResource(lafFont)));
}
/**
* Assigns the appropriate font matcher to each component in the related component group
*/
protected void assignFontMatchersToComponentIds() {
defineComponentFontMatcher(MENU_COMPONENTS, menuFontMatcher);
defineComponentFontMatcher(VIEW_COMPONENTS, viewFontMatcher);
}
/**
* Assigns the appropriate color matcher to each component in the related component group
*/
protected void assignColorMatchersToComponentIds() {
defineComponentColorMatcher(VIEW_COMPONENTS, viewColorMatcher);
defineComponentColorMatcher(TOOLTIP_COMPONENTS, tooltipColorMatcher);
}
/**
* Assigns every component name in the component group to the given ColorValueMatcher
* @param componentGroups a list of component names
* @param matcher the ColorMatcher that will provide the precedence of system ids to
* search when replacing LaF component specific values
*/
private void defineComponentColorMatcher(String[] componentGroups, ColorMatcher matcher) {
for (String componentGroup : componentGroups) {
componentToColorMatcherMap.put(componentGroup, matcher);
}
}
/**
* Assigns every component name in a component group to the given FontValueMapper
* @param componentGroups a list of component names
* @param matcher the FontValueMatcher that will provide the precedence of ststem font ids to
* search when replacing LaF component specific fonts with a system Font
*/
private void defineComponentFontMatcher(String[] componentGroups, FontMatcher matcher) {
for (String componentGroup : componentGroups) {
componentToFontMatcherMap.put(componentGroup, matcher);
}
}
/**
* Populates the GThemeValueMap with normalized font ids. For example
* it will assign "laf.font.Button.font" to "system.font.control".
*/
private void assignNormalizedFontValues() {
List<String> list = new ArrayList<>(extractedValues.getFontIds());
Collections.sort(list);
for (String lafId : list) {
// we don't want to create java defaults for laf system ids since changing them would
// have no effect
if (ignoredLafIds.contains(lafId)) {
continue;
}
String createdId = LAF_FONT_ID_PREFIX + lafId;
lafIdToNormalizedIdMap.put(lafId, createdId);
Font lafFont = extractedValues.getResolvedFont(lafId);
FontValue fontValue = getFontValue(createdId, lafId, lafFont);
normalizedValues.addFont(fontValue);
}
}
/**
* Populates the GThemeValueMap with normalized icon ids. For example
* it will assign "laf.font.CheckBox.icon" to a direct icon that was mined from the UiDefaults
* using the id "CheckBox.icon"
*/
private void assignNormalizedIconValues() {
for (String lafId : extractedValues.getIconIds()) {
String createdId = LAF_ICON_ID_PREFIX + lafId;
Icon icon = extractedValues.getResolvedIcon(lafId);
if (icon != null) {
normalizedValues.addIcon(new IconValue(createdId, icon));
lafIdToNormalizedIdMap.put(lafId, createdId);
}
}
}
/**
* Populates the GThemeValueMap with normalized color ids. For example
* it will assign "laf.color.Button.background" to "system.color.bg.control".
*/
protected void assignNormalizedColorValues() {
List<String> list = new ArrayList<>(extractedValues.getColorIds());
Collections.sort(list);
for (String lafId : list) {
if (ignoredLafIds.contains(lafId)) {
continue;
}
String createdId = LAF_COLOR_ID_PREFIX + lafId;
lafIdToNormalizedIdMap.put(lafId, createdId);
Color lafColor = extractedValues.getResolvedColor(lafId);
ColorValue colorValue = getColorValue(createdId, lafId, lafColor);
normalizedValues.addColor(colorValue);
}
}
/**
* Creates a {@link ColorValue} for the given id. It either finds a system color id that matches
* the given color, or a shared palette color if no system color found.
* @param id the id to get a color value for
* @param lafId the lafId that we are creating a standard id/color value for
* @param lafColor the color as defined in the UiDefaults for the lafId
* @return a {@link ColorValue} for the given id. It either finds a system color id that matches
* the given color, or a shared palette color if no system color found.
*/
private ColorValue getColorValue(String id, String lafId, Color lafColor) {
String systemId = findSystemColorId(lafId, lafColor);
if (systemId == null) {
systemId = getColorPaletteId(lafColor);
}
return new ColorValue(id, systemId);
}
/**
* Creates a {@link FontValue} for the given id. It either finds a system font id that matches
* the given Font, or a shared palette Font if no system font found.
* @param id the id to get a Font value for
* @param lafId the lafId that we are creating a standard id/Font value for
* @param lafFont the Font as defined in the UiDefaults for the lafId
* @return a {@link FontValue} for the given id. It either finds a system font id that matches
* the given Font, or a shared palette Font if no group Font found.
*/
private FontValue getFontValue(String id, String lafId, Font lafFont) {
String systemFontId = findSystemFontId(lafId, lafFont);
if (systemFontId == null) {
systemFontId = getFontPaletteId(lafFont);
}
return new FontValue(id, systemFontId);
}
/**
* Finds a matching color palette id or creates a new one
* @param lafColor the color to find a matching palette color for
* @return a matching color palette id or creates a new one
*/
private String getColorPaletteId(Color lafColor) {
String paletteId = lafColorPaletteMap.get(lafColor);
if (paletteId == null) {
nextColorPaletteId++;
// laf.palette.color01
paletteId = String.format("%s%02d", LAF_COLOR_PALETTE_PREFIX, nextColorPaletteId);
lafColorPaletteMap.put(lafColor, paletteId);
normalizedValues.addColor(new ColorValue(paletteId, lafColor));
}
return paletteId;
}
/**
* Finds a matching font palette id or creates a new one
* @param lafFont the font to find a matching palette font for
* @return a matching font palette id or creates a new one
*/
private String getFontPaletteId(Font lafFont) {
String paletteId = lafFontPaletteMap.get(lafFont);
if (paletteId == null) {
nextFontPaletteId++;
// laf.palette.font01
paletteId = String.format("%s%02d", LAF_FONT_PALETTE_PREFIX, nextFontPaletteId);
lafFontPaletteMap.put(lafFont, paletteId);
normalizedValues.addFont(new FontValue(paletteId, lafFont));
}
return paletteId;
}
/**
* Attempts to find a system color id that matches the given color. The order system ids are
* searched depends on the component (Button, Menu, etc.) which is derived from the given
* lafId.
* @param lafId the lafId we are attempting to get a system color for
* @param lafColor the color we are trying to match to a system color
* @return a system color id that matches the given lafColor or null if one can't be found
*/
private String findSystemColorId(String lafId, Color lafColor) {
String componentName = getComponentName(lafId);
ColorMatcher colorMatcher = componentToColorMatcherMap.get(componentName);
// check in widget specific group first
if (colorMatcher != null) {
String systemId = colorMatcher.getSystemId(lafColor);
if (systemId != null) {
return systemId;
}
}
// not found in widget specific group, check general component groups
return defaultColorMatcher.getSystemId(lafColor);
}
/**
* Attempts to find a system font id that matches the given font. The order system fonts are
* searched depends on the component (Button, Menu, etc.) which is derived from the given
* lafId.
* @param lafId the lafId we are attempting to get a system font for
* @param lafFont the font we are trying to match to a system font
* @return a system font id that matches the given lafFont or null if one can't be found
*/
private String findSystemFontId(String lafId, Font lafFont) {
String componentName = getComponentName(lafId);
FontMatcher fontMatcher = componentToFontMatcherMap.get(componentName);
// check in widget specific group first
if (fontMatcher != null) {
String systemId = fontMatcher.getSystemId(lafFont);
if (systemId != null) {
return systemId;
}
}
// not found in widget specific group, check general component groups
return defaultFontMatcher.getSystemId(lafFont);
}
/**
* Gets the component name from the given lafId.
* @param lafId the lafId that starts with a component name
* @return the component name from the given lafId.
*/
private String getComponentName(String lafId) {
int dotIndex = lafId.indexOf(".");
if (dotIndex < 0) {
return lafId;
}
return lafId.substring(0, dotIndex);
}
/**
* Replaces UiDefaults values with {@link GColorUIResource} values the provide the theme
* indirection.
*/
protected void installGColorsIntoUIDefaults() {
Map<String, GColorUIResource> cachedColors = new HashMap<>();
for (String lafId : extractedValues.getColorIds()) {
String standardColorId = lafIdToNormalizedIdMap.get(lafId);
if (standardColorId != null) {
GColorUIResource sharedGColor = getSharedGColor(cachedColors, standardColorId);
defaults.put(lafId, sharedGColor);
}
}
}
/**
* Replaces any theme overridden icons into the UiDefaults.
* @param overrides the theme values that potentially override a laf icon value
*/
private void installOverriddenIconsIntoUIDefaults(GThemeValueMap overrides) {
for (String lafId : extractedValues.getIconIds()) {
String standardId = lafIdToNormalizedIdMap.get(lafId);
if (overrides.containsIcon(standardId)) {
defaults.put(lafId, overrides.getResolvedIcon(standardId));
}
}
}
/**
* Replaces any theme overridden fonts into the UiDefaults.
* @param overrides the theme values that potentially override a laf font value
*/
private void installOverriddenFontsIntoUIDefaults(GThemeValueMap overrides) {
for (String lafId : extractedValues.getFontIds()) {
String standardId = lafIdToNormalizedIdMap.get(lafId);
if (overrides.containsFont(standardId)) {
defaults.put(lafId, new FontUIResource(overrides.getResolvedFont(standardId)));
}
}
}
/**
* When putting {@link GColorUIResource} values into the UiDefaults, we need to make sure
* we use the same instance for the same color. Some LookAndFeels do "==" checks on colors
* when updating UI values.
* @param cache the cache of shared {@link GColorUIResource}s
* @param id the id we are creating a shared GColorUIResource for
* @return a GColorUIResource such that only one instance for a given id exists.
*/
private GColorUIResource getSharedGColor(Map<String, GColorUIResource> cache, String id) {
GColorUIResource gColor = cache.get(id);
if (gColor == null) {
gColor = new GColorUIResource(id);
cache.put(id, gColor);
}
return gColor;
}
protected void overrideColor(String lafId, String sytemId) {
String normalizedId = lafIdToNormalizedIdMap.get(lafId);
if (normalizedId == null) {
Msg.debug(this, "Missing value for laf id: \"" + lafId);
return;
}
normalizedValues.addColor(new ColorValue(normalizedId, sytemId));
}
/**
* Mines the UiDefaults for all color values.
* @return a map of id to values for UIDefaults Colors.
*/
protected GThemeValueMap extractColorFontAndIconValuesFromDefaults() {
GThemeValueMap values = new GThemeValueMap();
List<String> ids = getLookAndFeelIdsForType(Color.class);
for (String id : ids) {
values.addColor(new ColorValue(id, defaults.getColor(id)));
}
ids = getLookAndFeelIdsForType(Font.class);
for (String id : ids) {
values.addFont(new FontValue(id, defaults.getFont(id)));
}
ids = getLookAndFeelIdsForType(Icon.class);
for (String id : ids) {
Icon icon = defaults.getIcon(id);
values.addIcon(new IconValue(id, icon));
}
return values;
}
private Font fromUiResource(Font font) {
if (font instanceof UIResource) {
return new FontNonUiResource(font);
}
return font;
}
/**
* Finds all ids in the UIDefaults for a specific type (Color, Font, Icon)
* @param clazz the class of the type to mine for
* @return a list of all ids that have the given value type
*/
private List<String> getLookAndFeelIdsForType(Class<?> clazz) {
List<String> colorKeys = new ArrayList<>();
List<Object> keyList = IteratorUtils.toList(defaults.keys().asIterator());
for (Object key : keyList) {
if (key instanceof String) {
Object value = defaults.get(key);
if (clazz.isInstance(value)) {
colorKeys.add((String) key);
}
}
}
return colorKeys;
}
/**
* Used to match values (Colors or Fonts) into appropriate system ids. System ids are searched
* in the order the system ids are given in the constructor.
* @param <T> The theme value type (Color or Font)
*/
private abstract class ValueMatcher<T> {
private Map<T, String> map = new HashMap<>();
private List<String> systemIdList;
private boolean initialized;
ValueMatcher(String... systemIds) {
systemIdList = new ArrayList<>(Arrays.asList(systemIds));
}
private void initialize() {
initialized = true;
// process in reverse order so that earlier items in the list can overwrite later
// items if they have the same font
for (int i = systemIdList.size() - 1; i >= 0; i--) {
String systemId = systemIdList.get(i);
T value = getValueFromJavaDefaults(systemId);
if (value != null) {
map.put(value, systemId);
}
}
}
protected abstract T getValueFromJavaDefaults(String systemId);
String getSystemId(T value) {
if (!initialized) {
initialize();
}
return map.get(value);
}
}
/**
* Searches through all the system color ids registered for this matcher to find a system color
* id that matches a given color. The order that color system ids are added is important and is
* the precedence order if more than one system color id has the same color.
*/
private class ColorMatcher extends ValueMatcher<Color> {
ColorMatcher(String... systemIds) {
super(systemIds);
}
@Override
protected Color getValueFromJavaDefaults(String systemId) {
return normalizedValues.getResolvedColor(systemId);
}
}
/**
* Searches through all the system font ids registered for this matcher to find a system font id
* that matches a given font. The order that system font ids are added is important and is
* the precedence order if more than one system id has the same font.
*/
private class FontMatcher extends ValueMatcher<Font> {
FontMatcher(String... systemIds) {
super(systemIds);
}
@Override
protected Font getValueFromJavaDefaults(String systemId) {
return normalizedValues.getResolvedFont(systemId);
}
}
}

View file

@ -15,6 +15,8 @@
*/
package generic.theme.laf;
import javax.swing.UIDefaults;
import generic.theme.ApplicationThemeManager;
import generic.theme.LafType;
@ -23,4 +25,9 @@ public class WindowsClassicLookAndFeelManager extends LookAndFeelManager {
public WindowsClassicLookAndFeelManager(ApplicationThemeManager themeManager) {
super(LafType.WINDOWS_CLASSIC, themeManager);
}
@Override
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
return new WindowsClassicUiDefaultsMapper(defaults);
}
}

View file

@ -0,0 +1,26 @@
/* ###
* 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 javax.swing.UIDefaults;
public class WindowsClassicUiDefaultsMapper extends UiDefaultsMapper {
protected WindowsClassicUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
}

View file

@ -15,6 +15,8 @@
*/
package generic.theme.laf;
import javax.swing.UIDefaults;
import generic.theme.ApplicationThemeManager;
import generic.theme.LafType;
@ -24,4 +26,9 @@ public class WindowsLookAndFeelManager extends LookAndFeelManager {
super(LafType.WINDOWS, themeManager);
}
@Override
protected UiDefaultsMapper getUiDefaultsMapper(UIDefaults defaults) {
return new WindowsUiDefaultsMapper(defaults);
}
}

View file

@ -0,0 +1,26 @@
/* ###
* 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 javax.swing.UIDefaults;
public class WindowsUiDefaultsMapper extends UiDefaultsMapper {
protected WindowsUiDefaultsMapper(UIDefaults defaults) {
super(defaults);
}
}

View file

@ -23,12 +23,10 @@ import java.awt.Font;
import java.net.URL;
import java.util.*;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.*;
import javax.swing.plaf.UIResource;
import org.junit.Before;
import org.junit.Test;
import org.junit.*;
import generic.theme.builtin.*;
import resources.ResourceManager;
@ -73,6 +71,12 @@ public class ApplicationThemeManagerTest {
themeManager = new DummyApplicationThemeManager();
}
@After
public void cleanupUIDefaults() {
UIDefaults defaults = UIManager.getDefaults();
defaults.clear();
}
@Test
public void testDarkThemeColorOverride() {
GColor gColor = new GColor("color.test.bg");
@ -348,21 +352,21 @@ public class ApplicationThemeManagerTest {
}
@Override
protected ThemeDefaultsProvider getThemeDefaultsProvider() {
return new ThemeDefaultsProvider() {
protected ApplicationThemeDefaults getApplicationDefaults() {
return new ApplicationThemeDefaults() {
@Override
public GThemeValueMap getDefaults() {
public GThemeValueMap getLightValues() {
return defaultValues;
}
@Override
public GThemeValueMap getDarkDefaults() {
public GThemeValueMap getDarkValues() {
return darkDefaultValues;
}
@Override
public GThemeValueMap getLookAndFeelDefaults(LafType lafType) {
public GThemeValueMap getLookAndFeelValues(LafType lafType) {
return null;
}

View file

@ -1,104 +0,0 @@
/* ###
* 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 static ghidra.util.WebColors.*;
import static org.junit.Assert.*;
import java.awt.Color;
import java.awt.Font;
import org.junit.Before;
import org.junit.Test;
import generic.theme.*;
public class ThemeGrouperTest {
private static Font FONT1 = new Font("Dialog", Font.PLAIN, 12);
private static Font FONT2 = new Font("Dialog", Font.BOLD, 16);
private ThemeGrouper grouper;
private GThemeValueMap values;
@Before
public void setUp() {
grouper = new ThemeGrouper();
values = new GThemeValueMap();
}
@Test
public void testGroupColorUsingPreferredSources() {
initColor("control", RED);
initColor("menu", RED);
initColor("Menu.background", RED);
grouper.group(values);
ColorValue colorValue = values.getColor("Menu.background");
assertEquals("menu", colorValue.getReferenceId());
}
@Test
public void testGroupColorUsingNonPreferredSourceWhenPreferredDoesntMatch() {
initColor("control", RED);
initColor("menu", BLUE);
initColor("Menu.background", RED);
grouper.group(values);
ColorValue colorValue = values.getColor("Menu.background");
assertEquals("control", colorValue.getReferenceId());
}
@Test
public void testGroupFontUsingPreferredSources() {
initFont("Button.font", FONT1);
initFont("RadioButton.font", FONT1);
initFont("Menu.font", FONT1);
initFont("MenuItem.font", FONT1);
grouper.group(values);
assertEquals(FONT1, values.getFont("font.button").getRawValue());
assertEquals(FONT1, values.getFont("font.menu").getRawValue());
assertEquals("font.button", values.getFont("Button.font").getReferenceId());
assertEquals("font.button", values.getFont("RadioButton.font").getReferenceId());
assertEquals("font.menu", values.getFont("Menu.font").getReferenceId());
assertEquals("font.menu", values.getFont("MenuItem.font").getReferenceId());
}
@Test
public void testGroupFontUsingNonPreferredSourceWhenPreferredDoesntMatch() {
initFont("Button.font", FONT1);
initFont("RadioButton.font", FONT1);
initFont("Menu.font", FONT2);
initFont("MenuItem.font", FONT1);
grouper.group(values);
assertEquals(FONT1, values.getFont("font.button").getRawValue());
assertEquals(FONT2, values.getFont("font.menu").getRawValue());
assertEquals("font.button", values.getFont("Button.font").getReferenceId());
assertEquals("font.button", values.getFont("RadioButton.font").getReferenceId());
assertEquals("font.menu", values.getFont("Menu.font").getReferenceId());
assertEquals("font.button", values.getFont("MenuItem.font").getReferenceId());
}
private void initColor(String id, Color color) {
values.addColor(new ColorValue(id, color));
}
private void initFont(String id, Font font) {
values.addFont(new FontValue(id, font));
}
}

View file

@ -0,0 +1,183 @@
/* ###
* 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 static org.junit.Assert.*;
import java.awt.Color;
import java.awt.Font;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.UIDefaults;
import org.junit.Before;
import org.junit.Test;
import generic.theme.*;
import resources.ResourceManager;
public class UIDefaultsMapperTest {
private Icon ICON = ResourceManager.loadImage("images/exec.png");
private Font SMALL_FONT = new Font("Dialog", Font.PLAIN, 4);
private Font FONT = new Font("Dialog", Font.PLAIN, 13);
private UIDefaults defaults;
private UiDefaultsMapper mapper;
@Before
public void setup() {
defaults = createDefaults();
defaults.put("control", Color.RED);
defaults.put("Button.background", Color.RED);
defaults.put("Button.font", FONT);
defaults.put("CheckBox.icon", ICON);
}
@Test
public void testGetJavaDefaults() {
mapper = new UiDefaultsMapper(defaults);
GThemeValueMap javaDefaults = mapper.getJavaDefaults();
assertEquals(Color.RED, javaDefaults.getResolvedColor("system.color.bg.control"));
assertEquals(Color.RED, javaDefaults.getResolvedColor("laf.color.Button.background"));
assertEquals(FONT, javaDefaults.getResolvedFont("laf.font.Button.font"));
assertEquals(ICON, javaDefaults.getResolvedIcon("laf.icon.CheckBox.icon"));
assertIndirectColor(javaDefaults, "laf.color.Button.background", "system.color.bg.control");
assertIndirectFont(javaDefaults, "laf.font.Button.font", "system.font.control");
assertDirectIcon(javaDefaults, "laf.icon.CheckBox.icon", ICON);
}
@Test
public void testGetJavaDefaultsNoGroupCreatesPaletteColors() {
defaults.put("CheckBox.background", Color.GREEN); // Green not defined in a color group
defaults.put("ToggleButton.background", Color.GREEN); // Green not defined in a color group
defaults.put("RadioButton.background", Color.BLUE); // Blue not defined in a color group
mapper = new UiDefaultsMapper(defaults);
GThemeValueMap javaDefaults = mapper.getJavaDefaults();
// expecting two palette groups to be created
String greenPalette = findPaletteColor(javaDefaults, Color.GREEN);
String bluePalette = findPaletteColor(javaDefaults, Color.BLUE);
assertIndirectColor(javaDefaults, "laf.color.Button.background", "system.color.bg.control");
assertIndirectColor(javaDefaults, "laf.color.CheckBox.background", greenPalette);
assertIndirectColor(javaDefaults, "laf.color.ToggleButton.background", greenPalette);
assertIndirectColor(javaDefaults, "laf.color.RadioButton.background", bluePalette);
assertDirectColor(javaDefaults, "system.color.bg.control", Color.RED);
}
@Test
public void testGetJavaDefaultsNoGroupCreatesPaletteFonts() {
defaults.put("CheckBox.font", SMALL_FONT); // SMALL_FONT not defined in a font group
defaults.put("ToggleButton.font", SMALL_FONT); // Green not defined in a color group
mapper = new UiDefaultsMapper(defaults);
GThemeValueMap javaDefaults = mapper.getJavaDefaults();
assertDirectFont(javaDefaults, "laf.palette.font.01", SMALL_FONT);
assertIndirectFont(javaDefaults, "laf.font.ToggleButton.font", "laf.palette.font.01");
assertIndirectFont(javaDefaults, "laf.font.CheckBox.font", "laf.palette.font.01");
assertIndirectFont(javaDefaults, "laf.font.Button.font", "system.font.control");
}
@Test
public void testInstallValuesIntoUiDefaults() {
mapper = new UiDefaultsMapper(defaults);
mapper.installValuesIntoUIDefaults(new GThemeValueMap());
assertEquals(new GColorUIResource("laf.color.Button.background"),
defaults.getColor("Button.background"));
assertEquals(FONT, defaults.getFont("Button.font"));
assertEquals(ICON, defaults.getIcon("CheckBox.icon"));
}
@Test
public void testGetNormalizedIdToLafidMap() {
mapper = new UiDefaultsMapper(defaults);
Map<String, String> map = mapper.getNormalizedIdToLafIdMap();
assertEquals("Button.background", map.get("laf.color.Button.background"));
assertEquals("Button.font", map.get("laf.font.Button.font"));
assertEquals("CheckBox.icon", map.get("laf.icon.CheckBox.icon"));
}
private void assertDirectColor(GThemeValueMap javaDefaults, String id, Color color) {
ColorValue colorValue = javaDefaults.getColor(id);
assertEquals(color, colorValue.getRawValue());
}
private void assertDirectFont(GThemeValueMap javaDefaults, String id, Font font) {
FontValue fontValue = javaDefaults.getFont(id);
assertEquals(font, fontValue.getRawValue());
}
private void assertIndirectColor(GThemeValueMap javaDefaults, String id, String indirectId) {
ColorValue colorValue = javaDefaults.getColor(id);
assertEquals(indirectId, colorValue.getReferenceId());
}
private void assertIndirectFont(GThemeValueMap javaDefaults, String id, String indirectId) {
FontValue fontValue = javaDefaults.getFont(id);
assertEquals(indirectId, fontValue.getReferenceId());
}
private void assertDirectIcon(GThemeValueMap javaDefaults, String id, Icon icon) {
IconValue iconValue = javaDefaults.getIcon(id);
assertEquals(icon, iconValue.getRawValue());
}
private String findPaletteColor(GThemeValueMap javaDefaults, Color color) {
for (ColorValue colorValue : javaDefaults.getColors()) {
if (colorValue.getId().contains("palette.color") &&
colorValue.getRawValue().equals(color)) {
return colorValue.getId();
}
}
fail("Could not find pallete color for " + color);
return null;
}
private UIDefaults createDefaults() {
// populate defaults with standard laf group values, to avoid warning messages complaining
// about them being undefined
UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put("control", Color.BLACK); // for laf.group.control.color.bg
uiDefaults.put("controlText", Color.BLACK); // for laf.group.control.color.fg
uiDefaults.put("controlShadow", Color.BLACK); // for laf.group.control.color.border
uiDefaults.put("window", Color.BLACK); // for laf.group.view.color.bg
uiDefaults.put("windowText", Color.BLACK); // for laf.group.view.color.fg
uiDefaults.put("textHighlight", Color.BLACK); // for laf.group.view.color.bg.selected and laf.group.text.color.bg.selected
uiDefaults.put("textHighlightText", Color.BLACK); // for laf.group.view.color.fg.selected and laf.group.text.color.fg.selected
uiDefaults.put("textText", Color.BLACK); // for laf.group.text.color.fg
uiDefaults.put("text", Color.BLACK); // for laf.group.text.color.bg
uiDefaults.put("textInactiveText", Color.BLACK); // for laf.group.text.color.fg.disabled
uiDefaults.put("info", Color.BLACK); // for laf.group.tooltip.color.bg
uiDefaults.put("infoText", Color.BLACK); // for laf.group.tooltip.color.fg
uiDefaults.put("Panel.font", FONT); // for laf.group.control.font
uiDefaults.put("TextField.font", FONT); // for laf.group.text.font
uiDefaults.put("Table.font", FONT); // for laf.group.view.font
uiDefaults.put("Menu.font", FONT); // for laf.group.menu.font
return uiDefaults;
}
}