Scaffold de fenêtre et chrome
Construisez des mises en page plein fenêtre et des barres de titre personnalisées sur Tao avec WindowScaffold, les zones de drag, WindowControls et les matériaux de plateforme.
DecoratedWindow + TitleBar plaçaient toujours le contenu principal sous une bande de chrome
fixe. Cela bloquait les mises en page plein fenêtre : pas de liste sous la toolbar, pas de sidebar
en verre qui rejoint les feux de circulation, pas de headerbar de design system qui déplace encore
la fenêtre. Depuis la 2.2, WindowScaffold et un petit jeu de primitives de chrome résolvent cela
sans abandonner les zones de caption natives, l'ordre des boutons ni le drag.
TL;DR
- Composez le chrome avec
WindowScaffold— n'importe quel slot de barre de titre, placementDockedouOverlay. - Activez le drag avec
Modifier.windowDragArea; excluez des enfants avecnoWindowDrag. - Placez min/max/fermer système avec
WindowControls(ou padez avecLocalWindowChromeInsetssous macOS). - Thématisez le client vide avec
WindowBackground; forcez clair/sombre natif avecWindowAppearance. - Matériaux de plateforme :
Modifier.windowGlassRegionsous macOS,WindowsBackdropsous Windows 11.
Ajouter la dépendance
Les API de scaffold sont livrées dans decorated-window-tao (même artefact que le backend Tao) :
dependencies {
implementation("dev.nucleusframework:nucleus.nucleus-application:2.2.0")
implementation("dev.nucleusframework:nucleus.decorated-window-tao:2.2.0")
}Les sites d'appel existants DecoratedWindow + TitleBar continuent de fonctionner. Le scaffold
est purement additif.
Démarrage rapide
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.nucleusframework.application.DecoratedWindow
import dev.nucleusframework.application.nucleusApplication
import dev.nucleusframework.window.TitleBarPlacement
import dev.nucleusframework.window.WindowBackground
import dev.nucleusframework.window.WindowControls
import dev.nucleusframework.window.WindowScaffold
import dev.nucleusframework.window.windowDragArea
fun main() = nucleusApplication {
DecoratedWindow(onCloseRequest = ::exitApplication, title = "Démo scaffold") {
MaterialTheme {
WindowBackground(MaterialTheme.colorScheme.background)
WindowScaffold(
titleBar = {
Box(
Modifier
.fillMaxWidth()
.height(48.dp)
.windowDragArea()
.padding(horizontal = 12.dp),
) {
Text("Mon app", Modifier.align(Alignment.CenterStart))
WindowControls(Modifier.align(Alignment.CenterEnd))
}
},
titleBarPlacement = TitleBarPlacement.Overlay(),
) { contentPadding ->
Box(Modifier.fillMaxSize().padding(contentPadding)) {
/* contenu — défile sous la barre overlay si paddé */
}
}
}
}
}Comment ça marche
WindowScaffold est l'unique enfant prévu de la lambda de contenu d'un DecoratedWindow. Il :
- Mesure le slot
titleBaret publie la hauteur vers la couche native (centrage des feux de circulation macOS, zone de caption / drag tactile Windows) — en remplacement du contrat hard-codéTitleBarStyle.metrics.height. - Fournit
LocalWindowChromeInsetspour que le chrome et le contenu évitent les zones de contrôles réservées par la plateforme (feux de circulation, padding de bord KDE). - En mode
Overlay, laisse le contenu remplir toute la hauteur derrière la barre et renvoie la hauteur mesurée de la barre enPaddingValuessupérieur.
Rien n'est implicitement draggable au niveau du scaffold. Un chrome personnalisé doit déclarer sa
surface de déplacement avec windowDragArea — le même chemin que le TitleBar stock.
Placement de la barre de titre
| Placement | Comportement |
|---|---|
TitleBarPlacement.Docked | Layout classique : la barre prend de la hauteur, le contenu remplit le reste. Par défaut. |
TitleBarPlacement.Overlay(autoHideInFullscreen = true, passThroughToContent = false) | Le contenu est en fillMaxSize ; la barre flotte au-dessus. Le contenu reçoit la hauteur de la barre en padding supérieur. |
autoHideInFullscreen retire la barre en plein écran pour un contenu immersif.
passThroughToContent hit-teste aussi les appuis contre le contenu sous la barre, pour que les
contrôles fusionnés dans la bande de barre de titre (hamburger d'un panneau de nav réduit, bande de
tabs) restent interactifs. Désactivé par défaut : avec un contenu full-bleed qui défile derrière un
chrome opaque, un clic sur la barre activerait sinon ce qui se trouve en dessous.
Primitives de chrome
Zones de drag
modifier = Modifier
.windowDragArea(doubleClickAction = WindowDoubleClickAction.ToggleMaximize)Un appui primaire non consommé suivi d'un déplacement démarre le move natif de la fenêtre. Les enfants interactifs (boutons, champs de texte) s'excluent en consommant l'appui. Les détecteurs de geste qui ne revendiquent le pointeur qu'une fois qu'il bouge — barres de défilement, sliders, poignées de redimensionnement — laissent l'appui non consommé : encapsulez-les :
Scrollbar(Modifier.noWindowDrag())WindowDoubleClickAction.None désactive le double-clic → maximiser sur cette zone.
Contrôles de fenêtre
WindowControls dessine la bande système min / maximiser-restaurer / fermer en dehors de
TitleBar, pour qu'un design system la place dans sa propre toolbar :
WindowControls(
modifier = Modifier.align(Alignment.CenterEnd),
renderer = WindowControlsRenderer.Platform, // par défaut
)Nucleus possède la sémantique (ordre et côté des boutons, y compris le button-layout Linux ;
maximiser ↔ restaurer ; échange plein écran ; fermer → onCloseRequest). Le renderer ne dessine
que. Sous macOS, WindowControlsRenderer.Platform ne dessine rien et réserve l'empreinte des feux
de circulation — padez avec soit WindowControls soit
LocalWindowChromeInsets.controlsInsets, jamais les deux.
Fournissez un WindowControlsRenderer personnalisé pour dessiner les boutons dans le style du
design system.
Insets de chrome
val insets = LocalWindowChromeInsets.current
Row(Modifier.padding(insets.controlsInsets)) { /* contenu de barre de titre */ }controlsInsets est la zone de sécurité horizontale pour les contrôles système ; titleBarHeight
est la hauteur mesurée du slot (0.dp quand la barre est masquée).
Matériaux de plateforme et apparence
Fond de fenêtre
Tao donne à chaque fenêtre son propre ComposeScene, si bien que le thème vit souvent à
l'intérieur de la fenêtre. WindowBackground fixe la couleur de clear depuis cet arbre — la
couleur visible pendant un redimensionnement live, autour des panneaux flottants, et partout où
Compose ne peint rien :
WindowBackground(MaterialTheme.colorScheme.background)Apparence de fenêtre
Force les surfaces natives (verre, feux de circulation, menus, glyphes de chrome Windows) à suivre le thème de l'application plutôt que le réglage OS :
WindowAppearance(
if (dark) WindowAppearanceMode.Dark else WindowAppearanceMode.Light,
)No-op sous Linux. Retombe sur l'apparence dérivée du système quand le composable quitte la composition.
Régions de verre macOS
Modifier.windowGlassRegion héberge un vrai panneau NSSplitViewController sous la surface
Compose pour qu'AppKit applique le matériau teinté papier peint de System Settings — le bureau
transparaît, les fenêtres intermédiaires jamais :
Box(
Modifier
.width(248.dp)
.fillMaxHeight()
.windowGlassRegion(WindowGlassRegionKind.Sidebar, cornerRadius = 12.dp),
) { /* contenu de sidebar ; laissez des zones non peintes pour le matériau */ }Kinds : Sidebar, ContentList, Inspector (macOS 14+). Le composant ne doit pas peindre un fond
opaque plein. macOS uniquement ; no-op ailleurs. Il n'y a pas de verre macOS plein fenêtre — ce
n'est pas un pattern AppKit.
Backdrops Windows 11
WindowsBackdrop(
style = WindowsBackdropStyle.Mica, // ou Acrylic, MicaAlt
tint = Color.Unspecified, // teinte d'app sur le matériau
tier = WindowsBackdropTier.Auto, // Modern / LegacyMica / Windows10Acrylic
)DWM compose le matériau derrière la fenêtre partout où l'app ne peint rien. Laissez des panneaux ou
des marges non peints pour le révéler ; un fond opaque full-bleed le masque. WindowBackground est
suspendu tant qu'un backdrop est actif et restauré quand il quitte la composition.
Dégrade sur les Windows plus anciens : Windows 11 pré-22H2 mappe les styles Mica vers l'ancien attribut mono-matériau ; Windows 10 retombe sur l'acrylic quand disponible. No-op silencieux sous macOS et Linux.
Overlays transparents et sans cadre
La transparence plein fenêtre est un flag de création sur le DecoratedWindow Tao (AWT hors
périmètre). En 2.2.0, l'umbrella NucleusApplicationScope.DecoratedWindow ne le retransmet pas —
utilisez taoApplication ou la surcharge de scope Tao :
import dev.nucleusframework.window.tao.taoApplication
import dev.nucleusframework.window.tao.DecoratedWindow
fun main() = taoApplication {
DecoratedWindow(
onCloseRequest = ::exitApplication,
undecorated = true, // pas de cadre, pas d'ombre DWM
transparent = true, // le client vide compose le bureau
) {
Box(Modifier.size(48.dp).background(Color(0xFFFF00AA)))
}
}Les hôtes clearent en alpha-0 ; les clears opaques de WindowBackground / barre de titre sont
contraints en transparent pour que les régions vides restent transparentes. Les teintes
semi-transparentes s'appliquent toujours.
Notes
- Prévu comme unique enfant de la lambda de contenu de la fenêtre ; il remplit la hauteur restante.
- Le
TitleBarstock reste valable pour les apps qui n'ont pas besoin d'une mise en page plein fenêtre — aucune migration requise. passThroughToContentest opt-in pour une raison : activez-le seulement quand du contenu interactif est censé vivre sous la bande de chrome.
Suite
- DecoratedWindow sur Tao — paramètres de fenêtre et membres du scope.
- Window & toolkits — API de fenêtre partagée et thèmes de toolkit.
- Accessibilité — sémantique Compose projetée vers VoiceOver, Narrator, Orca.
DecoratedWindow sur Tao
Ouvrez une fenêtre Compose Desktop sur le backend Tao avec un slot de barre de titre personnalisé et des contrôles de fenêtre natifs, sans AWT.
Vues natives sur Tao
Intégrez n'importe quelle vue native de la plateforme — NSView, HWND ou GtkWidget — dans une mise en page Compose avec le backend Tao.