mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-10-18 21:34:03 +02:00
Draw the whole interface in Compose
Etappen 3 to 6a of the migration (129 commits). Every window the operator sees is Compose; the JavaFX ones still exist beside them so the two could be compared against a live session, which is the only way the differences came to light. The toolchain first, then the settings window with its eleven tabs and 72 real preferences; then the multi-column table as a component, and with it the rules it needs -- stable row identity, a selection that survives a refresh, column order the operator can drag, and a repaint when the rows change rather than when the list instance does. Then the update and monitor windows, and the main window: 22 station columns, the filter bar, the selected-station panel, the message tabs, the priority bar, the sked controls, the status indicators and the timeline, assembled behind a splitter because Compose has none. The map stops being a WebView. A Compose canvas projects WebMercator tiles itself, with the clustering in core and an asynchronous fetcher in place of the local tile proxy; derive() becomes a pure Kotlin reimplementation instead of a reflection call into com.sun.javafx. Most of the volume here is parity the operator would notice rather than new behaviour: desktop density instead of Material's phone minimums, colours taken from the JavaFX stylesheets instead of Material's palette, row dividers, band columns, grid-square labels and their colours, the home marker and antenna beam, pinch-to-zoom under Wayland, and layouts that survive a window narrower than the chart they stack. Two lessons are worth more than the diff. A control bound to a value Compose cannot observe writes through and never redraws -- that one shipped repeatedly with a green build, because no test drew anything. And a layout that clips instead of wrapping is worse than one that is merely too narrow: half a word is the worst available outcome.
This commit is contained in:
173 files changed
+26500
-4305
No files matched your search
@@ -1,7 +1,9 @@
|
||||
plugins {
|
||||
java
|
||||
application
|
||||
alias(libs.plugins.javafx)
|
||||
alias(libs.plugins.kotlinJvm)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
}
|
||||
|
||||
javafx {
|
||||
@@ -9,13 +11,20 @@ javafx {
|
||||
modules = listOf("javafx.controls", "javafx.fxml", "javafx.web", "javafx.media")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.openjfx:javafx-swing:${libs.versions.javafx.get()}")
|
||||
// Mirrors the compile-scope entries in pom.xml: 11 test classes live under
|
||||
// src/main/java and need JUnit and Mockito on the main compile classpath.
|
||||
implementation(libs.junit.jupiter.api)
|
||||
implementation(libs.mockito.core.compile)
|
||||
implementation(libs.jetbrains.annotations)
|
||||
implementation(project(":core"))
|
||||
implementation(compose.desktop.currentOs)
|
||||
implementation(compose.material3)
|
||||
implementation(libs.sqlite.jdbc)
|
||||
implementation(libs.jlayer)
|
||||
testImplementation(libs.junit.jupiter)
|
||||
@@ -25,46 +34,77 @@ dependencies {
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("kst4contest.view.Kst4ContestApplication")
|
||||
}
|
||||
compose.desktop {
|
||||
application {
|
||||
// Main and not Kst4ContestApplication: an Application subclass cannot be
|
||||
// started from the classpath, which is where JavaFX lives since Etappe 1.
|
||||
mainClass = "kst4contest.view.Main"
|
||||
|
||||
// Collects the application jar and its runtime dependencies into the layout the
|
||||
// packaging scripts expect: a flat directory with the main jar named app.jar.
|
||||
val collectRuntime by tasks.registering(Sync::class) {
|
||||
dependsOn(tasks.jar)
|
||||
from(tasks.jar) { rename { "app.jar" } }
|
||||
from(configurations.runtimeClasspath)
|
||||
into(layout.buildDirectory.dir("dist-libs"))
|
||||
}
|
||||
nativeDistributions {
|
||||
/*
|
||||
* Only the formats the building machine can actually produce. Compose rejects a
|
||||
* foreign one while CONFIGURING the project, not while packaging it, so a list
|
||||
* naming all five made every Gradle task fail on macOS and Windows — including
|
||||
* `run` and `test`, which package nothing. The build was Linux-only without
|
||||
* anyone having decided that.
|
||||
*/
|
||||
val host = org.gradle.internal.os.OperatingSystem.current()
|
||||
targetFormats(
|
||||
*when {
|
||||
host.isMacOsX -> arrayOf(
|
||||
org.jetbrains.compose.desktop.application.dsl.TargetFormat.Dmg,
|
||||
)
|
||||
host.isWindows -> arrayOf(
|
||||
org.jetbrains.compose.desktop.application.dsl.TargetFormat.Msi,
|
||||
)
|
||||
else -> arrayOf(
|
||||
org.jetbrains.compose.desktop.application.dsl.TargetFormat.AppImage,
|
||||
org.jetbrains.compose.desktop.application.dsl.TargetFormat.Deb,
|
||||
org.jetbrains.compose.desktop.application.dsl.TargetFormat.Rpm,
|
||||
)
|
||||
}
|
||||
)
|
||||
/*
|
||||
* jlink must come from JDK 21, not from whatever the Gradle JVM is.
|
||||
* jdk.jsobject was removed after 21, and this machine has 26 on PATH;
|
||||
* Etappe 1 hit the same wall with jpackage.
|
||||
*/
|
||||
javaHome = javaToolchains.launcherFor {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}.get().metadata.installationPath.asFile.absolutePath
|
||||
|
||||
val packageImage by tasks.registering(Exec::class) {
|
||||
dependsOn(collectRuntime)
|
||||
// jpackage must come from the project toolchain, not from PATH: jdk.jsobject
|
||||
// was removed after JDK 21, and jlink then cannot resolve the module list.
|
||||
val launcher = javaToolchains.launcherFor {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}.get()
|
||||
val jpackageTool = launcher.metadata.installationPath.file("bin/jpackage").asFile.absolutePath
|
||||
val input = layout.buildDirectory.dir("dist-libs").get().asFile
|
||||
val output = layout.buildDirectory.dir("jpackage").get().asFile
|
||||
val os = org.gradle.internal.os.OperatingSystem.current()
|
||||
val icon = when {
|
||||
os.isWindows -> "packaging/icons/kst4contest.ico"
|
||||
os.isMacOsX -> "packaging/icons/kst4contest.icns"
|
||||
else -> "packaging/icons/kst4contest.png"
|
||||
/*
|
||||
* KST4Contest and not praktiKST: this one name drives the app-image
|
||||
* directory, the launcher, the .app bundle, the bundled icon file
|
||||
* and -- lowercased -- the deb/rpm package name. Every packaging
|
||||
* consumer already uses KST4Contest there (AppRun, the Arch and
|
||||
* Flatpak wrappers, the AUR PKGBUILDs, build-signed-dmg.sh), and the
|
||||
* shipped deb/rpm must keep the package identity "kst4contest" so an
|
||||
* installed copy still upgrades.
|
||||
*/
|
||||
packageName = "KST4Contest"
|
||||
packageVersion = providers.gradleProperty("composePackageVersion").get()
|
||||
|
||||
/*
|
||||
* Kept from the former module-info.java, minus the javafx.* modules.
|
||||
* jdk.jsobject carries netscape.javascript for the map bridge, and
|
||||
* jdk.unsupported carries sun.misc.Unsafe, without which the JavaFX
|
||||
* Marlin renderer fails to start. Neither is inferred: JavaFX comes
|
||||
* from the classpath, so nothing declares them.
|
||||
*/
|
||||
modules(
|
||||
"java.desktop", "java.net.http", "java.sql",
|
||||
"jdk.crypto.ec", "jdk.jsobject", "jdk.net",
|
||||
"jdk.xml.dom", "jdk.unsupported",
|
||||
)
|
||||
|
||||
linux { iconFile.set(rootProject.file("packaging/icons/kst4contest.png")) }
|
||||
windows { iconFile.set(rootProject.file("packaging/icons/kst4contest.ico")) }
|
||||
macOS {
|
||||
iconFile.set(rootProject.file("packaging/icons/kst4contest.icns"))
|
||||
// Must stay de.x08.KST4Contest: build-signed-dmg.sh signs against it.
|
||||
bundleID = "de.x08.KST4Contest"
|
||||
}
|
||||
}
|
||||
}
|
||||
doFirst { output.deleteRecursively() }
|
||||
commandLine(
|
||||
jpackageTool,
|
||||
"--icon", rootProject.file(icon).absolutePath,
|
||||
"--type", "app-image",
|
||||
"--name", "praktiKST",
|
||||
"--input", input.absolutePath,
|
||||
"--dest", output.absolutePath,
|
||||
"--main-jar", "app.jar",
|
||||
"--add-modules", providers.gradleProperty("jpackageAddModules").get(),
|
||||
"--main-class", "kst4contest.view.Main",
|
||||
"--java-options", "-Dfile.encoding=UTF-8"
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user