Skip to content

Commit c54c72a

Browse files
committed
refactor: migrate deprecated Kotlin and Compose APIs
1 parent e96b70e commit c54c72a

14 files changed

Lines changed: 78 additions & 18 deletions

File tree

‎docs/DESIGN.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ Panels use restrained `#0C2444 → #08192E` depth, with `#135582` outlines. The
180180
- Use an application-wide Material `Surface` for proper light text over the dark theme. Do not depend on default `Text` content colors after drawing raw backgrounds.
181181
- The main window chooses an initial size constrained by the desktop work area. The instance panel is vertically scrollable, with a visible scrollbar; crucial controls should be visible without scrolling on typical full-HD screens.
182182
- Compact hero typography avoids text collisions. Below 760 dp of content width, stack the hero/Play and setup panel with a visible page scrollbar; otherwise keep Play visible and scroll the setup panel independently. Very short windows scroll the page as well. The minimum native window is 400 × 400. Never make unsupported future features appear as active navigation.
183-
- Optimized JPEG/PNG resources load locally via painterResource without network access, runtime blur, repeated animation or dynamic downloads. Shared vector icons, 18 dp panel radii, 9 dp field radii and typography live in `SyntaxTheme.kt` / `SyntaxComponents.kt`. Account setup details belong behind the Player information action, keeping the normal launch screen concise.
183+
- Optimized JPEG/PNG resources load locally via `rememberBrandingPainter` and the Compose resources bitmap decoder, retaining the packaged classpath paths. Decoding is remembered per resource path and streams are closed after reading; no network access, runtime blur, repeated animation or dynamic downloads. Shared vector icons, 18 dp panel radii, 9 dp field radii and typography live in `SyntaxTheme.kt` / `SyntaxComponents.kt`. Account setup details belong behind the Player information action, keeping the normal launch screen concise.
184184

185185
### Startup update screen
186186

‎docs/DEVELOPMENT.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,10 @@ remain separate roadmap items.
351351

352352
`MinecraftVersionCatalog` uses the shared HTTP client with a 15-second request deadline. Desktop supplies `cacheDir/minecraft/version_manifest_v2.json`; tests may omit the path. Each fetch requests fresh data, validates JSON and catalog consistency, then publishes the raw Mojang document through a unique sibling temporary file and atomic replacement where supported. This is disposable remote metadata, not a launcher settings schema. Invalid/missing caches are never silently treated as an empty catalog. Failed refreshes can use a re-parsed valid cache, cancellation propagates, and cache-write failures are logged without discarding a valid online result. Disk work runs on the IO dispatcher. Deterministic MockEngine tests cover restart reuse, replacement, malformed/HTTP failures, corruption, unavailable storage and cancellation. Full offline launching remains a separate concern.
353353

354+
## Desktop resource and dialog APIs
355+
356+
Brand PNG/JPEG files remain in `src/main/resources/branding` for the native package, classpath tools and existing image-dimension regressions. Use `rememberBrandingPainter` instead of deprecated `androidx.compose.ui.res.painterResource`: the helper remembers a `BitmapPainter` by path, closes the classpath stream and decodes with `org.jetbrains.compose.resources.decodeToImageBitmap`, following [JetBrains’ classpath migration example](https://github.com/JetBrains/compose-multiplatform-core/pull/1457). Native instance/mod windows use `DialogWindow` with the existing dialog state and close guards. Material3 and resources dependencies use explicit coordinates because the old Compose Gradle accessors are deprecated; keep the resources version aligned with the Compose plugin and retain the resolved Material3 version when changing notation.
357+
354358
## Interface localization (EN / PL / DE)
355359

356360
`desktop/i18n/UiStrings` loads UTF-8 `resources/i18n/{en,pl,de}.properties` bundles once. Use stable keys with numbered `{0}` placeholders; format runtime values through `strings.text(key, arguments…)` rather than embedding translated content in core. `LocalStrings` scopes language to the application composition. `LauncherLanguageHost` loads the system/saved choice off the UI thread and publishes a new localizer only after an atomic successful preference save. A save failure leaves the current language active and presents a translated retry explanation. No JVM-wide locale mutation is used.

‎docs/ROADMAP.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Agents and contributors MUST keep this file synchronized with actual repository
1414

1515
## Cross-milestone desktop development ergonomics (October 2026)
1616

17+
- [x] Replace deprecated desktop resource/dialog calls with Compose resource bitmap decoding and `DialogWindow`; align OfflineAccount copy visibility with its private constructor and remove redundant loader null checks. Full local tests/build and Linux package verification pass; native window acceptance remains separate.
1718
- [x] Embed the semantic product version, CI run number and source SHA at build time; show exact CI build identity in the launcher header and updater without altering release-version comparisons. Local builds have an explicit non-CI label.
1819
- [ ] 🚧 IN PROGRESS — Consolidate product SemVer in root `gradle.properties` for Gradle modules, embedded metadata, jpackage numeric version derivation, update bundles, GitHub Actions tags and Build Explorer publication; source and deterministic tests committed, final CI acceptance pending.
1920
- [ ] 🚧 IN PROGRESS — Replace native window decorations with branded controls integrated into the existing Compose header (drag, minimize, maximize/restore, close, 8 resize grips, 400×400 minimum). Source implementation and unit coverage for resize geometry added; real native/manual acceptance on Windows, macOS and Linux X11/Wayland, DPI and small screens remains open.

‎docs/WORKLOG.md‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,45 @@ Every code/build/documentation/CI task must add an entry according to `AGENTS.md
88

99
---
1010

11+
## 2026-10-21 — Migrate deprecated Kotlin and Compose APIs
12+
13+
**Milestone:** v0.1 — existing core/UI maintenance; v0.12 — build compatibility
14+
**Author/Agent:** Codex / GPT-6
15+
**Status:** Completed
16+
17+
### Completed
18+
- Applied `@ConsistentCopyVisibility` to OfflineAccount so the generated copy method respects its private constructor; all repository creation remains through validated `create`.
19+
- Removed redundant Elvis/safe calls where Kotlin already proves that the delayed installer has a selected loader and registry. Loader resolution, preparation order and errors for missing services remain unchanged.
20+
- Replaced deprecated image calls in the main window, header, hero and startup updater with one remembered classpath bitmap helper using the Compose resources decoder. Existing PNG/JPEG paths, artwork, sizing and native package icon remain intact; input streams close after reading.
21+
- Migrated the instance and local-mod native windows from Dialog to DialogWindow, preserving size/state, resize support, busy close guards and content.
22+
- Declared Material3 explicitly at its existing resolved version and Compose resources at the plugin version, avoiding deprecated Gradle dependency accessors. No warning suppressions or product-version bump introduced.
23+
24+
### Changed files
25+
- `launcher-core/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/core/account/OfflineAccount.kt` — consistent generated-copy visibility.
26+
- `launcher-core/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/core/launch/MinecraftLaunchCoordinator.kt` — remove proven-unnecessary null handling.
27+
- `launcher-desktop/build.gradle.kts` — explicit Material3 and resources dependencies.
28+
- `launcher-desktop/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/desktop/ui/BrandingResources.kt` — shared remembered painter using Compose resources bitmap decoding.
29+
- `launcher-desktop/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/desktop/Main.kt`, `ui/{LauncherArtwork,StartupUpdateGate,SyntaxLauncherApp}.kt` — migrate all branding painter calls.
30+
- `launcher-desktop/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/desktop/ui/{InstanceWorkspace,LocalModsDialog}.kt` — migrate native window API.
31+
- `docs/{DESIGN,DEVELOPMENT,ROADMAP,WORKLOG}.md` — current resource/dependency conventions, maintenance progress and evidence.
32+
33+
### Verification
34+
- `./gradlew test` — PASS via `./gradlew --offline --no-daemon -PlauncherBuildNumber=156 -PlauncherCommit=e96b70e7be7a41a3ee0e15f32cf9fc3d1145ef67 -PlauncherUpdateChannel=dev test build :launcher-desktop:createDistributable --rerun-tasks --no-build-cache --warning-mode all`; 136 core and 25 desktop tests passed, two opt-in live tests skipped. All 19 actionable tasks executed.
35+
- `./gradlew build` — PASS in the same invocation; zero Kotlin source or Gradle-script compiler warnings. Only the two upstream plugin deprecations listed below remain.
36+
- `:launcher-desktop:createDistributable` — PASS for Linux. Verified that the packaged desktop JAR includes the new helper and all three original branding files. `scripts/package_update.py` and `:launcher-desktop:verifyUpdateBundle` also PASS for the complete Linux update archive.
37+
- Manual verification — inspected regenerated Polish home (1440×810) and startup updater (440×600) renders. Branding, transparency and geometry preserved; native dialogs and Windows/macOS still require platform acceptance.
38+
- `git diff --check` — PASS.
39+
40+
### Roadmap updates
41+
- `[x] Cross-milestone desktop development ergonomics / Deprecated core/Compose APIs` — local migration/verification complete.
42+
- `[ ] Native UI and Windows/macOS/platform acceptance` — remains open; headless rendering and Linux packaging do not replace native checks.
43+
44+
### Follow-ups / known issues
45+
- Two upstream Compose/Hot Reload Gradle deprecations remain: Configuration.setVisible and the HotReloadUsageType attribute value type. No repository calls use these APIs; upgrading or disabling plugins is outside the supplied source-warning cleanup.
46+
- No authentication, loader feature, persistence-schema, artwork, canonical product-version or release-policy change. Hosted CI and native dialog focus/resize acceptance remain separate.
47+
48+
---
49+
1150
## 2026-10-21 — Fix nested home scrolling in CI render tests
1251

1352
**Milestone:** v0.1 — translated home regression coverage; v0.12 — CI reliability

‎launcher-core/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/core/account/OfflineAccount.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package pl.syntaxdevteam.syntaxlauncher.core.account
33
import java.nio.charset.StandardCharsets
44
import java.util.UUID
55

6+
@ConsistentCopyVisibility
67
data class OfflineAccount private constructor(
78
override val username: String,
89
override val uuid: UUID,

‎launcher-core/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/core/launch/MinecraftLaunchCoordinator.kt‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,9 @@ class MinecraftLaunchCoordinator(
109109
}
110110

111111
val prepared = if (delayedInstaller) {
112-
val selected = loader ?: error("Missing installed loader selection")
112+
val selected = loader
113113
stage(LaunchPhase.PREPARING_LOADER, "Installing " + selected.id + " " + selected.version)
114-
val installed = loaders?.install(version, selected, instanceLayout, runtime)
115-
?: throw ModLoaderException("Mod loader installer is not configured.")
114+
val installed = loaders.install(version, selected, instanceLayout, runtime)
116115
stage(LaunchPhase.PREPARING_GAME, "Verifying installed ${selected.id} files")
117116
preparation.prepare(installed, platform, instanceLayout) { event ->
118117
if (event is MinecraftPreparationEvent.Download) downloaded(event.event)

‎launcher-desktop/build.gradle.kts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ dependencies {
1515
// Desktop itself owns HttpClient lifecycle and therefore needs this API directly.
1616
implementation("io.ktor:ktor-client-core:3.6.0")
1717
implementation(compose.desktop.currentOs)
18-
implementation(compose.material3)
18+
implementation("org.jetbrains.compose.material3:material3:1.9.0")
19+
implementation("org.jetbrains.compose.components:components-resources:1.12.2")
1920
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.11.0")
2021
runtimeOnly("ch.qos.logback:logback-classic:1.6.5")
2122
testImplementation(kotlin("test-junit5"))

‎launcher-desktop/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/desktop/Main.kt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import androidx.compose.ui.unit.dp
99
import androidx.compose.foundation.window.WindowDraggableArea
1010
import androidx.compose.ui.window.WindowPlacement
1111
import androidx.compose.ui.window.rememberWindowState
12-
import androidx.compose.ui.res.painterResource
1312
import androidx.compose.ui.window.Window
1413
import androidx.compose.ui.window.WindowState
1514
import androidx.compose.ui.window.application
@@ -35,6 +34,7 @@ import pl.syntaxdevteam.syntaxlauncher.core.storage.LauncherPreferencesStore
3534
import pl.syntaxdevteam.syntaxlauncher.core.storage.MinecraftInstallationLayout
3635
import pl.syntaxdevteam.syntaxlauncher.desktop.i18n.LauncherLanguageHost
3736
import pl.syntaxdevteam.syntaxlauncher.desktop.ui.SyntaxLauncherApp
37+
import pl.syntaxdevteam.syntaxlauncher.desktop.ui.rememberBrandingPainter
3838
import pl.syntaxdevteam.syntaxlauncher.desktop.ui.StartupUpdateGate
3939
import pl.syntaxdevteam.syntaxlauncher.desktop.ui.WindowControls
4040
import pl.syntaxdevteam.syntaxlauncher.desktop.ui.WindowResizeHandles
@@ -104,7 +104,7 @@ fun main(args: Array<String>) {
104104
Window(
105105
onCloseRequest = closeLauncher,
106106
title = "SyntaxLauncher · v" + LauncherBuildInfo.version + " · " + LauncherBuildInfo.buildLabel,
107-
icon = painterResource("branding/syntaxlauncher-icon.png"),
107+
icon = rememberBrandingPainter("branding/syntaxlauncher-icon.png"),
108108
state = windowState,
109109
undecorated = true,
110110
resizable = true,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package pl.syntaxdevteam.syntaxlauncher.desktop.ui
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.remember
5+
import androidx.compose.ui.graphics.painter.BitmapPainter
6+
import androidx.compose.ui.graphics.painter.Painter
7+
import org.jetbrains.compose.resources.decodeToImageBitmap
8+
9+
/** Keep packaged branding on the classpath while using the current Compose bitmap decoder. */
10+
@Composable
11+
internal fun rememberBrandingPainter(path: String): Painter = remember(path) {
12+
val bytes = checkNotNull(BrandingResources::class.java.getResourceAsStream("/$path")) {
13+
"Missing packaged branding resource: $path"
14+
}.use { it.readAllBytes() }
15+
BitmapPainter(bytes.decodeToImageBitmap())
16+
}
17+
18+
private object BrandingResources

‎launcher-desktop/src/main/kotlin/pl/syntaxdevteam/syntaxlauncher/desktop/ui/InstanceWorkspace.kt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import androidx.compose.ui.text.font.FontWeight
4545
import androidx.compose.ui.text.style.TextOverflow
4646
import androidx.compose.ui.unit.DpSize
4747
import androidx.compose.ui.unit.dp
48-
import androidx.compose.ui.window.Dialog
48+
import androidx.compose.ui.window.DialogWindow
4949
import androidx.compose.ui.window.rememberDialogState
5050
import pl.syntaxdevteam.syntaxlauncher.core.instance.InstanceCopyProgress
5151
import pl.syntaxdevteam.syntaxlauncher.core.instance.MinecraftInstance
@@ -162,7 +162,7 @@ fun InstanceWorkspace(
162162
)
163163
}
164164

165-
Dialog(
165+
DialogWindow(
166166
onCloseRequest = { if (!isBusy) onDismiss() },
167167
title = strings.text("ui.syntaxlauncher_instances"),
168168
state = rememberDialogState(size = DpSize(width, height)),

0 commit comments

Comments
 (0)