diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/laf/UiDefaultsMapper.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/laf/UiDefaultsMapper.java index 17ef4d0129..94813fcb62 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/theme/laf/UiDefaultsMapper.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/laf/UiDefaultsMapper.java @@ -23,6 +23,7 @@ import java.util.*; import java.util.Map.Entry; import javax.swing.*; +import javax.swing.UIDefaults.LazyValue; import javax.swing.plaf.FontUIResource; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicLookAndFeel; @@ -674,6 +675,32 @@ public class UiDefaultsMapper { */ private List getLookAndFeelIdsForType(Class clazz) { List ids = new ArrayList<>(); + + /* + Note: the act of calling defaults.get(key) will cause LazyValues to get loaded and + installed. This is a problem for any complex object that holds Color state. After this + method is called we will install our GColor indirection mechanism. But, if the + LazyValue gets created during this method, then that value may be holding Color objects + that are not using our indirection, thus making them not responsive to theme changes. + An example of this is a custom Border object with saved Color fields. + + To work around this issue, we save off all LazyValues and then restore them after we get + the value. By doing this, the LazyValue will get called again later after we have + installed our indirection. + */ + + // 1) save all LazyValues so we can restore them later + Map lazyRestores = new HashMap<>(); + Set> entries = defaults.entrySet(); + for (Entry entry : entries) { + Object key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof LazyValue) { + lazyRestores.put(key, value); + } + } + + // 2) find all keys that map to the given 'clazz' passed to this method List keyList = IteratorUtils.toList(defaults.keys().asIterator()); for (Object key : keyList) { if (key instanceof String) { @@ -681,8 +708,18 @@ public class UiDefaultsMapper { if (clazz.isInstance(value)) { ids.add((String) key); } + } } + + // 3_ put the LazyValues back so we undo any items created while we were getting values + Set> restoreEntries = lazyRestores.entrySet(); + for (Entry entry : restoreEntries) { + Object key = entry.getKey(); + Object value = entry.getValue(); + defaults.put(key, value); + } + return ids; }