The previous overrides set JideTabbedPane.selectedTabTextForeground and
TabbedPane.selectedForeground to fgSelection (always white). Selected tabs
render on the raised surface (bgRaised) which is white in light mode, so
white-on-white made selected tab labels invisible.
Selected tabs are not list/table row selections that use the blue
bgSelection background - they sit on the same raised tone as other surfaces.
Use fgPrimary so the text is dark on the light raised surface in light mode
and light on the dark raised surface in dark mode.
Toggle-off problem: applyExplicitOverrides was storing plain java.awt.Color
values via UIManager.put. LookAndFeel.installColorsAndFont only replaces a
component's existing colour when it is null or an instance of UIResource -
plain Colors are not, so once a component picked up its dark colour it kept
that colour for the rest of the JVM lifetime even after toggling back to
light mode. Switching every override to ColorUIResource lets subsequent
updateComponentTreeUI calls replace the cached colour cleanly.
Chart visibility: the JavaFX charts on the Channel tab (NoiseSquelchView)
were inheriting Modena's default 8-colour series palette, which has several
hues that read poorly against a dark plot background. The existing
sdrtrunk_style.css also hardcodes .chart-symbol to solid black. Override
.default-color0..7.chart-series-line and matching .chart-line-symbol selectors
with a brighter high-contrast palette, darken the plot background to #1c1c1c
for stronger contrast against the grid lines, and add explicit colours for
axis tick marks, alternate row fills and legend border.
The Channel tab embeds two JavaFX scenes inside Swing via JFXPanel: the
noise squelch chart (NoiseSquelchView) and the symbol view. Neither was
registered with the ThemeManager, so the dark stylesheet was never applied
to them. In dark mode the chart legend, axis area and surrounding panel
rendered with JavaFX's default light Modena styling against the otherwise
dark UI.
JFXPanel-hosted scenes are invisible to javafx.stage.Window.getWindows(),
so the theme manager cannot find them automatically on toggle - they have
to be explicitly registered at creation time, the same as the JavaFX
Stages JavaFxWindowManager already registers.
ControllerPanel.getTabbedPane() and NowPlayingPanel.getTabbedPane() each
called setForeground(Color.BLACK) on their JideTabbedPane instances.
Color.BLACK is a plain java.awt.Color, not a ColorUIResource, so it is
never replaced by LookAndFeel.installColorsAndFont() on subsequent UI
installs - it permanently pins the tab labels to black regardless of any
JIDE or FlatLaf defaults the theme manager sets. In dark mode this leaves
the tab labels black-on-dark and unreadable.
Drop the explicit foreground calls so the LAF (JideTabbedPaneUI reading
JideTabbedPane.foreground / .unselectedTabTextForeground /
.selectedTabTextForeground / .activeTabTextForeground) drives the colour
in both light and dark modes.
The Now Playing / Map / Tuners / Playlist Editor / Details / Events /
Messages / Channel tabs are JIDE JideTabbedPane components and their UI
delegate reads JideTabbedPane.* UIManager keys, not the standard
TabbedPane.* keys. The previous overrides only set the standard keys so
the tabs were still rendering with JIDE's Metal-era dark foreground on
the now-dark background.
Override the JIDE-specific text foreground keys
(JideTabbedPane.foreground, .selectedTabTextForeground,
.unselectedTabTextForeground, .activeTabTextForeground) along with the
matching background and shadow keys. Also theme JideSplitPane,
JideButton and JideLabel keys for consistency.
Symptom: With dark mode enabled the inner Swing content area now had dark
panels (good) but tab labels, table cells, status-bar labels and button
text were rendering nearly black-on-dark (bad).
Root cause: UIManager.getLookAndFeelDefaults() lookups for FlatLaf's
component foreground keys still resolve to Metal-era dark values after the
JIDE install + FlatLaf re-apply, because some FlatLaf keys are derived /
LazyValue and recompute against state that JIDE seeded.
Fix: After the bulk FlatLaf-defaults copy, explicitly UIManager.put the
foreground / background / selection / disabled / border colors for every
standard Swing component family. UIManager.put writes to the user-defaults
layer that takes precedence over the LAF defaults, so these overrides win
regardless of what JIDE or derived-value resolution produces.
Also bump the JavaFX dark stylesheet text colour from #d6d6d6 to #ececec so
labels read closer to white on the dark panels.
Symptom: With dark mode enabled, the Swing main window's spectrum chrome and
menu bar were dark (FlatLaf) but tables, tabbed panes and the inner content
area were light/Metal coloured.
Root cause: LookAndFeelFactory.installJideExtension installs JIDE's
Metal-flavored UI defaults (VsnetMetalUtils etc.) into the live UIDefaults
table, overwriting FlatLaf's standard component colors and fonts for keys
like Table.background, TabbedPane.foreground, Panel.background.
Fix: After the JIDE install, walk a fresh FlatLaf UIDefaults table and put
every key back into the active defaults, skipping anything Jide-prefixed.
JIDE's UI delegate registrations live under Jide* keys that FlatLaf does
not define, so they survive untouched while the standard Swing color and
font keys are restored to their dark equivalents.
Also strengthen the JavaFX dark stylesheet: explicit borders on text fields,
tables, titled panes, split-pane dividers and separators so components have
more defining outlines in dark mode.
JIDE 3.6.18's LookAndFeelFactory contains compile-time
\`instanceof com.sun.java.swing.plaf.windows.WindowsLookAndFeel\` checks in
multiple methods, including a JIDE PropertyChangeListener that fires on every
UIManager.setLookAndFeel call. On Linux and macOS JDKs the class is genuinely
absent from java.desktop, so the JVM throws NoClassDefFoundError the first time
JIDE's dispatch code is executed (during JFrame init and again from
JideSplitPane.updateUI on each LAF change).
Two complementary fixes:
1. Pass an explicit MetalLookAndFeel instance to the 3-arg
installJideExtension(UIDefaults, LookAndFeel, int) overload so JIDE's Metal
branch matches first and never falls through to the Windows branch in the
normal install path.
2. Add a no-op stub class at com.sun.java.swing.plaf.windows.WindowsLookAndFeel
so that the compile-time class reference resolves on non-Windows JDKs. The
stub reports isSupportedLookAndFeel = false and is never instantiated -
JIDE only uses it for instanceof comparisons against the active LAF, which
always return false in our setup. On Windows JDKs the real class in
java.desktop is loaded by the platform classloader and shadows this stub.
- Apply Swing LAF synchronously on the calling thread so the main JFrame's RootPane
has a populated UIDefaults set when constructed. Previously the LAF install was
scheduled via EventQueue.invokeLater, so JFrame construction raced ahead and threw
"no ComponentUI class for: javax.swing.JPanel".
- Call LookAndFeelFactory.installJideExtension(VSNET_STYLE) with an explicit style
instead of the no-arg auto-detection variant. The auto-detection path probes
com.sun.java.swing.plaf.windows.WindowsLookAndFeel via Class.forName, which is
absent on non-Windows JDKs and threw ClassNotFoundException at startup.
- Bounce only the SwingUtilities.updateComponentTreeUI walk through the EDT so it
redraws already-realized windows on theme toggle.
- Add darkMode boolean preference to ApplicationPreference (persisted via java.util.prefs)
- Add ToggleSwitch labeled "Dark Mode" to ApplicationPreferenceEditor
- Add ThemeManager singleton that listens for preference updates and applies/removes
the dark stylesheet to all registered JavaFX Scenes and swaps the Swing LAF
- Add sdrtrunk_dark.css with dark styling for controls, tables, menus, dialogs, charts
- Add FlatLaf dependency for full Swing dark theming (replaces previous OS-specific
MetalLookAndFeel setup; FlatLaf works cross-platform)
- Register all Stages created in JavaFxWindowManager with the ThemeManager and walk
Window.getWindows() at apply-time so unregistered dialogs/alerts also pick up the theme
- Add Foojay toolchain resolver to settings.gradle so Gradle can auto-provision JDK 25
consolidates left and right channels into a single playback channel. Implements continuous audio streaming using
silence to fill the gaps to avoid mixer source data line start/stop issues. Code structure is ready to layer on
multi-channel (ie 5.1 or 7.1) audio playback devices, but that hasn't been tested.
Co-authored-by: denny <dennis.sheirer@gmail.com>
* #1892 Icecast 2.4+: Fix retries after network disruption
* #1892 Icecast 2.4+: Log the connection attempt/failure once and suppress subsequent logging until reconnected
* Modified P25P1 MBE ESS order. ESS will now be populated after LDU2, not at the beginning of LDU2
* Added a version field and incremented it to '2' to signify a breaking change in the order of P25P1 ESS placement.
C4FM now uses SIMD differential decoding and soft sync detection.
LSM updated with soft sync detector and retains original Gardner error detector.
Adds new QPSK symbol view in the now playing channel tab.
Restores PLL error driven tuner Auto-PPM feature.
Deletes the demod viewer GUI application and all instrumented decoders
Updates DMR to send soft symbols to new symbol view in Now Playing.
Co-authored-by: Dennis Sheirer <dsheirer@github.com>