DecoratedWindow
Un seul Composable pour toute fenêtre desktop — slot de barre de titre, chrome OS, drag, multi-fenêtres — agnostique au backend.
DecoratedWindow est la porte entre le UI Compose et le window manager de l'OS. Il dessine un cadre natif, expose un slot TitleBar qu'on remplit librement en Compose, et donne un handle NucleusWindow identique qu'on tourne sur Tao, JBR ou JNI en dessous.
TL;DR
- Appelle
DecoratedWindowà l'intérieur denucleusApplication { }— c'est toute la surface. - Le slot
TitleBar { state -> … }est un Composable complet. Boutons, search, segmented controls — tout est permis. nucleusWindowest le handle agnostique : focus, minimise / maximise / fullscreen, icône, close.- Pour le multi-fenêtres, appelle
DecoratedWindowplusieurs fois. Chaque appel obtient sonNucleusWindow. - Pour changer de style visuel, ajoute un module de toolkit — même Composable, autre look. Voir Toolkits.
Install
nucleus-application plus un module backend. Tao pour le nouveau code :
dependencies {
implementation("dev.nucleusframework:nucleus.nucleus-application:<version>")
implementation("dev.nucleusframework:nucleus.decorated-window-tao:<version>")
}Quickstart
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.*
import androidx.compose.ui.window.rememberWindowState
import dev.nucleusframework.application.*
fun main() = nucleusApplication {
val state = rememberWindowState(size = DpSize(1100.dp, 720.dp))
DecoratedWindow(
onCloseRequest = ::exitApplication,
state = state,
title = "Sample",
minimumSize = DpSize(640.dp, 480.dp),
) {
TitleBar { _ ->
Text("My App", Modifier.padding(8.dp))
}
Box(Modifier.fillMaxSize()) { /* contenu */ }
}
}Comment ça marche
nucleusApplication { } est l'entrée parapluie. Il détecte le backend actif (Tao si decorated-window-tao est au classpath, AWT sinon), amorce les intégrations plateforme (init GraalVM, single-instance lock, deep links), et expose un NucleusApplicationScope qui contient DecoratedWindow et DecoratedDialog.
Dans le lambda de contenu on obtient un NucleusDecoratedWindowScope. Il porte un NucleusWindow — un handle agnostique pour le cycle de vie de la fenêtre. La plupart des apps ne touchent jamais le ComposeWindow / TaoWindow brut ; elles lisent isFocused, observent focusFlow, appellent setMaximized / setMinimumSize. Pour descendre au handle natif, nucleusWindow.unsafe.awtWindow ou nucleusWindow.unsafe.taoWindow est l'échappatoire.
Le style visuel — hauteur de barre de titre, couleurs, icônes des boutons — vit dans TitleBarStyle / DecoratedWindowStyle de decorated-window-core. Enveloppe le app dans NucleusDecoratedWindowTheme pour l'appliquer. Ajouter un des modules de toolkit (-jewel, -material2, -material3, et les packs macOS / Fluent / Yaru à venir) donne un thème prêt à l'emploi.
Apps multi-fenêtres
Ouvre plusieurs fenêtres en appelant DecoratedWindow plus d'une fois. Chaque appel gère son state et son close.
fun main() = nucleusApplication {
var showInspector by remember { mutableStateOf(false) }
DecoratedWindow(onCloseRequest = ::exitApplication, title = "Main") {
TitleBar { _ -> /* … */ }
Button(onClick = { showInspector = true }) { Text("Open inspector") }
}
if (showInspector) {
DecoratedWindow(
onCloseRequest = { showInspector = false },
title = "Inspector",
) {
TitleBar { _ -> Text("Inspector") }
InspectorContent()
}
}
}Reference
DecoratedWindow
@Composable
fun NucleusApplicationScope.DecoratedWindow(
onCloseRequest: () -> Unit,
state: WindowState = rememberWindowState(),
visible: Boolean = true,
title: String = "",
icon: Painter? = null,
resizable: Boolean = true,
enabled: Boolean = true,
focusable: Boolean = true,
alwaysOnTop: Boolean = false,
minimumSize: DpSize? = null,
onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
onKeyEvent: (KeyEvent) -> Boolean = { false },
content: @Composable NucleusDecoratedWindowScope.() -> Unit,
)NucleusWindow
interface NucleusWindow {
val isFocused: Boolean
val isMinimized: Boolean
val isMaximized: Boolean
val isFullscreen: Boolean
val focusFlow: StateFlow<Boolean>
val minimizedFlow: StateFlow<Boolean>
val maximizedFlow: StateFlow<Boolean>
val fullscreenFlow: StateFlow<Boolean>
fun show()
fun hide()
fun toFront()
fun requestFocus()
fun setMinimized(value: Boolean)
fun setMaximized(value: Boolean)
fun setFullscreen(value: Boolean)
fun setAlwaysOnTop(value: Boolean)
fun setMinimumSize(size: DpSize?)
fun setIcon(painter: Painter?)
fun close()
val unsafe: NucleusWindowUnsafe
}NucleusWindowUnsafe
interface NucleusWindowUnsafe {
val awtWindow: ComposeWindow? // null sur Tao
val awtDialog: ComposeDialog?
val taoWindow: TaoWindow? // null sur AWT
val taoHandle: Long?
}Composition locals
LocalNucleusWindow— leNucleusWindowcourant.LocalNucleusBackend—AwtouTao.LocalTitleBarStyle,LocalDecoratedWindowStyle,LocalIsDarkTheme,LocalContentColor.
Besoin d'une dialog ? DecoratedDialog est le miroir de DecoratedWindow avec DialogTitleBar. Pratique pour les panneaux modaux ou détachés.
Notes
nucleusApplicationprend un single-instance lock par défaut. PasseenableSingleInstance = falsepour autoriser plusieurs processus.- Les deep links arrivent sur
onDeepLink { uri -> … }dans le scope. - Pour le tour des quatre looks natifs, voir Toolkits.