diff --git a/github_docs/de-Benutzeroberflaeche.md b/github_docs/de-Benutzeroberflaeche.md index 470ea951..8ca0765b 100644 --- a/github_docs/de-Benutzeroberflaeche.md +++ b/github_docs/de-Benutzeroberflaeche.md @@ -482,8 +482,9 @@ Die serverbezogenen Funktionen sind nur bei vollständig aufgebauter ON4KST-Verb - **Hide cluster / stranger QSOs** beziehungsweise **Show cluster / stranger QSOs** blendet das zusätzliche Cluster- und QSO-Monitorfenster aus oder wieder ein. - **hide options** beziehungsweise **show options** blendet das Einstellungsfenster aus oder wieder ein. -- **Use dark mode design** aktiviert das dunkle Farbschema. -- **Use default mode design** aktiviert das normale helle Farbschema. +- **Use dark mode design** aktiviert das dunkle Farbschema für die laufende Sitzung. +- **Use default mode design** aktiviert das normale helle Farbschema für die laufende Sitzung. + Das Farbschema beim Start wird pro Profil im Einstellungsreiter **GUI** unter **Design** festgelegt. - **Show / hide station map** öffnet beziehungsweise schließt das separate Fenster mit Stationskarte und Streckenanalyse. --- diff --git a/github_docs/de-Changelog.md b/github_docs/de-Changelog.md index 05162a99..a4d04f7f 100644 --- a/github_docs/de-Changelog.md +++ b/github_docs/de-Changelog.md @@ -30,8 +30,14 @@ Mehrere Operateure an einem Rechner können jetzt eigene Rufzeichen, Locators un - **Rufzeichen-Vorgabe ist leer:** Fehlt in der `preferences.xml` ein Login-Rufzeichen, bleibt das Feld jetzt leer, statt auf ein im Programm hinterlegtes Rufzeichen zurückzufallen. Ein neu angelegtes Profil startet damit bewusst ohne Anmeldedaten. +- **Dark Mode pro Profil:** Im Reiter **GUI** legt jedes Operator-Profil unter **Design** fest, ob KST4Contest hell oder dunkel startet. Die Menüeinträge unter **Windows** schalten weiterhin schnell um, gelten aber nur für die laufende Sitzung. Das Farbschema erfasst jetzt auch das Update-Fenster und die Liste der Top-Kandidaten. + ### Behoben +- **Einheitliche Schriftgröße:** Unter macOS und Linux verwendet JavaFX eine etwas größere Standardschrift als unter Windows. Dadurch wurden Beschriftungen in fest bemessenen Bedienelementen abgeschnitten. KST4Contest verwendet jetzt auf allen Systemen dieselbe Grundschriftgröße; unter Windows ändert sich nichts. + +- **Dark Mode nach dem Neustart:** Das Hauptfenster startete immer hell, auch wenn zuletzt der Dark Mode aktiv war, während die Stationskarte dunkel blieb. Jetzt starten alle Fenster einheitlich mit dem Farbschema des Profils. + - **Freigegebene Hintergrundressourcen:** Der ON4KST-Überwachungsthread, der Sked-Erinnerungs-Scheduler, der Reachability-Executor, der PSTRotator-Wiederholungs-Scheduler und der Kachel-Proxy der Karte werden beim Schließen des Chatcontrollers freigegeben. Bisher liefen sie bis zum Programmende weiter. ### Hinweise zur Aktualisierung diff --git a/github_docs/de-Funktionen.md b/github_docs/de-Funktionen.md index 09d2fef6..99353966 100644 --- a/github_docs/de-Funktionen.md +++ b/github_docs/de-Funktionen.md @@ -318,7 +318,7 @@ KST4Contest meldet sich mit einem lokalen Login-Rufzeichen und Passwort einmal b ## Dark Mode (ab v1.26) -Aktivierbar über **Windows → Use dark mode design**. Mit **Windows → Use default mode design** wird wieder auf das normale helle Farbschema umgeschaltet. +Welches Farbschema beim Start gilt, wird pro Operator-Profil im Reiter **GUI** unter **Design** festgelegt. Über **Windows → Use dark mode design** und **Windows → Use default mode design** lässt sich für die laufende Sitzung schnell umschalten. Die grüne Altersskala der Privatnachrichten bleibt in beiden Darstellungen erhalten. Textfarbe, normale Tabellenfarbe und die separate Hervorhebung eigener Nachrichten folgen dem jeweils geladenen Standarddesign. diff --git a/github_docs/de-Konfiguration.md b/github_docs/de-Konfiguration.md index 26a50dd5..12a4093a 100644 --- a/github_docs/de-Konfiguration.md +++ b/github_docs/de-Konfiguration.md @@ -958,7 +958,9 @@ Das ON4KST-Passwort steht wie bisher im Klartext in der `preferences.xml` des je ## Dark Mode (ab v1.26) -Der Dark Mode wird über **Windows → Use dark mode design** aktiviert. Mit **Windows → Use default mode design** wird wieder das normale helle Farbschema geladen. +Mit welchem Farbschema KST4Contest startet, legt jedes Operator-Profil selbst fest: im Reiter **GUI** unter **Design** mit **Light mode** oder **Dark mode**. Die Auswahl wirkt sofort und wird mit **Save Settings** in der `preferences.xml` des Profils gespeichert. + +Für einen kurzen Wechsel während des Betriebs gibt es **Windows → Use dark mode design** und **Windows → Use default mode design**. Diese Umschaltung gilt nur bis zum nächsten Start oder Profilwechsel; danach gilt wieder die Einstellung im Reiter **GUI**. --- diff --git a/github_docs/en-Changelog.md b/github_docs/en-Changelog.md index ae841fcb..3f267d29 100644 --- a/github_docs/en-Changelog.md +++ b/github_docs/en-Changelog.md @@ -30,8 +30,14 @@ Several operators sharing one computer can now use their own callsigns, locators - **The default login callsign is empty:** if `preferences.xml` has no login callsign, the field now stays empty instead of falling back to a callsign compiled into the program. A newly created profile therefore deliberately starts without credentials. +- **Dark mode per profile:** on the **GUI** tab each operator profile sets under **Design** whether KST4Contest starts light or dark. The entries in the **Windows** menu still switch quickly, but only for the running session. The colour scheme now also covers the update window and the top candidates list. + ### Fixed +- **Consistent font size:** on macOS and Linux JavaFX uses a slightly larger default font than on Windows, which cut off labels in fixed-size controls. KST4Contest now uses the same base font size on all systems; nothing changes on Windows. + +- **Dark mode after a restart:** the main window always started light, even if dark mode had been active, while the station map stayed dark. All windows now start consistently with the profile's colour scheme. + - **Background resources are released:** the ON4KST supervisor thread, the sked reminder scheduler, the reachability executor, the PSTRotator retry scheduler and the map tile proxy are released when the chat controller is closed. They used to keep running until the program ended. ### Upgrade notes diff --git a/github_docs/en-Configuration.md b/github_docs/en-Configuration.md index 4e735361..fa90c9db 100644 --- a/github_docs/en-Configuration.md +++ b/github_docs/en-Configuration.md @@ -1015,7 +1015,9 @@ As before, the ON4KST password is stored in clear text in the `preferences.xml` ## Dark Mode (from v1.26) -Enable Dark Mode through **Windows → Use dark mode design**. Use **Windows → Use default mode design** to restore the normal light colour scheme. +Each operator profile sets its own startup colour scheme: on the **GUI** tab under **Design**, choose **Light mode** or **Dark mode**. The choice takes effect immediately and is stored in the profile's `preferences.xml` with **Save Settings**. + +For a quick change during operation, use **Windows → Use dark mode design** and **Windows → Use default mode design**. This switch only lasts until the next start or profile switch; after that the setting on the **GUI** tab applies again. --- diff --git a/github_docs/en-Features.md b/github_docs/en-Features.md index 6ef74616..e8ea7e35 100644 --- a/github_docs/en-Features.md +++ b/github_docs/en-Features.md @@ -317,7 +317,7 @@ KST4Contest signs in to ON4KST once with one local login callsign and password. ## Dark Mode (from v1.26) -Enable it through **Windows → Use dark mode design**. Use **Windows → Use default mode design** to return to the normal light colour scheme. +Each operator profile sets its startup colour scheme on the **GUI** tab under **Design**. **Windows → Use dark mode design** and **Windows → Use default mode design** switch quickly for the running session. The green private-message age scale remains available in both designs. Text colour, normal table colour and the separate highlight for locally sent messages follow the selected built-in design. diff --git a/github_docs/en-User-Interface.md b/github_docs/en-User-Interface.md index d69ff8e7..7feda25b 100644 --- a/github_docs/en-User-Interface.md +++ b/github_docs/en-User-Interface.md @@ -486,8 +486,9 @@ Functions which communicate with the server are available only after the ON4KST - **Hide cluster / stranger QSOs** and **Show cluster / stranger QSOs** hide or restore the separate cluster and QSO monitor window. - **hide options** and **show options** hide or restore the settings window. -- **Use dark mode design** activates the dark colour scheme. -- **Use default mode design** restores the standard light colour scheme. +- **Use dark mode design** activates the dark colour scheme for the running session. +- **Use default mode design** restores the standard light colour scheme for the running session. + The startup colour scheme is set per profile on the **GUI** settings tab under **Design**. - **Show / hide station map** opens or closes the separate station-map and path-analysis window. --- diff --git a/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java index 47ed5545..6804cf55 100644 --- a/src/main/java/kst4contest/view/Kst4ContestApplication.java +++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java @@ -133,6 +133,9 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL private On4KstConnectionState lastDisplayedConnectionState; private String lastDisplayedConnectionDetail = ""; + /** Scenes whose stylesheet follows the light/dark design switch. */ + private final List themedScenes = new ArrayList<>(); + private final Button btnBandUpgradeIndicator = new Button("BAND+"); private final Tooltip tipBandUpgradeIndicator = new Tooltip(); private Timeline bandUpgradeBlinkTimeline; @@ -4589,7 +4592,10 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL root.setCenter(listView); root.setBottom(new Label("Double-click a candidate to select it.")); - stage.setScene(new Scene(root, 360, 500)); + Scene candidatesScene = new Scene(root, 360, 500); + registerThemedScene(candidatesScene); + stage.setOnHidden(event -> themedScenes.remove(candidatesScene)); + stage.setScene(candidatesScene); stage.show(); } @@ -5621,21 +5627,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL window30.setOnAction(new EventHandler() { public void handle(ActionEvent event) { - System.out.println("KST4ContestApp, info: switching to dark mode"); - - scn_ChatwindowMainScene.getStylesheets().clear(); - clusterAndQSOMonScene.getStylesheets().clear(); - settingsScene.getStylesheets().clear(); - setUserAgentStylesheet(null); - - scn_ChatwindowMainScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_EVENING); - clusterAndQSOMonScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_EVENING); - settingsScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_EVENING); - - chatcontroller.getChatPreferences().setGUI_darkModeActive(true); - if (stationMapBridge != null) { - stationMapBridge.applyThemeFromPreferences(); //dark mode for the map - } + // quick switch for this session only, the startup design is set per profile in the GUI options + applyTheme(true); } }); @@ -5643,21 +5636,8 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL window40.setOnAction(new EventHandler() { public void handle(ActionEvent event) { - System.out.println("KST4ContestApp, info: switching to default mode"); - - scn_ChatwindowMainScene.getStylesheets().clear(); - clusterAndQSOMonScene.getStylesheets().clear(); - settingsScene.getStylesheets().clear(); - setUserAgentStylesheet(null); - - scn_ChatwindowMainScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_DAYLIGHT); - clusterAndQSOMonScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_DAYLIGHT); - settingsScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_DAYLIGHT); - chatcontroller.getChatPreferences().setGUI_darkModeActive(false); - - if (stationMapBridge != null) { - stationMapBridge.applyThemeFromPreferences(); - } + // quick switch for this session only, the startup design is set per profile in the GUI options + applyTheme(false); } }); @@ -5779,6 +5759,43 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL return menubar; } + /** + * Registers a scene whose stylesheet follows the light/dark design switch and + * applies the currently active design to it. + */ + private void registerThemedScene(Scene scene) { + if (scene == null) { + return; + } + themedScenes.add(scene); + applyThemeStylesheet(scene, chatcontroller.getChatPreferences().isGUI_darkModeActive()); + } + + /** + * Switches all registered windows and the station map to the dark or light design + * for the running session. The per-profile startup design is not changed here. + */ + private void applyTheme(boolean darkMode) { + LOGGER.info("Switching GUI design to " + (darkMode ? "dark" : "light") + " mode"); + + chatcontroller.getChatPreferences().setGUI_darkModeActive(darkMode); + setUserAgentStylesheet(null); + + for (Scene scene : themedScenes) { + applyThemeStylesheet(scene, darkMode); + } + + if (stationMapBridge != null) { + stationMapBridge.applyThemeFromPreferences(); + } + } + + private static void applyThemeStylesheet(Scene scene, boolean darkMode) { + scene.getStylesheets().setAll(darkMode + ? ApplicationConstants.STYLECSSFILE_DEFAULT_EVENING + : ApplicationConstants.STYLECSSFILE_DEFAULT_DAYLIGHT); + } + /***************************************************** * Sked warning Initializing and functional section @@ -6979,6 +6996,9 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL setDebugFileLoggingEnabled(chatcontroller.getChatPreferences() .isMessageHandling_debugModeToFileEnabled()); layoutAutosave = new LayoutAutosave(chatcontroller.getChatPreferences()); + // Every profile starts with its own configured design; the menu quick switch only lasts for the session. + chatcontroller.getChatPreferences().setGUI_darkModeActive( + chatcontroller.getChatPreferences().isGUI_darkModeActiveByDefault()); messageVariableResolver = new MessageVariableResolver(chatcontroller.getChatPreferences()); chatcontroller.setStatusListener(this); //callback interface for updating Thread events in visual @@ -7113,7 +7133,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL screenAwareMainSceneSizeHW[0] ); - scn_ChatwindowMainScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_DAYLIGHT); + registerThemedScene(scn_ChatwindowMainScene); @@ -9127,7 +9147,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL clusterAndQSOMonScene = new Scene(pnl_directedMSGWin, chatcontroller.getChatPreferences().getGUIclusterAndQSOMonStage_SceneSizeHW()[0], chatcontroller.getChatPreferences().getGUIclusterAndQSOMonStage_SceneSizeHW()[1]); - clusterAndQSOMonScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_DAYLIGHT); + registerThemedScene(clusterAndQSOMonScene); clusterAndQSOMonScene.heightProperty().addListener(new ChangeListener() { @Override @@ -9255,6 +9275,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL System.out.println("SRVR Version: " + chatcontroller.getUpdateInformation().getLatestVersionNumberOnServer() + " // installed version " + ApplicationConstants.APPLICATION_CURRENTVERSIONNUMBER); stage_updateStage.setScene(new Scene(vbxUpdateWindow, chatcontroller.getChatPreferences().getGUIstage_updateStage_SceneSizeHW()[0], chatcontroller.getChatPreferences().getGUIstage_updateStage_SceneSizeHW()[1])); + registerThemedScene(stage_updateStage.getScene()); stage_updateStage.getScene().widthProperty().addListener((observable, oldValue, newValue) -> { chatcontroller.getChatPreferences().getGUIstage_updateStage_SceneSizeHW()[0] = newValue.doubleValue(); requestLayoutSave(); @@ -11973,6 +11994,33 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL grdPnlGuiOptions.add(lblShowFreshCallHint, 0, 6); grdPnlGuiOptions.add(chkBxShowFreshCallHint, 1, 6); + // Startup design of this operator profile; the Windows menu only switches for the session. + grdPnlGuiOptions.add(generateLabeledSeparator(100, "Design"), 0, 7, 2, 1); + + ToggleGroup guiOptions_tglGrpDesign = new ToggleGroup(); + RadioButton designLightModeRB = new RadioButton("Light mode "); + designLightModeRB.setToggleGroup(guiOptions_tglGrpDesign); + RadioButton designDarkModeRB = new RadioButton("Dark mode "); + designDarkModeRB.setToggleGroup(guiOptions_tglGrpDesign); + + if (chatcontroller.getChatPreferences().isGUI_darkModeActiveByDefault()) { + designDarkModeRB.setSelected(true); + } else { + designLightModeRB.setSelected(true); + } + + guiOptions_tglGrpDesign.selectedToggleProperty().addListener((obs, oldToggle, newToggle) -> { + if (newToggle == null) { + return; + } + boolean darkMode = newToggle == designDarkModeRB; + chatcontroller.getChatPreferences().setGUI_darkModeActiveByDefault(darkMode); + applyTheme(darkMode); + }); + + grdPnlGuiOptions.add(new Label("Design at startup of this profile:"), 0, 8); + grdPnlGuiOptions.add(new HBox(designLightModeRB, designDarkModeRB), 1, 8); + @@ -12289,7 +12337,7 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL // VBox vBox = new VBox(tabPaneOptions); settingsScene = new Scene(optionsPanel, chatcontroller.getChatPreferences().getGUIsettingsStageSceneSizeHW()[0], chatcontroller.getChatPreferences().getGUIsettingsStageSceneSizeHW()[1]); - settingsScene.getStylesheets().add(ApplicationConstants.STYLECSSFILE_DEFAULT_DAYLIGHT); + registerThemedScene(settingsScene); settingsScene.widthProperty().addListener((observable, oldValue, newValue) -> { chatcontroller.getChatPreferences().getGUIsettingsStageSceneSizeHW()[0] = newValue.doubleValue(); requestLayoutSave(); diff --git a/src/main/java/kst4contest/view/OperatorProfilePickerDialog.java b/src/main/java/kst4contest/view/OperatorProfilePickerDialog.java index d9710159..42f1d921 100644 --- a/src/main/java/kst4contest/view/OperatorProfilePickerDialog.java +++ b/src/main/java/kst4contest/view/OperatorProfilePickerDialog.java @@ -90,6 +90,8 @@ public final class OperatorProfilePickerDialog { profileListView, buttonRow); dialogContent.setPadding(new Insets(15)); + // No application stylesheet yet: use the same base font size as the main windows. + dialogContent.setStyle("-fx-font-size: 12px;"); dialogStage.setScene(new Scene(dialogContent, 380, 280)); profileListView.requestFocus(); diff --git a/src/main/resources/KST4ContestDefaultDay.css b/src/main/resources/KST4ContestDefaultDay.css index cbe2ba01..1b277b0b 100644 --- a/src/main/resources/KST4ContestDefaultDay.css +++ b/src/main/resources/KST4ContestDefaultDay.css @@ -1,3 +1,8 @@ +.root { + /* Same base font size on all platforms: macOS/Linux default to 13px, Windows to 12px. */ + -fx-font-size: 12px; +} + .button:pressed { -fx-border-color: #ff0000; } diff --git a/src/main/resources/KST4ContestDefaultEvening.css b/src/main/resources/KST4ContestDefaultEvening.css index 14419f6d..460151e6 100644 --- a/src/main/resources/KST4ContestDefaultEvening.css +++ b/src/main/resources/KST4ContestDefaultEvening.css @@ -1,4 +1,6 @@ .root { + /* Same base font size on all platforms: macOS/Linux default to 13px, Windows to 12px. */ + -fx-font-size: 12px; -fx-accent: #1e74c6; -fx-focus-color: -fx-accent; -fx-base: #373e43;