feat(desktop): platform/appearance/accent overrides for local preview
Adds three override hooks so a single-OS developer can preview every platform's theming without VMs: AMETHYST_PLATFORM=GNOME ./gradlew :desktopApp:run AMETHYST_APPEARANCE=light ./gradlew :desktopApp:run AMETHYST_ACCENT=#3584E4 ./gradlew :desktopApp:run Each override accepts either an env var or a `-Damethyst.<key>=<value>` JVM property (forwarded from the gradle invocation via build.gradle.kts). - PlatformInfo: `amethyst.platform` accepts MACOS, WINDOWS, GNOME, KDE, LINUX_OTHER, UNKNOWN. Adds a `host` accessor for code that needs the real underlying OS (for a future fallback hook). - PlatformAppearance: `amethyst.appearance` accepts light/dark. - PlatformAccent: `amethyst.accent` accepts `#RRGGBB`, `RRGGBB`, or any libadwaita accent name (blue, teal, green, yellow, orange, red, pink, purple, slate). Window chrome stays native to the host OS — overriding the platform swaps in-app theming only, not the AWT title bar — so on a Mac you still see traffic lights even when previewing the GNOME theme. https://claude.ai/code/session_01NufduPfZvYQVYwLkbCjCUo
This commit is contained in:
+29
-2
@@ -38,14 +38,41 @@ import com.vitorpamplona.quartz.utils.Log
|
||||
* identity and looks intentional rather than broken.
|
||||
*/
|
||||
object PlatformAccent {
|
||||
fun systemAccent(): Color =
|
||||
when (PlatformInfo.current) {
|
||||
/**
|
||||
* Resolves the OS accent color, with an override hook for testing:
|
||||
* `-Damethyst.accent=#3584E4` (system property) or `AMETHYST_ACCENT=blue`
|
||||
* (env var). Accepted: `#RRGGBB`, `RRGGBB`, or any libadwaita accent name
|
||||
* (blue, teal, green, yellow, orange, red, pink, purple, slate).
|
||||
*/
|
||||
fun systemAccent(): Color {
|
||||
forcedAccent()?.let { return it }
|
||||
return when (PlatformInfo.current) {
|
||||
Platform.MACOS -> macOSAccent()
|
||||
Platform.GNOME -> gnomeAccent()
|
||||
Platform.KDE -> kdeAccent()
|
||||
Platform.WINDOWS -> windowsAccent()
|
||||
else -> DefaultPrimary
|
||||
} ?: DefaultPrimary
|
||||
}
|
||||
|
||||
private fun forcedAccent(): Color? {
|
||||
val raw =
|
||||
System.getProperty("amethyst.accent")
|
||||
?: System.getenv("AMETHYST_ACCENT")
|
||||
?: return null
|
||||
val trimmed = raw.trim()
|
||||
// Hex (#RRGGBB or RRGGBB) — long form for clarity
|
||||
val hex = trimmed.removePrefix("#")
|
||||
if (hex.length == 6 && hex.all { it.isDigit() || it in 'a'..'f' || it in 'A'..'F' }) {
|
||||
val v = hex.toLongOrNull(16) ?: return null
|
||||
return Color(
|
||||
red = ((v shr 16) and 0xFFL).toInt(),
|
||||
green = ((v shr 8) and 0xFFL).toInt(),
|
||||
blue = (v and 0xFFL).toInt(),
|
||||
)
|
||||
}
|
||||
return gnomeAccents[trimmed.lowercase()]
|
||||
}
|
||||
|
||||
// macOS named accent colors (NSColor controlAccentColor variants).
|
||||
// Index matches AppleAccentColor defaults value; -1 = Multicolor (use highlight).
|
||||
|
||||
+26
-2
@@ -41,14 +41,38 @@ import java.awt.event.WindowFocusListener
|
||||
* system theme see Amethyst follow within a second of bringing the window forward.
|
||||
*/
|
||||
object PlatformAppearance {
|
||||
fun isSystemDark(): Boolean =
|
||||
when (PlatformInfo.current) {
|
||||
/**
|
||||
* Resolves the OS dark/light preference, with an override hook for testing
|
||||
* on a single machine: `-Damethyst.appearance=light|dark` (system property)
|
||||
* or `AMETHYST_APPEARANCE=light|dark` (env var).
|
||||
*
|
||||
* When the platform is overridden but appearance isn't, this calls the
|
||||
* override platform's detection routine. On a Mac forced to GNOME the
|
||||
* `gsettings` shell-out won't exist and it falls through to the GNOME
|
||||
* default (dark) — pass `AMETHYST_APPEARANCE=light` to flip it.
|
||||
*/
|
||||
fun isSystemDark(): Boolean {
|
||||
forcedAppearance()?.let { return it }
|
||||
return when (PlatformInfo.current) {
|
||||
Platform.MACOS -> isMacOSDark()
|
||||
Platform.GNOME -> isGnomeDark()
|
||||
Platform.KDE -> isKdeDark()
|
||||
Platform.WINDOWS -> isWindowsDark()
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun forcedAppearance(): Boolean? {
|
||||
val raw =
|
||||
System.getProperty("amethyst.appearance")
|
||||
?: System.getenv("AMETHYST_APPEARANCE")
|
||||
?: return null
|
||||
return when (raw.lowercase()) {
|
||||
"dark" -> true
|
||||
"light" -> false
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun isMacOSDark(): Boolean {
|
||||
val out = exec("defaults", "read", "-g", "AppleInterfaceStyle") ?: return false
|
||||
|
||||
+22
-1
@@ -39,7 +39,20 @@ enum class Platform {
|
||||
}
|
||||
|
||||
object PlatformInfo {
|
||||
val current: Platform by lazy { detect() }
|
||||
/**
|
||||
* The detected (or overridden) UI platform — drives all per-OS theming.
|
||||
*
|
||||
* Override for testing on a single machine via `-Damethyst.platform=GNOME`
|
||||
* (system property) or `AMETHYST_PLATFORM=GNOME` (env var). Accepted values
|
||||
* are the [Platform] enum names, case-insensitive: MACOS, WINDOWS, GNOME,
|
||||
* KDE, LINUX_OTHER, UNKNOWN.
|
||||
*/
|
||||
val current: Platform by lazy { override() ?: detect() }
|
||||
|
||||
/** The actual host OS, ignoring any override. Useful for shell-out helpers
|
||||
* that should always hit the real underlying system (e.g. accent detection
|
||||
* falling back to the Mac's value when the override platform's CLI is absent). */
|
||||
val host: Platform by lazy { detect() }
|
||||
|
||||
val isMacOS: Boolean get() = current == Platform.MACOS
|
||||
val isWindows: Boolean get() = current == Platform.WINDOWS
|
||||
@@ -47,6 +60,14 @@ object PlatformInfo {
|
||||
val isKde: Boolean get() = current == Platform.KDE
|
||||
val isLinux: Boolean get() = current.isLinux
|
||||
|
||||
private fun override(): Platform? {
|
||||
val raw =
|
||||
System.getProperty("amethyst.platform")
|
||||
?: System.getenv("AMETHYST_PLATFORM")
|
||||
?: return null
|
||||
return runCatching { Platform.valueOf(raw.uppercase()) }.getOrNull()
|
||||
}
|
||||
|
||||
private fun detect(): Platform {
|
||||
val osName = System.getProperty("os.name", "").lowercase()
|
||||
return when {
|
||||
|
||||
Reference in New Issue
Block a user