Skip to content

fix(application): keep window content on the UI applier (#636) [2.6] - #649

Merged
kdroidFilter merged 1 commit into
nucleus-2.6from
port/2.6-636-composable-target-window-scope
Sep 5, 2026
Merged

kdroidFilter merged 1 commit into
nucleus-2.6from
port/2.6-636-composable-target-window-scope

Conversation

@kdroidFilter

Copy link
Copy Markdown
Collaborator

Port de #648 (commit 41266637) sur nucleus-2.6, étendu aux ouvreurs de fenêtres propres à cette branche.

Rappel du bug (#636)

Les ouvreurs de fenêtres/dialogues laissaient le compilateur inférer leur cible de composition ([0[0]]) : un seul jeton liait l'applier de l'appelant à celui du contenu. Un composable non-UI appelé dans le scope nucleusApplication — le compilateur colle @ComposableTarget sur toute factory non annotée qui transmet un lambda de contenu marqué, cf. rememberMapState de MapLibre Compose — reclassait tout le scope, fenêtres imbriquées incluses, et chaque appel @UiComposable déclenchait un warning (fatal avec -Werror).

Correctif

Chaque ouvreur est @ComposableOpenTarget(-1) avec des lambdas de contenu @UiComposable : appelable depuis n'importe quel applier, et contenu toujours composé en UI dans la composition propre de la fenêtre.

Couvert : DecoratedWindow/DecoratedDialog, les interfaces NucleusWindowHost/NucleusDialogHost + implémentations par défaut, HostedWindow/HostedDialog, SatelliteWindow, Satellite, Tab (nouveaux en 2.6), et les équivalents Tao + TaoStandalonePopup.

Déjà corrects par inférence, laissés tels quels (vérifié dans le bytecode) : TabWindows, les DecoratedWindow/DecoratedDialog v2, DragGhostWindow, et les wrappers Material 2/3 / Jewel — tous en [_[androidx.compose.ui.UiComposable]].

Point non évident

Dès qu'une déclaration porte une cible explicite, l'inférence s'arrête pour toute la déclaration : chaque paramètre lambda composable doit être annoté, pas seulement content. floatingContentWrapper de Satellite était le cas concret — le laisser nu continuait de faire entrer l'applier de l'appelant dans le contenu du satellite. La fixture passe désormais explicitement floatingContentWrapper, header, strip et windowContentWrapper pour couvrir ça.

Garde anti-régression

COMPOSE_APPLIER_CALL_MISMATCH n'est qu'un warning : compileTestKotlin l'escalade en erreur (-Xwarning-level) dans les deux modules, et une ComposableTargetIsolationFixture par module compile la forme rapportée (factory non-UI dans le scope application, contenu UI dans chaque fenêtre).

Vérifications

Conflits du cherry-pick (en-têtes de fichier + imports des 3 fichiers de nucleus-application) résolus à la main. apiCheck, detekt, ktlintCheck et testClasses passent ; aucun changement à l'exécution (annotations de compilation uniquement).

🤖 Generated with Claude Code

Port of 4126663 to 2.6, extended to the branch's own window openers.

The window and dialog openers had their composition target inferred, so a
single non-UI-targeted composable called in the `nucleusApplication` scope
(the Compose compiler bakes `@ComposableTarget` onto any unmarked factory
forwarding a target-marked content lambda) reclassified the whole scope —
nested windows included — and every `@UiComposable` call in it warned, which
is fatal under `-Werror`.

Declare each opener `@ComposableOpenTarget(-1)` with `@UiComposable` content
lambdas, the way Compose Desktop's own `Window`/`Dialog` are open: they are
callable from any applier and always compose UI content in the new window's
own composition, so neither direction of the applier leak survives. Covers
`DecoratedWindow`/`DecoratedDialog`, the `NucleusWindowHost` /
`NucleusDialogHost` interfaces with their default implementations,
`HostedWindow`/`HostedDialog`, `SatelliteWindow`, `Satellite`, `Tab`, and
Tao's equivalents plus `TaoStandalonePopup`. `TabWindows`, the v2
`DecoratedWindow`/`DecoratedDialog` and `DragGhostWindow` already infer the
same scheme (`[_[UiComposable]]`), as do the Material/Jewel wrappers.

Once a declaration carries an explicit target, inference stops for all of it:
every composable lambda parameter needs the annotation, not just `content`.
`Satellite`'s `floatingContentWrapper` was the one case where leaving one
unmarked kept dragging the caller's applier into the satellite content.

`COMPOSE_APPLIER_CALL_MISMATCH` is only a warning, so both test compilations
escalate it to an error and a fixture per module compiles the reported shape
(non-UI factory in the application scope, UI content in every window).
@kdroidFilter
kdroidFilter merged commit 8ed383e into nucleus-2.6 Sep 5, 2026
40 of 46 checks passed
@kdroidFilter
kdroidFilter deleted the port/2.6-636-composable-target-window-scope branch September 5, 2026 16:56
@kdroidFilter kdroidFilter mentioned this pull request Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant