Nucleus
Tao backend

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.

Le composable NativeView intègre une vue native de la plateforme dans une mise en page Compose sur le backend Tao. C'est l'équivalent Tao d'AndroidView sur Android et d'UIKitView sur Compose iOS : vous fournissez un NucleusPlatformView (NSView*, HWND ou GtkWidget*), et Nucleus reparente ce handle dans la fenêtre Tao, le garde dimensionné sur l'emplacement de la mise en page Compose, et le libère quand le composable quitte la composition. Tout ce qui expose l'un de ces handles fonctionne — vues AppKit/SwiftUI, contrôles Win32 ou WinUI, WebView2, widgets GTK, etc.

Ajouter la dépendance

build.gradle.kts
plugins {
    id("dev.nucleusframework")
}

dependencies {
    implementation("dev.nucleusframework:nucleus.nucleus-application:2.5.4")
    implementation("dev.nucleusframework:nucleus.decorated-window-tao:2.5.4")
}

NativeView et NucleusPlatformView vivent dans dev.nucleusframework.window.tao, fournis par decorated-window-tao. Ils sont disponibles dans toute fenêtre ouverte sur le backend Tao.

Intégrer une vue native

Enveloppez votre handle natif dans un NucleusPlatformView et passez une factory à NativeView. La variante que vous implémentez décide de la stratégie d'intégration : NsView sur macOS, HWnd sur Windows, GtkWidget sur Linux :

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import dev.nucleusframework.window.tao.NativeView
import dev.nucleusframework.window.tao.NucleusPlatformView

// Une vue macOS adossée à un handle NSView* brut, obtenu depuis votre
// propre pont AppKit / SwiftUI.
class WebPlatformView(private val handle: Long) : NucleusPlatformView.NsView {
    override val nsViewHandle: Long = handle

    override fun dispose() {
        // Libérez la vue native créée dans la factory.
    }
}

@Composable
fun BrowserPane(url: String) {
    NativeView(
        factory = { WebPlatformView(createNativeWebView(url)) },
        modifier = Modifier.fillMaxSize(),
    )
}

factory s'exécute une seule fois et son résultat est mémorisé pendant toute la durée de vie du composable. Nucleus appelle dispose() quand NativeView quitte la composition. La vue native suit automatiquement la position et la taille de l'emplacement — vous ne fixez pas les cadres vous-même.

Nucleus ne crée pas la vue native à votre place. Ci-dessus, createNativeWebView est un espace réservé pour votre propre code d'interop, qui renvoie le handle de la plateforme (un NSView* sur macOS, un HWND sur Windows, un GtkWidget* sur Linux) sous forme de Long.

Fonctionnement

NativeView lit la variante du NucleusPlatformView renvoyé par factory et route vers le chemin d'intégration correspondant. Sur macOS, le NsView est ajouté comme frère de la vue de contenu Tao ; sur Windows, le HWnd est reparenté sous le HWND principal de Tao ; sur Linux, le GtkWidget est ajouté au widget de contenu GTK de Tao. Chaque chemin suit l'emplacement Compose avec onGloballyPositioned et transmet les bornes au côté natif, si bien que la vue intégrée accompagne les redimensionnements et le défilement sans câblage supplémentaire.

Si la variante renvoyée ne correspond pas au système d'exploitation courant — ou si la plomberie de scène de la plateforme n'est pas disponible — NativeView affiche un Box(modifier) vide au lieu d'échouer. Vous pouvez ainsi écrire un seul point d'appel par plateforme et vous appuyer sur ce repli sur les autres.

L'emplacement content optionnel affiche de l'UI Compose par-dessus la vue intégrée, dans la scène Compose de la fenêtre hôte sur les trois plateformes — le même chemin de fusion par découpe que les composables frères déclarés ensuite et que les popups de la scène. Le contenu d'overlay revendique lui-même ses entrées ; tout ce qu'il ne couvre pas traverse vers la vue native, sans modifier de marquage.

Référence de l'API

Décrire la vue native

NucleusPlatformView est une interface scellée. Implémentez exactement une variante de plateforme et exposez le handle correspondant sous forme de Long :

VariantePlateformePropriété de handle
NucleusPlatformView.NsViewmacOSnsViewHandle — pointeur vers votre NSView*
NucleusPlatformView.HWndWindowshwndHandle — pointeur vers votre HWND
NucleusPlatformView.GtkWidgetLinuxgtkWidgetHandle — pointeur vers votre GtkWidget*

L'interface déclare aussi des rappels de cycle de vie dont le corps est vide par défaut. Redéfinissez ceux dont votre vue a besoin :

MembreAppelé quand
resize(widthPx, heightPx)La taille logique de la vue intégrée change.
setBounds(xPx, yPx, widthPx, heightPx)Le cadre complet change ; à redéfinir quand le rectangle de dessin de la vue est découplé du rectangle de sa fenêtre (par exemple WebView2).
setCornerRadius(radiusPx)Le rognage des coins arrondis change et le rognage générique de l'hôte n'atteint pas la surface de la vue.
clearFocus()La fenêtre hôte ou une couche Compose frère prend le focus.
dispose()Démontage final — libère les ressources natives que vous possédez.

Intégrer la vue

@Composable
fun NativeView(
    factory: () -> NucleusPlatformView,
    modifier: Modifier = Modifier,
    update: (NucleusPlatformView) -> Unit = {},
    cornerRadius: Dp = Dp.Unspecified,
    content: @Composable () -> Unit = {},
)
ParamètreNotes
factoryCrée le NucleusPlatformView. S'exécute une fois ; le résultat est mémorisé puis libéré quand le composable quitte la composition.
modifierDimensionne et positionne l'emplacement. La vue intégrée le remplit.
updateS'exécute à chaque recomposition avec la vue mémorisée. Sert à pousser un nouvel état dans la vue native.
cornerRadiusRognage des coins arrondis ; voir plus bas.
contentSuperposition Compose affichée par-dessus la vue native.

Superposer du contenu Compose

L'emplacement content dessine de l'UI Compose par-dessus la vue intégrée, dans la scène Compose de la fenêtre elle-même. Ce que vous y placez est un composable ordinaire : il reçoit les événements pointeur là où il est dessiné, et les événements qu'il ne consomme pas sont rejoués vers la vue native en dessous.

NativeView(
    factory = { WebPlatformView(createNativeWebView(url)) },
    modifier = Modifier.fillMaxSize(),
) {
    // Une barre d'outils Compose au-dessus de la vue web. Le bouton gère ses
    // propres clics ; les clics ailleurs atteignent la vue web en dessous.
    Button(onClick = { /* recharger */ }) {
        Text("Recharger")
    }
}

Comme l'overlay vit dans la scène principale, les composables déclarés après le NativeView peignent aussi par-dessus, et les Popup issus de la scène s'affichent au-dessus de la vue native.

Modifier.consumeOverlayPointerEvents() est déprécié et n'est plus nécessaire — le contenu d'overlay revendique lui-même ses entrées. L'argument cursor reste mappé sur Modifier.pointerHoverIcon ; utilisez directement ce modifier.

La vue native est composée dans la scène par découpe : Compose efface le rectangle de la vue en transparent, et la plateforme y laisse apparaître le contenu natif.

Le NSView est inséré sous la vue de contenu Tao, et le calque Metal devient non opaque pour que Skia efface à alpha 0. Les déplacements sont appliqués dans la même frame que la géométrie Compose.

Le HWND enfant est rattaché à la fenêtre. Sous Win32, un enfant peint toujours au-dessus de son parent : Compose est donc re-rendu par-dessus via un overlay DirectComposition découpé à l'union des rectangles NativeView vivants — Compose voit toujours les événements en premier à l'intérieur de ces rectangles.

Un hwndHandle à 0L est licite : une vue sans fenêtre enfant Win32 (WebView2 en mode composition, par exemple) pilote son propre visuel via setBounds et setCornerRadius.

Le GtkWidget est rattaché à la fenêtre et la surface GL peint au-dessus : la découpe révèle donc le widget. Tant qu'un NativeView est attaché, la surface annonce une région opaque vide pour que le compositeur continue de mélanger le contenu GTK en dessous.

Cela exige Wayland. Sur le backend X11, l'alpha d'une fenêtre enfant n'est jamais mélangé à celui de son parent : le rectangle découpé montre alors le bureau au lieu du widget — lequel continue de tourner, mais derrière la surface GL.

Rogner en coins arrondis

Le Modifier.clip() de Compose ne se propage pas aux vues natives intégrées, la même limitation qu'AndroidView et UIKitView. Utilisez cornerRadius à la place :

NativeView(
    factory = { WebPlatformView(createNativeWebView(url)) },
    modifier = Modifier.fillMaxSize(),
    cornerRadius = 12.dp,
)

Passez Dp.Infinity pour un rognage entièrement circulaire quelle que soit la taille. La valeur par défaut, Dp.Unspecified, n'applique aucun rognage.

cornerRadius n'a aucun effet sous Linux, où GTK 3 ne sait pas rogner un widget en coins arrondis. Dessinez plutôt une bordure Compose RoundedCornerShape dans l'emplacement content.

Et ensuite