Nucleus
Tao backend

Multi-touch & gestes

Lisez les gestes trackpad et tactiles du backend Tao via les pointer events Compose, ou via le flux de gestes brut.

Le backend Tao relaie le flux de gestes du système vers les pointer events Compose : les pincements, rotations et scrolls trackpad atteignent votre UI via les APIs d'entrée Compose standard. Quand vous avez besoin des données de geste non traitées, TaoWindow expose aussi le flux trackpad brut.

Ajouter la dépendance

La prise en charge des gestes est incluse dans le backend Tao. Aucun artefact supplémentaire n'est nécessaire.

build.gradle.kts
dependencies {
    implementation("dev.nucleusframework:nucleus.decorated-window-tao:2.0.7")
}

Lire les gestes depuis Compose

La plupart du traitement des gestes passe par Compose. Les entrées trackpad et tactiles arrivent sous forme de PointerInputChange ; detectTransformGestures et les autres modifiers d'entrée fonctionnent donc sans code spécifique à Tao :

import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput

@Composable
fun ZoomCanvas() {
    var scale by remember { mutableStateOf(1f) }

    Box(
        Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectTransformGestures { _, _, zoom, _ ->
                    scale *= zoom
                }
            },
    )
}

Chaque PointerInputChange porte aussi la source d'entrée et la pression, ce qui vous permet de réagir selon la manière dont l'événement a été produit :

import androidx.compose.ui.input.pointer.PointerType

Modifier.pointerInput(Unit) {
    awaitPointerEventScope {
        while (true) {
            val change = awaitPointerEvent().changes.first()
            val source = change.type          // PointerType.Mouse | Touch | Stylus | Eraser
            val pressure = change.pressure    // 0f si le périphérique ne rapporte pas de pression
            // Réagir selon source / pression.
        }
    }
}

Lire le flux de gestes brut

Pour distinguer directement le type et la phase d'un geste — par exemple séparer un pincement d'un scroll à deux doigts — enregistrez un TrackpadGestureListener sur le TaoWindow courant :

import dev.nucleusframework.window.tao.LocalTaoWindow
import dev.nucleusframework.window.tao.TaoTrackpadGesture
import dev.nucleusframework.window.tao.TaoTrackpadPhase

val window = LocalTaoWindow.current
window?.onTrackpadGesture { kind, phase, xFixed, yFixed, valueFixed ->
    if (kind == TaoTrackpadGesture.MAGNIFY && phase == TaoTrackpadPhase.CHANGED) {
        val ratio = valueFixed / 10_000f   // delta de magnify sous forme de ratio
        // Appliquez le delta de zoom.
    }
}

Le callback reçoit des entiers en virgule fixe : xFixed et yFixed sont des pixels physiques relatifs à la vue, multipliés par 1024, et valueFixed est le delta multiplié par 10000 (un ratio pour magnify, des degrés pour rotate).

onTrackpadGesture remplace le listener précédemment enregistré au lieu d'en ajouter un. Enregistrez un seul listener par fenêtre.

Fonctionnement

Sur chaque plateforme, Tao s'abonne à la source de gestes native haute résolution — NSEvent magnify et rotate sur macOS, l'entrée des touchpads de précision sur Windows, et le protocole de gestes du pointeur Wayland sur Linux. Le pont Rust remet ces événements en forme et les livre à la JVM via un format unique, que le scene host convertit en pointer events Compose.

Le TrackpadGestureListener brut se place sur ce même flux, avant la synthèse des pointer events. macOS et Linux émettent magnify, rotate et smart-magnify ; Windows n'émet que magnify. Il n'existe pas de source native sur les autres configurations, où le listener ne se déclenche donc pas.

Référence de l'API

Enregistrer un listener de gestes

// dev.nucleusframework.window.tao.TaoWindow
fun onTrackpadGesture(listener: TrackpadGestureListener)

fun interface TrackpadGestureListener {
    fun onGesture(
        kind: Int,        // valeur TaoTrackpadGesture
        phase: Int,       // valeur TaoTrackpadPhase
        xFixed: Int,      // pixels physiques × 1024, relatifs à la vue
        yFixed: Int,
        valueFixed: Int,  // delta × 10000 (ratio pour magnify, degrés pour rotate)
    )
}

Récupérez la fenêtre via LocalTaoWindow.current, qui vaut null en dehors d'une fenêtre Tao.

Constantes de gestes

object TaoTrackpadGesture {
    const val MAGNIFY: Int = 0
    const val ROTATE: Int = 1
    const val SMART_MAGNIFY: Int = 2
}

object TaoTrackpadPhase {
    const val BEGAN: Int = 0
    const val CHANGED: Int = 1
    const val ENDED: Int = 2
    const val CANCELLED: Int = 3
}

Infos pointer sur chaque changement

// androidx.compose.ui.input.pointer.PointerInputChange
val type: PointerType         // Mouse | Touch | Stylus | Eraser
val pressure: Float           // 0f si le périphérique ne rapporte pas de pression
val position: Offset          // position sub-pixel
val scrollDelta: Offset       // molette et scroll trackpad

Et ensuite

  • Stylet — pression et inclinaison des périphériques stylet.
  • Wayland — comportement d'entrée et de fenêtrage spécifique à Linux.
  • Fenêtre décorée — la fenêtre Tao et sa barre de titre.
  • Backend Tao — ce que fournit le backend Tao.