Compare commits

...
2 Commits
Author SHA1 Message Date
Rsclub2_2andClaude Opus 5.5 3dd2cf1091 Keep the map drawing test off the network and the user's tile cache
The test assumed no tile ever arrives, but it used the default TileFetcher,
which reads the real disk cache and the OSM tile server. On CI a tile now and
then landed before the frame was rendered and covered the background the
'unreachable tile source' test asserts on. An empty temporary cache and a
source that always fails make the assumption hold by construction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:18:35 +02:00
Rsclub2_2andClaude Opus 5.5 2125137709 Fix Codex review findings on PR 89: station row key, map selection, sort after filter
- Station row key now includes the chat category, so the same callsign in two
  categories no longer collides.
- Map marker click and Reset view go through the central station selection.
- Sorting now operates on the filtered rows, so filters stay effective.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:11:01 +02:00
5 changed files with 47 additions and 9 deletions

No files matched your search

@@ -3034,7 +3034,8 @@ public class Kst4ContestApplication implements StatusUpdateListener, SettingsHos
member -> parseTableDouble(formatPriorityScore(member)),
(member, band, worked) -> formatBandCellStatus(member, band, worked),
BandOpportunityResolver.getEnabledStationBands(chatcontroller.getChatPreferences())),
RowKeys.INSTANCE.byValue(ChatMember::getCallSign),
RowKeys.INSTANCE.byValue(member -> member.getCallSign() + "|"
+ (member.getChatCategory() == null ? "" : member.getChatCategory().getCategoryNumber())),
"compose-stations",
widths);
@@ -3222,6 +3223,11 @@ public class Kst4ContestApplication implements StatusUpdateListener, SettingsHos
composeMemberListener = rows -> uiDispatcher.runOnUi(() -> stations.replaceRows(rows));
chatcontroller.getLst_chatMemberList().addListener(composeMemberListener);
composeMainWindowState.setStationSelector(member -> {
selectStationInCompose(selectedStation, stations, member);
return kotlin.Unit.INSTANCE;
});
composeChatListener = rows -> uiDispatcher.runOnUi(() -> {
directed.replaceRows(chatcontroller.toMeMessages());
publicMessages.replaceRows(rows);
@@ -318,12 +318,12 @@ class DataTableState<T>(
* rows jump.
*/
val order = column.comparator
?: compareBy(currentRows.associateWith { column.value(it) }::getValue)
?: compareBy(filteredRows.associateWith { column.value(it) }::getValue)
if (ascending) {
currentRows.sortedWith(order)
filteredRows.sortedWith(order)
} else {
currentRows.sortedWith(order.reversed())
filteredRows.sortedWith(order.reversed())
}
}
@@ -62,6 +62,13 @@ class MainWindowState(
) {
/** The shortcut buttons above the send line. */
/**
* The one path that selects a station for the whole application (panel, table, score
* service, send line). Set by the host after construction; other windows, such as the
* map, go through it instead of touching [selectedStation] alone. Null clears.
*/
var stationSelector: ((ChatMember?) -> Unit)? = null
val shortcuts = RosterMirror(prefs.getLst_txtShortCutBtnList())
/** The snippets behind Ctrl+1..Ctrl+0 and the right-click menus. */
@@ -217,7 +217,8 @@ object StationMapWindow {
}
kst4contest.view.compose.Form.button(strings.mapResetView) {
mainWindowState.selectedStation.select(null)
mainWindowState.stationSelector?.invoke(null)
?: mainWindowState.selectedStation.select(null)
val home = runCatching {
kst4contest.locatorUtils.Location(ownLocator6)
}.getOrNull()
@@ -277,10 +278,22 @@ object StationMapWindow {
darkMode = isDarkTheme,
onMarkerClicked = { snapshot ->
val chatController = ApplicationRuntimeLauncher.getCurrent()?.chatController
val member = chatController?.lst_chatMemberList?.snapshot()?.find { it.callSignRaw == snapshot.callSignRaw() }
val variants = chatController?.lst_chatMemberList?.snapshot()
?.filter { it.callSignRaw == snapshot.callSignRaw() }
.orEmpty()
/*
* A marker stands for a raw callsign, not a category. Keep the
* category already selected when it is one of the variants.
*/
val member = variants.firstOrNull { it === mainWindowState.selectedStation.selected }
?: variants.firstOrNull()
if (member != null) {
mainWindowState.selectedStation.select(member)
val selector = mainWindowState.stationSelector
if (selector != null) {
selector(member)
} else {
mainWindowState.selectedStation.select(member)
}
// Mirror old JavaFX handleMapCallsignSelection logic: prefill /cq
mainWindowState.chatInput.prepareCq(member.callSign ?: "", forceOverwrite = true)
}
@@ -11,6 +11,8 @@ import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.awt.image.BufferedImage
import java.io.ByteArrayInputStream
import java.io.IOException
import java.nio.file.Files
import javax.imageio.ImageIO
import kotlin.math.abs
@@ -42,6 +44,16 @@ class ComposeStationMapDrawingTest {
private val darkMapBackground = 0x23282D
/*
* An empty cache and a source that always fails, so that "tiles never arrive" holds by
* construction. The default fetcher read the user's disk cache and the real tile
* server, and on CI a tile now and then made it in before the frame was rendered.
*/
private val offlineTileFetcher = TileFetcher(
Files.createTempDirectory("kst4contest-tile-cache-test"),
{ throw IOException("no tile source in this test") },
)
private fun station(call: String, locator: String, lat: Double, lon: Double) =
MapCallsignRawSnapshot(
call, call, locator, lat, lon, "", emptyMap(),
@@ -56,7 +68,7 @@ class ComposeStationMapDrawingTest {
val scene = ImageComposeScene(canvasWidth, canvasHeight, Density(1f)) {
ComposeStationMap(
markers = markers,
tileFetcher = TileFetcher(),
tileFetcher = offlineTileFetcher,
mapState = MapState(zoom = zoom, centerLon = 10.0, centerLat = 51.0),
groupingEnabled = false,
selectedMarkerCallsign = null,