Releases: Mustry-Solutions/ignition-designer-dark-mode-module
Release list
v0.2.0
A defect-fixing release. Ten dark-mode defects found by a Designer QA sweep
and fixed, each root-caused to a mechanism rather than patched by eye, and each
confirmed in a real Designer on 8.3.6. The theming passes gained failure
isolation, and the test harness gained two instruments it was missing:
component-level state diffing, and a check that everything this module reaches
by name still exists.
Added
-
A test for everything the module reaches by name
(#53).
This module works by reaching into Ignition, JIDE and JFreeChart internals as
strings — a class calledInlineTipLabel, a field calledCOLOR, a method
calledsetLineHighlightColor— none of which the compiler checks, and every
pass that uses one is deliberately guarded so a failure costs a surface rather
than the Designer. Together that means an IA rename stops a pass working
silently.ReflectiveSurfaceTestenumerates the whole surface (about 40
names) and asserts each still resolves, and CI now runs the harness at both
the 8.3.0 support floor and the current release. It cannot tell you a class
still behaves the same — that is what the QA checklist is for — but it turns
a silent regression into a red build. -
When
updateComponentTreeUIfails on a window, the module now says what
broke and how much of the tree went unrefreshed
(#12).
The failure was already survivable and already logged with its stack, but a
stack names the UI delegate that threw — not the component, and not the
subtree the aborted update never reached, which is the part that actually
matters. The report names components with no font at all and the path to each,
anyUIManagerfont key resolving to null, and every component left holding a
delegate from the wrong look and feel. Runs only on the failure path, so it is
not gated on the debug flag: that is precisely the moment nobody has verbose
logging on. -
The harness pins that no FlatLaf scaling listener is left registered
(#12).
With FlatLaf user scaling on,UIScaleregisters a listener on all three
defaults tables that is still there after the stock theme is back, reacting to
another look and feel's font changes. The module's only defence is one
ordering-sensitive line settingflatlaf.uiScale.enabled=falsebefore FlatLaf
loads, and nothing guarded it. Measured both ways: as shipped no such listener
appears anywhere; flip the property and all three tables carry one after a
restore. -
A headless look-and-feel harness (
./gradlew :designer:lafHarness,
#32).
It drives the real switch sequence against the real Synthetica, JIDE and
FlatLaf jars — no gateway, no Designer, no screenshots — and diffs every
resolvableUIManagerdefault across a light→dark→light cycle. The unit
tests only ever saw stub look and feels, so every bug this module has had
(#14, #17, #19, #22, #23) had to be found by deploying and looking. Four
invariants are now pinned instead: a full cycle restores every default, the
FlatLaf overrides are cleared while FlatLaf is still installed (the ordering
#23 got wrong), repeated cycles converge, and JIDE'sTheme.paintermap
comes back to its stock entries, the standard Swing colours actually go dark,
and noUIManagerkey naming a background stays light under dark mode —
which is #22
turned from a manual dump into an assertion over 174 keys. It runs in CI.Its blind spots are mapped and documented rather than assumed: state outside
UIManager(the IA colour tokens, the Synthetica singleton) is invisible to
it, the dark half is weakly covered because JIDE derives dark colours
correctly with no Designer present, and it cannot see pixels. A Designer
still settles "does this look right". -
A partly-applied theme now says so, in the Designer's status bar: which
passes failed, out of how many, and where to read the stack traces. Every
pass after the look-and-feel swap is isolated, so one that fails leaves a
Designer that works and is visibly wrong somewhere; the only record used to
be a log file nobody knows to look for. -
The Designer's status bar stays readable under dark mode.
StatusBar .setMessagere-assertsColor.blackon the message label on every call, so
its own messages were black on a dark bar. -
docs/QA-CHECKLIST.md — a sweep of Designer
surfaces with a pass/fail per surface, so coverage gaps are found before a
release rather than reported as bugs
(#5).
The surface list is drawn from the catalogue in the MIT-licensed
Exchange dark-mode script;
its 8.1 class names have shifted on 8.3, its UI locations have not. -
LookAndFeelDefaultsTableTest(./gradlew :designer:lafHarness) pins the
gap between the developer defaults table and the look-and-feel table: 109
colour keys resolve throughUIManagerbut not through
getLookAndFeelDefaults()under the stock look and feel, and the restore
leaves that set exactly as it found it. Ignition code reading a colour
that way gets null in a stock Designer and a real colour under dark mode,
which is the opposite of the intuitive direction and has already caused
one stack trace to be misread.
Changed
-
The headless harness runs against the current Ignition, not the support
floor (#53).
sdk_versionstays at 8.3.0 — it is what the module compiles against and what
module.xmlclaims as the minimum — but the harness now resolves
harness_sdk_version(8.3.8 by default,-Pharness.sdk=8.3.6to pin). The
module reaches Ignition and JIDE internals by name, and none of that is
compile-checked, so testing it against jars nobody runs was the weakest part
of the setup. The full suite passes against 8.3.8. -
Inline tip banners are readable under dark mode again
(#47).
InlineTipLabel.paintComponentfills with a literal#D6E4ED, so no
look-and-feel swap reaches it — while its text is IA'sBase900token, which
this module lightens. The result was light-on-pale: not merely wrong but
illegible, in eleven places including Help → Diagnostics, the permissions
configurator and the UDT multi-instance wizard. The fill now darkens with the
other class constants and restores with them. -
SQL and expression editors are themed
(#48).
"The script editor" is two components: the Python editors are
RSyntaxTextAreaand were already covered, while JIDE'sCodeEditor— the
Database Query Browser and every expression editor in the Designer, 46
classes' worth — was not. Under dark mode it kept a cream#FFFFD7band
across the current line, a black caret on dark chrome, and syntax tokens
at#000000,#000080,#650099. A newCodeEditorThemelifts each syntax
colour to a readable luminance while keeping its hue, so a keyword still reads
as a keyword, and restores every value on the way back to light. -
The Tag Browser's
Valueheader and the Perspective property editor's
filter now come back when dark mode is switched off
(#45). Two different
mechanisms, both invisible to the defaults diff — everyUIManagerkey was
already coming back correct.The header is painted by a cell renderer, which is not in the component
hierarchy.JTableHeader.updateUI()reaches its default renderer anyway, but
only while that renderer is aComponent: on the way into dark mode it is, so
Swing callsDefaultTableCellRenderer.updateUI()— literallysuper.updateUI(); setForeground(null); setBackground(null);— and destroys the colours
SimpleTreeTable$SimpleHeaderRenderersets in its constructor and never sets
again. On the way back the renderer is wrapped in ours, which is not a
Component, so Swing skips it and the header keeps null colours and a FlatLaf
delegate for the rest of the session. The colours are now snapshotted before
the switch can wipe them, and the delegate is put back explicitly.The filter is JIDE's
LabeledTextField, whoseupdateUI()ends in
setEnabled(), which doessetBackground(getTextField().getBackground()).
updateComponentTreeUIwalks parent first, so on the restore the wrapper
copies the inner field's still-dark#46494Bonto itself a moment before that
field goes light. A second pass now re-runsupdateUI()child-first on
anything left holding a darkUIResourcebackground.Measured on the real components: a light→dark→light cycle over a
SimpleTreeTableand aQuickFilterFieldrendered 15,462 pixels different
from stock before the fix and 0 after. -
The tree-update diagnostic now reports null backgrounds and foregrounds,
not only fonts. It was written against the description in
#12,
which named aFont.getFamily()NPE. The failure actually caught in the wild
wasColor.getAlpha()on a null background, so the instrument was looking at
the wrong property and would have reportedno font at all: 0while saying
nothing about the cause. ItsUIManagers...
v0.1.0
What's Changed
- Darken JIDE toolbar button states by @sdonche in #10
- Theme the code editors; isolate window updates on restore by @sdonche in #11
- Recolour console output styles for dark mode by @sdonche in #13
- Repoint ThemePainter references JIDE cached before the switch by @sdonche in #16
- Measure startup readiness in dock frames, not trees by @sdonche in #18
- Darken borders drawn in the Base000 token; widen the inspector by @sdonche in #20
- Darken JIDE defaults that stayed light; add a light-defaults diagnostic by @sdonche in #22
- Re-assert the JIDE painter map on every rescan by @sdonche in #19
- Retake the screenshots against the current build by @sdonche in #24
- Assert design tokens restore to their exact stock value by @sdonche in #25
- Reconcile the docs with what the code actually does by @sdonche in #26
- Drop the DCO requirement rather than leave it unenforced by @sdonche in #28
- fix: clear the UIManager overrides before the light restore (#23) by @sdonche in #27
- docs: cut the 0.1.0 changelog section by @sdonche in #30
New Contributors
Full Changelog: https://github.com/Mustry-Solutions/ignition-designer-dark-mode-module/commits/v0.1.0