manual and code: fixed colour handling of the messages and better descripted it in the manual

This commit is contained in:
Marc Froehlich
2026-08-27 00:02:02 +02:00
parent b280f1b0db
commit 3ec6cab46a
7 changed files with 217 additions and 59 deletions
+17 -3
View File
@@ -239,7 +239,21 @@ Bedienung und Aufbau der Filterleiste: [Benutzeroberfläche Filter](de-Benut
## Farbige PM-Zeilen (ab v1.25) ## 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!)* *(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) ## 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.
--- ---
+1 -1
View File
@@ -871,7 +871,7 @@ Anzeige und Herleitung: [Gearbeitete Rufzeichen, neue Bänder und neue Großfeld
## Dark Mode (ab v1.26) ## 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.
--- ---
+1 -1
View File
@@ -926,7 +926,7 @@ Display and derivation: [Worked Callsigns, New Bands and New Grid Squares](en-Fe
## Dark Mode (from v1.26) ## 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.
--- ---
+17 -3
View File
@@ -238,7 +238,21 @@ Operation and layout of the filter bar: [User Interface Filters](en-User-Int
## Coloured PM Rows (from v1.25) ## 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!)* *(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) ## 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.
--- ---
@@ -25,7 +25,6 @@ import javafx.scene.control.TableRow; // For the priority coloring
import javafx.animation.PauseTransition; import javafx.animation.PauseTransition;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.css.PseudoClass;
import javafx.geometry.*; import javafx.geometry.*;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.input.*; 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<ChatMessage>() { tbl_privateMSGTable.setRowFactory(tv -> new TableRow<ChatMessage>() {
@Override @Override
protected void updateItem(ChatMessage item, boolean empty) { protected void updateItem(
final ChatMessage item,
final boolean empty
) {
super.updateItem(item, empty); super.updateItem(item, empty);
try {
if (item != null) {
if (item.getSender().getCallSign().equals(chatcontroller.getChatPreferences().getStn_loginCallSign())) { getStyleClass().removeAll(
PseudoClass foo = PseudoClass.getPseudoClass("messageHighlightOwn-column"); PrivateMessageRowStyleResolver.knownStyleClasses()
);
// System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> pm row style " + this.getStyleClass()); if (empty || item == null || item.getSender() == null) {
tv.setStyle(null); return;
}
// this.getStyleClass().clear(); final String ownCallsign = chatcontroller
this.getStyleClass().add("messageHighlightOwn-column"); //add new special colored css reference .getChatPreferences()
// setStyle("-fx-background-color: #ADD8E6;"); .getStn_loginCallSign();
} else {
// 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 final String styleClass;
// setStyle("-fx-background-color: #FF6F00;");
this.getStyleClass().clear();
this.getStyleClass().add("messageHighlight30-column"); //add new special colored css reference
} else if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 60 ) { //after 60 seconds change color if (ownMessage) {
this.getStyleClass().clear(); styleClass = PrivateMessageRowStyleResolver
this.getStyleClass().add("messageHighlight60-column"); //add new special colored css reference .resolveStyleClass(true, 0);
// setStyle("-fx-background-color: #FFB300;"); } else {
} else if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 90 ) { //after 90 seconds change color try {
this.getStyleClass().clear(); final long ageSeconds = new Utils4KST()
this.getStyleClass().add("messageHighlight90-column"); //add new special colored css reference .time_generateCurrentEpochTime()
// setStyle("-fx-background-color: #FFB300;"); - Long.parseLong(
} else if (( (new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())) <= 120 ) { //after 120 seconds change color item.getMessageGeneratedTime()
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
{
// setStyle(""); styleClass = PrivateMessageRowStyleResolver
} .resolveStyleClass(false, ageSeconds);
} } catch (NumberFormatException exception) {
return;
// 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
} }
}
// System.out.println("---> messagealter ---> " + (((new Utils4KST().time_generateCurrentEpochTime())) - (Long.parseLong(item.getMessageGeneratedTime())))); if (styleClass != null) {
} catch (Exception e) { getStyleClass().add(styleClass);
;
} }
} }
@@ -12952,4 +12938,4 @@ class CheckBoxTableCell<S, T> extends TableCell<S, T> {
} }
@@ -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<Long> AGE_LIMITS = List.of(
30L,
60L,
90L,
120L,
180L,
300L
);
/** Style classes corresponding to the configured age bounds. */
private static final List<String> 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<String> 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<String> 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;
}
}
@@ -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
)
);
}
}