From 3ec6cab46a680f199868f84286d7d026058fe7bc Mon Sep 17 00:00:00 2001 From: Marc Froehlich Date: Thu, 27 Aug 2026 00:02:02 +0200 Subject: [PATCH] manual and code: fixed colour handling of the messages and better descripted it in the manual --- github_docs/de-Funktionen.md | 20 ++++- github_docs/de-Konfiguration.md | 2 +- github_docs/en-Configuration.md | 2 +- github_docs/en-Features.md | 20 ++++- .../view/Kst4ContestApplication.java | 88 ++++++++----------- .../view/PrivateMessageRowStyleResolver.java | 83 +++++++++++++++++ .../PrivateMessageRowStyleResolverTest.java | 61 +++++++++++++ 7 files changed, 217 insertions(+), 59 deletions(-) create mode 100644 src/main/java/kst4contest/view/PrivateMessageRowStyleResolver.java create mode 100644 src/test/java/kst4contest/test/PrivateMessageRowStyleResolverTest.java diff --git a/github_docs/de-Funktionen.md b/github_docs/de-Funktionen.md index bcabc11c..33feec88 100644 --- a/github_docs/de-Funktionen.md +++ b/github_docs/de-Funktionen.md @@ -239,7 +239,21 @@ Bedienung und Aufbau der Filterleiste: [Benutzeroberfläche – Filter](de-Benut ## Farbige PM-Zeilen (ab v1.25) -Neue Privatnachrichten erscheinen in **Rot**. Die Farbe wechselt alle 30 Sekunden über Gelb bis Weiß – wie ein Regenbogen-Fade. So ist auf einen Blick erkennbar, wie aktuell eine Nachricht ist. +Neue eingehende Privatnachrichten werden in mehreren grünen Altersstufen hervorgehoben. Mit zunehmendem Alter wird das Grün schrittweise gedämpfter: + +| Alter der Nachricht | Darstellung | +|---|---| +| bis einschließlich 30 Sekunden | erste grüne Stufe | +| 31 bis 60 Sekunden | zweite grüne Stufe | +| 61 bis 90 Sekunden | dritte grüne Stufe | +| 91 bis 120 Sekunden | vierte grüne Stufe | +| 121 bis 180 Sekunden | fünfte grüne Stufe | +| 181 bis 300 Sekunden | sechste grüne Stufe | +| ab 301 Sekunden | normale Tabellenfarbe | + +Die Tabelle aktualisiert die Altersdarstellung alle fünf Sekunden. Ein Grenzübergang kann deshalb erst beim nächsten Aktualisierungslauf sichtbar werden. Nach fünf Minuten bleibt keine Altersklasse an der Zeile haften; auch wiederverwendete oder leere Tabellenzeilen kehren zu ihrem normalen Stil zurück. + +Eigene Nachrichten erhalten weiterhin eine separate Hervorhebung und verwenden nicht die grüne Altersskala. *(Idee von IU3OAR, Gianluca Costantino – danke!)* @@ -286,9 +300,9 @@ Gleichzeitiger Login in **zwei Chat-Kategorien** (z. B. 144 MHz und 432 MHz). Be ## Dark Mode (ab v1.26) -Aktivierbar über: **Window → Use Dark Mode** +Aktivierbar über **Windows → Use dark mode design**. Mit **Windows → Use default mode design** wird wieder auf das normale helle Farbschema umgeschaltet. -Für individuelle Farbanpassungen: CSS-Datei bearbeiten (Pfad in den Programmunterlagen). +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 7fc84b74..723b73a7 100644 --- a/github_docs/de-Konfiguration.md +++ b/github_docs/de-Konfiguration.md @@ -871,7 +871,7 @@ Anzeige und Herleitung: [Gearbeitete Rufzeichen, neue Bänder und neue Großfeld ## Dark Mode (ab v1.26) -Umschaltbar über das Menü: **Window → Use Dark Mode**. Die Farben können über CSS individuell angepasst werden. +Der Dark Mode wird über **Windows → Use dark mode design** aktiviert. Mit **Windows → Use default mode design** wird wieder das normale helle Farbschema geladen. --- diff --git a/github_docs/en-Configuration.md b/github_docs/en-Configuration.md index a2e56f62..32235e03 100644 --- a/github_docs/en-Configuration.md +++ b/github_docs/en-Configuration.md @@ -926,7 +926,7 @@ Display and derivation: [Worked Callsigns, New Bands and New Grid Squares](en-Fe ## Dark Mode (from v1.26) -Toggle via the menu: **Window → Use Dark Mode**. The colors can be individually customized via CSS. +Enable Dark Mode through **Windows → Use dark mode design**. Use **Windows → Use default mode design** to restore the normal light colour scheme. --- diff --git a/github_docs/en-Features.md b/github_docs/en-Features.md index debcca2d..e94ad044 100644 --- a/github_docs/en-Features.md +++ b/github_docs/en-Features.md @@ -238,7 +238,21 @@ Operation and layout of the filter bar: [User Interface – Filters](en-User-Int ## Coloured PM Rows (from v1.25) -New private messages appear in **red**. The colour fades every 30 seconds from yellow to white – like a rainbow fade. This makes it immediately clear how recent a message is. +New incoming private messages use a series of green age highlights. The green becomes progressively more muted as the message gets older: + +| Message age | Display | +|---|---| +| up to and including 30 seconds | first green level | +| 31 to 60 seconds | second green level | +| 61 to 90 seconds | third green level | +| 91 to 120 seconds | fourth green level | +| 121 to 180 seconds | fifth green level | +| 181 to 300 seconds | sixth green level | +| from 301 seconds | normal table colour | + +The table refreshes the age display every five seconds, so a boundary may become visible only during the next refresh. No age class remains attached after five minutes; reused and empty table rows also return to their normal style. + +Messages sent by the local station retain their separate highlight and do not use the green age scale. *(Idea by IU3OAR, Gianluca Costantino – thank you!)* @@ -285,9 +299,9 @@ Simultaneous login to **two chat categories** (e.g. 144 MHz and 432 MHz). Both c ## Dark Mode (from v1.26) -Toggle via: **Window → Use Dark Mode** +Enable it through **Windows → Use dark mode design**. Use **Windows → Use default mode design** to return to the normal light colour scheme. -For individual colour adjustments: edit the CSS file (path in the program settings). +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/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java index 07faa802..4d95b911 100644 --- a/src/main/java/kst4contest/view/Kst4ContestApplication.java +++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java @@ -25,7 +25,6 @@ import javafx.scene.control.TableRow; // For the priority coloring import javafx.animation.PauseTransition; import javafx.beans.binding.Bindings; -import javafx.css.PseudoClass; import javafx.geometry.*; import javafx.scene.control.*; import javafx.scene.input.*; @@ -3754,67 +3753,54 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL } }); - //experimental row coloring on new private messages (and recolouring if they get older) + // Color new private messages and restore the normal row style after five minutes. tbl_privateMSGTable.setRowFactory(tv -> new TableRow() { @Override - protected void updateItem(ChatMessage item, boolean empty) { + protected void updateItem( + final ChatMessage item, + final boolean empty + ) { super.updateItem(item, empty); - try { - if (item != null) { - if (item.getSender().getCallSign().equals(chatcontroller.getChatPreferences().getStn_loginCallSign())) { - PseudoClass foo = PseudoClass.getPseudoClass("messageHighlightOwn-column"); + getStyleClass().removeAll( + PrivateMessageRowStyleResolver.knownStyleClasses() + ); -// System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> pm row style " + this.getStyleClass()); - tv.setStyle(null); + if (empty || item == null || item.getSender() == null) { + return; + } -// this.getStyleClass().clear(); - this.getStyleClass().add("messageHighlightOwn-column"); //add new special colored css reference -// setStyle("-fx-background-color: #ADD8E6;"); - } else { + final String ownCallsign = chatcontroller + .getChatPreferences() + .getStn_loginCallSign(); -// System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> pm row style " + this.getStyleClass()); + final boolean ownMessage = Objects.equals( + item.getSender().getCallSign(), + ownCallsign + ); - if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 30 ) { //after 30 seconds change color -// setStyle("-fx-background-color: #FF6F00;"); - this.getStyleClass().clear(); - this.getStyleClass().add("messageHighlight30-column"); //add new special colored css reference + final String styleClass; - } else if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 60 ) { //after 60 seconds change color - this.getStyleClass().clear(); - this.getStyleClass().add("messageHighlight60-column"); //add new special colored css reference -// setStyle("-fx-background-color: #FFB300;"); - } else if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 90 ) { //after 90 seconds change color - this.getStyleClass().clear(); - this.getStyleClass().add("messageHighlight90-column"); //add new special colored css reference -// setStyle("-fx-background-color: #FFB300;"); - } else if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 120 ) { //after 120 seconds change color - this.getStyleClass().clear(); - this.getStyleClass().add("messageHighlight120-column"); //add new special colored css reference -// setStyle("-fx-background-color: #FFD54F;"); - } else if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 180 ) { //after 180 seconds change color - this.getStyleClass().clear(); - this.getStyleClass().add("messageHighlight180-column"); //add new special colored css reference -// setStyle("-fx-background-color: #FFD54F;"); - } else if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 300 ) { //after 300 seconds change color - this.getStyleClass().clear(); - this.getStyleClass().add("messageHighlight300-column"); //add new special colored css reference -// setStyle("-fx-background-color: #FFF176;"); - } else - { + if (ownMessage) { + styleClass = PrivateMessageRowStyleResolver + .resolveStyleClass(true, 0); + } else { + try { + final long ageSeconds = new Utils4KST() + .time_generateCurrentEpochTime() + - Long.parseLong( + item.getMessageGeneratedTime() + ); -// setStyle(""); - } - } - -// switch (Integer.parseInt("" + (((new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime()))))) { -// case int i -// } //TODO: update to JDK21 or bigger, then a range case is possible, improves speed maybe + styleClass = PrivateMessageRowStyleResolver + .resolveStyleClass(false, ageSeconds); + } catch (NumberFormatException exception) { + return; } + } -// System.out.println("---> messagealter ---> " + (((new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())))); - } catch (Exception e) { - ; + if (styleClass != null) { + getStyleClass().add(styleClass); } } @@ -12952,4 +12938,4 @@ class CheckBoxTableCell extends TableCell { -} \ No newline at end of file +} diff --git a/src/main/java/kst4contest/view/PrivateMessageRowStyleResolver.java b/src/main/java/kst4contest/view/PrivateMessageRowStyleResolver.java new file mode 100644 index 00000000..3a4008fe --- /dev/null +++ b/src/main/java/kst4contest/view/PrivateMessageRowStyleResolver.java @@ -0,0 +1,83 @@ +package kst4contest.view; + +import java.util.List; + +/** + * Selects the CSS style class used for a private-message table row. + */ +public final class PrivateMessageRowStyleResolver { + + /** Style used for messages sent by the local station. */ + public static final String OWN_STYLE_CLASS = + "messageHighlightOwn-column"; + + /** Upper inclusive age bounds for the private-message color levels. */ + private static final List AGE_LIMITS = List.of( + 30L, + 60L, + 90L, + 120L, + 180L, + 300L + ); + + /** Style classes corresponding to the configured age bounds. */ + private static final List AGE_STYLES = List.of( + "messageHighlight30-column", + "messageHighlight60-column", + "messageHighlight90-column", + "messageHighlight120-column", + "messageHighlight180-column", + "messageHighlight300-column" + ); + + /** All private-message row classes managed by the row factory. */ + private static final List MANAGED_STYLES = List.of( + OWN_STYLE_CLASS, + AGE_STYLES.get(0), + AGE_STYLES.get(1), + AGE_STYLES.get(2), + AGE_STYLES.get(3), + AGE_STYLES.get(4), + AGE_STYLES.get(5) + ); + + private PrivateMessageRowStyleResolver() { + } + + /** + * Returns the complete set of private-message row classes managed by the + * row factory. + * + * @return immutable list of managed style classes + */ + public static List knownStyleClasses() { + return MANAGED_STYLES; + } + + /** + * Selects the private-message row class for the supplied message age. + * + * @param ownMessage whether the message was sent by the local station + * @param ageSeconds message age in seconds + * @return managed CSS class, or {@code null} after the five-minute window + */ + public static String resolveStyleClass( + final boolean ownMessage, + final long ageSeconds + ) { + String styleClass = null; + if (ownMessage) { + styleClass = OWN_STYLE_CLASS; + } else { + for (int index = 0; index < AGE_LIMITS.size(); index++) { + if (ageSeconds <= AGE_LIMITS.get(index)) { + styleClass = AGE_STYLES.get(index); + break; + } + } + } + + return styleClass; + } +} diff --git a/src/test/java/kst4contest/test/PrivateMessageRowStyleResolverTest.java b/src/test/java/kst4contest/test/PrivateMessageRowStyleResolverTest.java new file mode 100644 index 00000000..d3ce009b --- /dev/null +++ b/src/test/java/kst4contest/test/PrivateMessageRowStyleResolverTest.java @@ -0,0 +1,61 @@ +package kst4contest.test; + +import kst4contest.view.PrivateMessageRowStyleResolver; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class PrivateMessageRowStyleResolverTest { + + @ParameterizedTest + @CsvSource({ + "0, messageHighlight30-column", + "30, messageHighlight30-column", + "31, messageHighlight60-column", + "60, messageHighlight60-column", + "61, messageHighlight90-column", + "90, messageHighlight90-column", + "91, messageHighlight120-column", + "120, messageHighlight120-column", + "121, messageHighlight180-column", + "180, messageHighlight180-column", + "181, messageHighlight300-column", + "300, messageHighlight300-column" + }) + void selectsAgeStyleClassAtEveryBoundary( + long ageSeconds, + String expectedStyleClass + ) { + assertEquals( + expectedStyleClass, + PrivateMessageRowStyleResolver.resolveStyleClass( + false, + ageSeconds + ) + ); + } + + @Test + void returnsNoAgeStyleClassAfterFiveMinutes() { + assertNull( + PrivateMessageRowStyleResolver.resolveStyleClass( + false, + 301 + ) + ); + } + + @Test + void keepsOwnMessageStyleAfterFiveMinutes() { + assertEquals( + PrivateMessageRowStyleResolver.OWN_STYLE_CLASS, + PrivateMessageRowStyleResolver.resolveStyleClass( + true, + 301 + ) + ); + } +}