react-native-gtkx 0.2.0-alpha.2 → 0.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/aliases/index.d.ts +84 -0
- package/dist/aliases/index.js +208 -0
- package/dist/aliases/index.js.map +1 -0
- package/dist/animated/index.d.ts +2 -0
- package/dist/animated/index.js +5 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/types.d.ts +1 -0
- package/dist/animated/types.js.map +1 -1
- package/dist/animated/value-animation.js +17 -4
- package/dist/animated/value-animation.js.map +1 -1
- package/dist/apis/host.gtkx.js +9 -12
- package/dist/apis/host.gtkx.js.map +1 -1
- package/dist/apis/index.d.ts +4 -2
- package/dist/apis/index.js +2 -0
- package/dist/apis/index.js.map +1 -1
- package/dist/apis/keyboard.d.ts +60 -0
- package/dist/apis/keyboard.js +63 -0
- package/dist/apis/keyboard.js.map +1 -0
- package/dist/apis/log-box.d.ts +15 -0
- package/dist/apis/log-box.js +46 -0
- package/dist/apis/log-box.js.map +1 -0
- package/dist/apis/platform.d.ts +18 -1
- package/dist/apis/platform.js.map +1 -1
- package/dist/common/index.d.ts +0 -1
- package/dist/common/index.js +24 -1
- package/dist/common/index.js.map +1 -1
- package/dist/components/animated.d.ts +70 -6
- package/dist/components/animated.js +559 -46
- package/dist/components/animated.js.map +1 -1
- package/dist/components/driven-size.d.ts +27 -0
- package/dist/components/driven-size.js +85 -0
- package/dist/components/driven-size.js.map +1 -0
- package/dist/components/find-node-handle.d.ts +11 -0
- package/dist/components/find-node-handle.js +53 -0
- package/dist/components/find-node-handle.js.map +1 -0
- package/dist/components/flat-list.d.ts +3 -2
- package/dist/components/flat-list.js.map +1 -1
- package/dist/components/frame-scheduler.d.ts +16 -0
- package/dist/components/frame-scheduler.js +37 -0
- package/dist/components/frame-scheduler.js.map +1 -0
- package/dist/components/image.d.ts +6 -1
- package/dist/components/image.js +15 -4
- package/dist/components/image.js.map +1 -1
- package/dist/components/index.d.ts +7 -5
- package/dist/components/index.js +5 -3
- package/dist/components/index.js.map +1 -1
- package/dist/components/measure.d.ts +51 -1
- package/dist/components/measure.js +101 -2
- package/dist/components/measure.js.map +1 -1
- package/dist/components/pressable.d.ts +61 -1
- package/dist/components/pressable.js +151 -4
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/rect-store.d.ts +58 -0
- package/dist/components/rect-store.js +124 -3
- package/dist/components/rect-store.js.map +1 -1
- package/dist/components/root.js +6 -1
- package/dist/components/root.js.map +1 -1
- package/dist/components/scroll-phase.d.ts +35 -0
- package/dist/components/scroll-phase.js +26 -0
- package/dist/components/scroll-phase.js.map +1 -0
- package/dist/components/scroll-view.d.ts +39 -3
- package/dist/components/scroll-view.js +378 -43
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/text-input.js +9 -25
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.d.ts +6 -2
- package/dist/components/text.js +43 -8
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-focus.d.ts +27 -0
- package/dist/components/use-focus.js +116 -0
- package/dist/components/use-focus.js.map +1 -0
- package/dist/components/use-layout-child.d.ts +13 -1
- package/dist/components/use-layout-child.js +188 -5
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/components/view.d.ts +5 -2
- package/dist/components/view.js +18 -13
- package/dist/components/view.js.map +1 -1
- package/dist/components/virtualized-list.d.ts +45 -5
- package/dist/components/virtualized-list.js +90 -24
- package/dist/components/virtualized-list.js.map +1 -1
- package/dist/components/wheel-scroll-session.d.ts +7 -0
- package/dist/components/wheel-scroll-session.js +53 -0
- package/dist/components/wheel-scroll-session.js.map +1 -0
- package/dist/components/widget-retention.d.ts +35 -0
- package/dist/components/widget-retention.js +169 -0
- package/dist/components/widget-retention.js.map +1 -0
- package/dist/contracts.d.ts +4 -0
- package/dist/contracts.js.map +1 -1
- package/dist/dnd/context.d.ts +69 -0
- package/dist/dnd/context.js +149 -0
- package/dist/dnd/context.js.map +1 -0
- package/dist/dnd/draggable.d.ts +59 -0
- package/dist/dnd/draggable.js +149 -0
- package/dist/dnd/draggable.js.map +1 -0
- package/dist/dnd/droppable.d.ts +24 -0
- package/dist/dnd/droppable.js +74 -0
- package/dist/dnd/droppable.js.map +1 -0
- package/dist/dnd/gtk-controllers.d.ts +39 -0
- package/dist/dnd/gtk-controllers.js +110 -0
- package/dist/dnd/gtk-controllers.js.map +1 -0
- package/dist/dnd/index.d.ts +6 -0
- package/dist/dnd/index.js +31 -0
- package/dist/dnd/index.js.map +1 -0
- package/dist/dnd/order.d.ts +7 -0
- package/dist/dnd/order.js +20 -0
- package/dist/dnd/order.js.map +1 -0
- package/dist/dnd/payload.d.ts +14 -0
- package/dist/dnd/payload.js +41 -0
- package/dist/dnd/payload.js.map +1 -0
- package/dist/dnd/sortable.d.ts +49 -0
- package/dist/dnd/sortable.js +307 -0
- package/dist/dnd/sortable.js.map +1 -0
- package/dist/dnd/types.d.ts +290 -0
- package/dist/dnd/types.js +25 -0
- package/dist/dnd/types.js.map +1 -0
- package/dist/gesture-handler-compat/builder.d.ts +251 -0
- package/dist/gesture-handler-compat/builder.js +513 -0
- package/dist/gesture-handler-compat/builder.js.map +1 -0
- package/dist/gesture-handler-compat/composition.d.ts +23 -0
- package/dist/gesture-handler-compat/composition.js +111 -0
- package/dist/gesture-handler-compat/composition.js.map +1 -0
- package/dist/gesture-handler-compat/deciders.d.ts +3 -0
- package/dist/gesture-handler-compat/deciders.js +47 -0
- package/dist/gesture-handler-compat/deciders.js.map +1 -0
- package/dist/gesture-handler-compat/detector-runtime.d.ts +44 -0
- package/dist/gesture-handler-compat/detector-runtime.js +374 -0
- package/dist/gesture-handler-compat/detector-runtime.js.map +1 -0
- package/dist/gesture-handler-compat/detector.d.ts +22 -0
- package/dist/gesture-handler-compat/detector.js +104 -0
- package/dist/gesture-handler-compat/detector.js.map +1 -0
- package/dist/gesture-handler-compat/fling.d.ts +24 -0
- package/dist/gesture-handler-compat/fling.js +123 -0
- package/dist/gesture-handler-compat/fling.js.map +1 -0
- package/dist/gesture-handler-compat/force-touch.d.ts +13 -0
- package/dist/gesture-handler-compat/force-touch.js +40 -0
- package/dist/gesture-handler-compat/force-touch.js.map +1 -0
- package/dist/gesture-handler-compat/hooks.d.ts +157 -0
- package/dist/gesture-handler-compat/hooks.js +268 -0
- package/dist/gesture-handler-compat/hooks.js.map +1 -0
- package/dist/gesture-handler-compat/hover.d.ts +2 -0
- package/dist/gesture-handler-compat/hover.js +22 -0
- package/dist/gesture-handler-compat/hover.js.map +1 -0
- package/dist/gesture-handler-compat/index.d.ts +98 -0
- package/dist/gesture-handler-compat/index.js +255 -0
- package/dist/gesture-handler-compat/index.js.map +1 -0
- package/dist/gesture-handler-compat/long-press.d.ts +6 -0
- package/dist/gesture-handler-compat/long-press.js +21 -0
- package/dist/gesture-handler-compat/long-press.js.map +1 -0
- package/dist/gesture-handler-compat/manual.d.ts +2 -0
- package/dist/gesture-handler-compat/manual.js +18 -0
- package/dist/gesture-handler-compat/manual.js.map +1 -0
- package/dist/gesture-handler-compat/native.d.ts +7 -0
- package/dist/gesture-handler-compat/native.js +34 -0
- package/dist/gesture-handler-compat/native.js.map +1 -0
- package/dist/gesture-handler-compat/orchestrator.d.ts +77 -0
- package/dist/gesture-handler-compat/orchestrator.js +284 -0
- package/dist/gesture-handler-compat/orchestrator.js.map +1 -0
- package/dist/gesture-handler-compat/pan.d.ts +23 -0
- package/dist/gesture-handler-compat/pan.js +116 -0
- package/dist/gesture-handler-compat/pan.js.map +1 -0
- package/dist/gesture-handler-compat/recognizer.d.ts +268 -0
- package/dist/gesture-handler-compat/recognizer.js +1056 -0
- package/dist/gesture-handler-compat/recognizer.js.map +1 -0
- package/dist/gesture-handler-compat/relations.d.ts +43 -0
- package/dist/gesture-handler-compat/relations.js +128 -0
- package/dist/gesture-handler-compat/relations.js.map +1 -0
- package/dist/gesture-handler-compat/tap.d.ts +6 -0
- package/dist/gesture-handler-compat/tap.js +29 -0
- package/dist/gesture-handler-compat/tap.js.map +1 -0
- package/dist/gesture-handler-compat/touchpad.d.ts +41 -0
- package/dist/gesture-handler-compat/touchpad.js +64 -0
- package/dist/gesture-handler-compat/touchpad.js.map +1 -0
- package/dist/gesture-handler-compat/types.d.ts +441 -0
- package/dist/gesture-handler-compat/types.js +167 -0
- package/dist/gesture-handler-compat/types.js.map +1 -0
- package/dist/gtk/controllers.d.ts +39 -0
- package/dist/gtk/controllers.js +92 -0
- package/dist/gtk/controllers.js.map +1 -0
- package/dist/gtk/index.d.ts +2 -1
- package/dist/gtk/index.js +7 -1
- package/dist/gtk/index.js.map +1 -1
- package/dist/gtkx/bridge/geometry.js +6 -4
- package/dist/gtkx/bridge/geometry.js.map +1 -1
- package/dist/gtkx/bridge/index.d.ts +5 -3
- package/dist/gtkx/bridge/index.js +6 -3
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/slot-portal.js.map +1 -1
- package/dist/gtkx/bridge/use-signal.js +1 -1
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/gtkx/bridge/view-box.d.ts +56 -0
- package/dist/gtkx/bridge/view-box.js +394 -9
- package/dist/gtkx/bridge/view-box.js.map +1 -1
- package/dist/gtkx/bridge/widget-css.d.ts +17 -0
- package/dist/gtkx/bridge/widget-css.js +72 -0
- package/dist/gtkx/bridge/widget-css.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/layout/driven-size.d.ts +12 -0
- package/dist/layout/driven-size.js +70 -0
- package/dist/layout/driven-size.js.map +1 -0
- package/dist/layout/engine.d.ts +17 -1
- package/dist/layout/engine.js +3 -20
- package/dist/layout/engine.js.map +1 -1
- package/dist/layout/node.d.ts +13 -0
- package/dist/layout/node.js +14 -0
- package/dist/layout/node.js.map +1 -1
- package/dist/layout/yoga.d.ts +3 -2
- package/dist/layout/yoga.js +5 -2
- package/dist/layout/yoga.js.map +1 -1
- package/dist/mcp/data/generated.d.ts +257 -60
- package/dist/mcp/data/generated.js +306 -62
- package/dist/mcp/data/generated.js.map +1 -1
- package/dist/metro/index.d.ts +10 -0
- package/dist/metro/index.js +21 -12
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +16 -0
- package/dist/navigation/sidebar.js +65 -1
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/reanimated-compat/animated-ref.d.ts +32 -0
- package/dist/reanimated-compat/animated-ref.js +57 -0
- package/dist/reanimated-compat/animated-ref.js.map +1 -0
- package/dist/reanimated-compat/animation.d.ts +177 -0
- package/dist/reanimated-compat/animation.js +540 -0
- package/dist/reanimated-compat/animation.js.map +1 -0
- package/dist/reanimated-compat/color.d.ts +28 -0
- package/dist/reanimated-compat/color.js +249 -0
- package/dist/reanimated-compat/color.js.map +1 -0
- package/dist/reanimated-compat/decay.d.ts +39 -0
- package/dist/reanimated-compat/decay.js +96 -0
- package/dist/reanimated-compat/decay.js.map +1 -0
- package/dist/reanimated-compat/easing.d.ts +30 -0
- package/dist/reanimated-compat/easing.js +76 -0
- package/dist/reanimated-compat/easing.js.map +1 -0
- package/dist/reanimated-compat/hooks.d.ts +14 -0
- package/dist/reanimated-compat/hooks.js +217 -0
- package/dist/reanimated-compat/hooks.js.map +1 -0
- package/dist/reanimated-compat/index.d.ts +258 -0
- package/dist/reanimated-compat/index.js +464 -0
- package/dist/reanimated-compat/index.js.map +1 -0
- package/dist/reanimated-compat/interpolation.d.ts +18 -0
- package/dist/reanimated-compat/interpolation.js +122 -0
- package/dist/reanimated-compat/interpolation.js.map +1 -0
- package/dist/reanimated-compat/layout-animation-config.d.ts +43 -0
- package/dist/reanimated-compat/layout-animation-config.js +78 -0
- package/dist/reanimated-compat/layout-animation-config.js.map +1 -0
- package/dist/reanimated-compat/layout-animation-presets.d.ts +117 -0
- package/dist/reanimated-compat/layout-animation-presets.js +498 -0
- package/dist/reanimated-compat/layout-animation-presets.js.map +1 -0
- package/dist/reanimated-compat/layout-animation-runtime.d.ts +21 -0
- package/dist/reanimated-compat/layout-animation-runtime.js +311 -0
- package/dist/reanimated-compat/layout-animation-runtime.js.map +1 -0
- package/dist/reanimated-compat/layout-animation-view.d.ts +27 -0
- package/dist/reanimated-compat/layout-animation-view.js +266 -0
- package/dist/reanimated-compat/layout-animation-view.js.map +1 -0
- package/dist/reanimated-compat/layout-animation.d.ts +171 -0
- package/dist/reanimated-compat/layout-animation.js +469 -0
- package/dist/reanimated-compat/layout-animation.js.map +1 -0
- package/dist/reanimated-compat/layout-transitions.d.ts +83 -0
- package/dist/reanimated-compat/layout-transitions.js +350 -0
- package/dist/reanimated-compat/layout-transitions.js.map +1 -0
- package/dist/reanimated-compat/mutable.d.ts +29 -0
- package/dist/reanimated-compat/mutable.js +139 -0
- package/dist/reanimated-compat/mutable.js.map +1 -0
- package/dist/reanimated-compat/props.d.ts +18 -0
- package/dist/reanimated-compat/props.js +111 -0
- package/dist/reanimated-compat/props.js.map +1 -0
- package/dist/reanimated-compat/scroll-handler.d.ts +119 -0
- package/dist/reanimated-compat/scroll-handler.js +274 -0
- package/dist/reanimated-compat/scroll-handler.js.map +1 -0
- package/dist/reanimated-compat/scroll-offset.d.ts +12 -0
- package/dist/reanimated-compat/scroll-offset.js +104 -0
- package/dist/reanimated-compat/scroll-offset.js.map +1 -0
- package/dist/reanimated-compat/style.d.ts +90 -0
- package/dist/reanimated-compat/style.js +452 -0
- package/dist/reanimated-compat/style.js.map +1 -0
- package/dist/reanimated-compat/test-timers.d.ts +28 -0
- package/dist/reanimated-compat/test-timers.js +136 -0
- package/dist/reanimated-compat/test-timers.js.map +1 -0
- package/dist/reanimated-compat/threads.d.ts +13 -0
- package/dist/reanimated-compat/threads.js +41 -0
- package/dist/reanimated-compat/threads.js.map +1 -0
- package/dist/reanimated-compat/tracking.d.ts +41 -0
- package/dist/reanimated-compat/tracking.js +106 -0
- package/dist/reanimated-compat/tracking.js.map +1 -0
- package/dist/reanimated-compat/updater-animations.d.ts +36 -0
- package/dist/reanimated-compat/updater-animations.js +273 -0
- package/dist/reanimated-compat/updater-animations.js.map +1 -0
- package/dist/responder/system.d.ts +80 -7
- package/dist/responder/system.js +143 -23
- package/dist/responder/system.js.map +1 -1
- package/dist/responder/use-responder.d.ts +11 -0
- package/dist/responder/use-responder.js +225 -6
- package/dist/responder/use-responder.js.map +1 -1
- package/dist/runner/host-dev.js +1 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +1 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.js +3 -3
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/gtkx-config-module.js +1 -1
- package/dist/sea/gtkx-config-module.js.map +1 -1
- package/dist/style/absolute-insets.d.ts +26 -0
- package/dist/style/absolute-insets.js +113 -0
- package/dist/style/absolute-insets.js.map +1 -0
- package/dist/style/animated-size.d.ts +25 -0
- package/dist/style/animated-size.js +225 -0
- package/dist/style/animated-size.js.map +1 -0
- package/dist/style/colors.d.ts +17 -0
- package/dist/style/colors.js +25 -6
- package/dist/style/colors.js.map +1 -1
- package/dist/style/imperative-css.d.ts +21 -0
- package/dist/style/imperative-css.js +63 -0
- package/dist/style/imperative-css.js.map +1 -0
- package/dist/style/index.d.ts +2 -1
- package/dist/style/index.js +2 -1
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.js +5 -0
- package/dist/style/split-style.js.map +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/unsupported-export.d.ts +8 -0
- package/dist/unsupported-export.js +66 -0
- package/dist/unsupported-export.js.map +1 -0
- package/dist/vite/index.d.ts +21 -8
- package/dist/vite/index.js +127 -82
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +2 -2
- package/dist/vitest/index.js.map +1 -1
- package/dist/worklets-compat/index.d.ts +4 -0
- package/dist/worklets-compat/index.js +28 -0
- package/dist/worklets-compat/index.js.map +1 -0
- package/dist/worklets-compat/surface.d.ts +110 -0
- package/dist/worklets-compat/surface.js +153 -0
- package/dist/worklets-compat/surface.js.map +1 -0
- package/package.json +25 -9
- package/types.d.ts +9 -0
- package/dist/common/list.d.ts +0 -82
- package/dist/common/list.js +0 -166
- package/dist/common/list.js.map +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// `Race`, `Simultaneous` and `Exclusive` — list-builders over the three
|
|
2
|
+
// relation maps, and nothing else.
|
|
3
|
+
//
|
|
4
|
+
// This file is the evidence for a claim the recon made and this slice had to
|
|
5
|
+
// either prove or withdraw: **composition is sugar**. There is no composed
|
|
6
|
+
// state machine, no composed recognizer and no second arbitration path. Each
|
|
7
|
+
// composer decides which references land in which of two maps, `prepare`
|
|
8
|
+
// below writes them, and the loop in ./orchestrator reads exactly the same
|
|
9
|
+
// maps it reads for a hand-written relation. If that were false, this file
|
|
10
|
+
// would need a mechanism of its own; it needs none, and a test asserts that
|
|
11
|
+
// a composition and the equivalent hand-written relations behave identically.
|
|
12
|
+
//
|
|
13
|
+
// None of the four measured consumers calls a composer
|
|
14
|
+
// (docs/research/gesture-detector.md, probe 3). They are here because they are
|
|
15
|
+
// cheap over the maps, not because anything is waiting on them.
|
|
16
|
+
import { flattenGestures, isComposedGestureSpec, } from "./types";
|
|
17
|
+
const prepareInto = (gesture, simultaneousWith, waitFor, out) => {
|
|
18
|
+
if (!isComposedGestureSpec(gesture)) {
|
|
19
|
+
out.push({
|
|
20
|
+
spec: gesture,
|
|
21
|
+
relations: {
|
|
22
|
+
// What the gesture asked for itself comes first, then what the
|
|
23
|
+
// composition adds. Built fresh rather than written back onto the
|
|
24
|
+
// config: upstream mutates the gesture object in `prepare()` and had
|
|
25
|
+
// to add a snapshot to stop the relations accumulating across renders
|
|
26
|
+
// (#3763). A composition that computes instead of mutating cannot
|
|
27
|
+
// accumulate anything.
|
|
28
|
+
simultaneousHandlers: [
|
|
29
|
+
...(gesture.config.simultaneousHandlers ?? []),
|
|
30
|
+
...simultaneousWith,
|
|
31
|
+
],
|
|
32
|
+
waitFor: [...(gesture.config.waitFor ?? []), ...waitFor],
|
|
33
|
+
// `blocksHandlers` is deliberately NOT extended by any composer, and
|
|
34
|
+
// that is upstream's own note: reversing the order of two gestures in
|
|
35
|
+
// `Exclusive` already produces the same relation from the other end.
|
|
36
|
+
blocksHandlers: gesture.config.blocksHandlers ?? [],
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const members = gesture.gestures;
|
|
42
|
+
if (gesture.composed === "simultaneous") {
|
|
43
|
+
// Pairwise: each member becomes simultaneous with every OTHER member,
|
|
44
|
+
// flattened. Flattened and not composed-as-a-unit, because a member that
|
|
45
|
+
// is itself an `Exclusive` group must not have its own members made
|
|
46
|
+
// simultaneous with each other — that would undo the exclusivity it was
|
|
47
|
+
// written to express.
|
|
48
|
+
members.forEach((member, index) => {
|
|
49
|
+
const others = members
|
|
50
|
+
.filter((_, other) => other !== index)
|
|
51
|
+
.flatMap(flattenGestures);
|
|
52
|
+
prepareInto(member, [...simultaneousWith, ...others], waitFor, out);
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (gesture.composed === "exclusive") {
|
|
57
|
+
// A chain: every group waits for all the groups before it. So the first
|
|
58
|
+
// one written wins if it can, and the last is the fallback — which is the
|
|
59
|
+
// ordering `Gesture.Exclusive(doubleTap, singleTap)` reads as.
|
|
60
|
+
let earlier = [];
|
|
61
|
+
for (const member of members) {
|
|
62
|
+
prepareInto(member, simultaneousWith, [...waitFor, ...earlier], out);
|
|
63
|
+
earlier = [...earlier, ...flattenGestures(member)];
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// `Race` adds nothing at all. It is the default — mutual exclusion between
|
|
68
|
+
// gestures that share the interaction is what the loop does with no
|
|
69
|
+
// relations recorded — so composing with it changes precisely one thing:
|
|
70
|
+
// the gestures end up on the same detector.
|
|
71
|
+
for (const member of members) {
|
|
72
|
+
prepareInto(member, simultaneousWith, waitFor, out);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Flattens a gesture or a composition into the recognizers a detector has to
|
|
77
|
+
* mount, each with the relations composition gave it.
|
|
78
|
+
*/
|
|
79
|
+
export const prepareGestures = (gesture) => {
|
|
80
|
+
const prepared = [];
|
|
81
|
+
prepareInto(gesture, [], [], prepared);
|
|
82
|
+
return prepared;
|
|
83
|
+
};
|
|
84
|
+
const compose = (composed, gestures) => {
|
|
85
|
+
const flattened = gestures.flatMap(flattenGestures);
|
|
86
|
+
if (new Set(flattened).size !== flattened.length) {
|
|
87
|
+
// Upstream throws here too, and the reason is worth keeping: the same
|
|
88
|
+
// gesture in two arms of one composition would be asked to be both
|
|
89
|
+
// simultaneous with itself and to wait for itself, and the maps have no
|
|
90
|
+
// way to say which was meant.
|
|
91
|
+
throw new Error("react-native-gtkx: each gesture can be used only once in a gesture composition.");
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
composed,
|
|
95
|
+
gestures,
|
|
96
|
+
toGestureArray: () => flattened,
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* `Gesture.Race()` — the default, spelled out.
|
|
101
|
+
*
|
|
102
|
+
* Upstream's hook spelling calls this `useCompetingGestures`, which is the
|
|
103
|
+
* better name for it: the gestures compete, the first to activate cancels the
|
|
104
|
+
* rest, and that would have happened anyway.
|
|
105
|
+
*/
|
|
106
|
+
export const raceGestures = (...gestures) => compose("race", gestures);
|
|
107
|
+
/** `Gesture.Simultaneous()` — a pairwise fill of `simultaneousHandlers`. */
|
|
108
|
+
export const simultaneousGestures = (...gestures) => compose("simultaneous", gestures);
|
|
109
|
+
/** `Gesture.Exclusive()` — a chain fill of `waitFor`. */
|
|
110
|
+
export const exclusiveGestures = (...gestures) => compose("exclusive", gestures);
|
|
111
|
+
//# sourceMappingURL=composition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composition.js","sourceRoot":"","sources":["../../src/gesture-handler-compat/composition.ts"],"names":[],"mappings":"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","sourcesContent":["// `Race`, `Simultaneous` and `Exclusive` — list-builders over the three\n// relation maps, and nothing else.\n//\n// This file is the evidence for a claim the recon made and this slice had to\n// either prove or withdraw: **composition is sugar**. There is no composed\n// state machine, no composed recognizer and no second arbitration path. Each\n// composer decides which references land in which of two maps, `prepare`\n// below writes them, and the loop in ./orchestrator reads exactly the same\n// maps it reads for a hand-written relation. If that were false, this file\n// would need a mechanism of its own; it needs none, and a test asserts that\n// a composition and the equivalent hand-written relations behave identically.\n//\n// None of the four measured consumers calls a composer\n// (docs/research/gesture-detector.md, probe 3). They are here because they are\n// cheap over the maps, not because anything is waiting on them.\nimport {\n flattenGestures,\n isComposedGestureSpec,\n type AnyGestureSpec,\n type ComposedGestureKind,\n type ComposedGestureSpec,\n type GestureRef,\n type GestureRelations,\n type GestureSpec,\n} from \"./types\"\n\n/** One recognizer, with the relations it ends up with once composed. */\nexport type PreparedGesture = {\n spec: GestureSpec\n relations: GestureRelations\n}\n\nconst prepareInto = (\n gesture: AnyGestureSpec,\n simultaneousWith: readonly GestureRef[],\n waitFor: readonly GestureRef[],\n out: PreparedGesture[],\n): void => {\n if (!isComposedGestureSpec(gesture)) {\n out.push({\n spec: gesture,\n relations: {\n // What the gesture asked for itself comes first, then what the\n // composition adds. Built fresh rather than written back onto the\n // config: upstream mutates the gesture object in `prepare()` and had\n // to add a snapshot to stop the relations accumulating across renders\n // (#3763). A composition that computes instead of mutating cannot\n // accumulate anything.\n simultaneousHandlers: [\n ...(gesture.config.simultaneousHandlers ?? []),\n ...simultaneousWith,\n ],\n waitFor: [...(gesture.config.waitFor ?? []), ...waitFor],\n // `blocksHandlers` is deliberately NOT extended by any composer, and\n // that is upstream's own note: reversing the order of two gestures in\n // `Exclusive` already produces the same relation from the other end.\n blocksHandlers: gesture.config.blocksHandlers ?? [],\n },\n })\n return\n }\n\n const members = gesture.gestures\n\n if (gesture.composed === \"simultaneous\") {\n // Pairwise: each member becomes simultaneous with every OTHER member,\n // flattened. Flattened and not composed-as-a-unit, because a member that\n // is itself an `Exclusive` group must not have its own members made\n // simultaneous with each other — that would undo the exclusivity it was\n // written to express.\n members.forEach((member, index) => {\n const others = members\n .filter((_, other) => other !== index)\n .flatMap(flattenGestures)\n prepareInto(member, [...simultaneousWith, ...others], waitFor, out)\n })\n return\n }\n\n if (gesture.composed === \"exclusive\") {\n // A chain: every group waits for all the groups before it. So the first\n // one written wins if it can, and the last is the fallback — which is the\n // ordering `Gesture.Exclusive(doubleTap, singleTap)` reads as.\n let earlier: GestureSpec[] = []\n for (const member of members) {\n prepareInto(member, simultaneousWith, [...waitFor, ...earlier], out)\n earlier = [...earlier, ...flattenGestures(member)]\n }\n return\n }\n\n // `Race` adds nothing at all. It is the default — mutual exclusion between\n // gestures that share the interaction is what the loop does with no\n // relations recorded — so composing with it changes precisely one thing:\n // the gestures end up on the same detector.\n for (const member of members) {\n prepareInto(member, simultaneousWith, waitFor, out)\n }\n}\n\n/**\n * Flattens a gesture or a composition into the recognizers a detector has to\n * mount, each with the relations composition gave it.\n */\nexport const prepareGestures = (gesture: AnyGestureSpec): PreparedGesture[] => {\n const prepared: PreparedGesture[] = []\n prepareInto(gesture, [], [], prepared)\n return prepared\n}\n\nconst compose = (\n composed: ComposedGestureKind,\n gestures: readonly AnyGestureSpec[],\n): ComposedGestureSpec => {\n const flattened = gestures.flatMap(flattenGestures)\n if (new Set(flattened).size !== flattened.length) {\n // Upstream throws here too, and the reason is worth keeping: the same\n // gesture in two arms of one composition would be asked to be both\n // simultaneous with itself and to wait for itself, and the maps have no\n // way to say which was meant.\n throw new Error(\n \"react-native-gtkx: each gesture can be used only once in a gesture composition.\",\n )\n }\n return {\n composed,\n gestures,\n toGestureArray: () => flattened,\n }\n}\n\n/**\n * `Gesture.Race()` — the default, spelled out.\n *\n * Upstream's hook spelling calls this `useCompetingGestures`, which is the\n * better name for it: the gestures compete, the first to activate cancels the\n * rest, and that would have happened anyway.\n */\nexport const raceGestures = (\n ...gestures: AnyGestureSpec[]\n): ComposedGestureSpec => compose(\"race\", gestures)\n\n/** `Gesture.Simultaneous()` — a pairwise fill of `simultaneousHandlers`. */\nexport const simultaneousGestures = (\n ...gestures: AnyGestureSpec[]\n): ComposedGestureSpec => compose(\"simultaneous\", gestures)\n\n/** `Gesture.Exclusive()` — a chain fill of `waitFor`. */\nexport const exclusiveGestures = (\n ...gestures: AnyGestureSpec[]\n): ComposedGestureSpec => compose(\"exclusive\", gestures)\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Which pair of predicates the one machine runs, per kind.
|
|
2
|
+
//
|
|
3
|
+
// The whole of what tells the four kinds apart. There is no second state
|
|
4
|
+
// machine, no second event stream and no second grant channel —
|
|
5
|
+
// `docs/research/gesture-detector.md` predicted that `Tap` and `LongPress`
|
|
6
|
+
// would be an afternoon over slice 1's core, and this map is the shape of that
|
|
7
|
+
// claim.
|
|
8
|
+
//
|
|
9
|
+
// `Native` stretched it by exactly one flag rather than one machine: it wants
|
|
10
|
+
// the same progression without the responder grant, because taking the
|
|
11
|
+
// interaction is the one thing a gesture that stands for the native widget
|
|
12
|
+
// must not do. See ./native and `RecognizerDecider.claimsResponder`.
|
|
13
|
+
//
|
|
14
|
+
// Its own file, and free of every platform import, so a test can mount a
|
|
15
|
+
// recognizer of any kind without pulling in GTK — which is what lets the
|
|
16
|
+
// arbitration tests drive several real recognizers off Linux.
|
|
17
|
+
import { flingDecider } from "./fling";
|
|
18
|
+
import { forceTouchDecider } from "./force-touch";
|
|
19
|
+
import { hoverDecider } from "./hover";
|
|
20
|
+
import { longPressDecider } from "./long-press";
|
|
21
|
+
import { manualDecider } from "./manual";
|
|
22
|
+
import { nativeDecider } from "./native";
|
|
23
|
+
import { panDecider } from "./pan";
|
|
24
|
+
import { tapDecider } from "./tap";
|
|
25
|
+
import { pinchDecider, rotationDecider } from "./touchpad";
|
|
26
|
+
export const DECIDERS = {
|
|
27
|
+
pan: panDecider,
|
|
28
|
+
tap: tapDecider,
|
|
29
|
+
longPress: longPressDecider,
|
|
30
|
+
native: nativeDecider,
|
|
31
|
+
// Two predicates each and a `source`, which is the whole of what slice 5
|
|
32
|
+
// added to the machine: `Pinch` and `Rotation` are fed by GTK because a
|
|
33
|
+
// touchpad pinch is not in the pointer stream, and are otherwise ordinary.
|
|
34
|
+
pinch: pinchDecider,
|
|
35
|
+
rotation: rotationDecider,
|
|
36
|
+
// The last four, and the map is the evidence for the claim the epic has been
|
|
37
|
+
// making since slice 2: ten recognizers, one state machine. Each of these
|
|
38
|
+
// is a pair of predicates and at most two flags —
|
|
39
|
+
// `Fling` adds `endsOnActivate`, `Manual` adds a release outcome that
|
|
40
|
+
// decides nothing, `Hover` and `ForceTouch` add a `source`. None of them
|
|
41
|
+
// adds a state, an event stream, a grant channel or an arbitration rule.
|
|
42
|
+
fling: flingDecider,
|
|
43
|
+
manual: manualDecider,
|
|
44
|
+
hover: hoverDecider,
|
|
45
|
+
forceTouch: forceTouchDecider,
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=deciders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deciders.js","sourceRoot":"","sources":["../../src/gesture-handler-compat/deciders.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,yEAAyE;AACzE,gEAAgE;AAChE,2EAA2E;AAC3E,+EAA+E;AAC/E,SAAS;AACT,EAAE;AACF,8EAA8E;AAC9E,uEAAuE;AACvE,2EAA2E;AAC3E,qEAAqE;AACrE,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,8DAA8D;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAElC,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAClC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAG1D,MAAM,CAAC,MAAM,QAAQ,GAA2C;IAC9D,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,UAAU;IACf,SAAS,EAAE,gBAAgB;IAC3B,MAAM,EAAE,aAAa;IACrB,yEAAyE;IACzE,wEAAwE;IACxE,2EAA2E;IAC3E,KAAK,EAAE,YAAY;IACnB,QAAQ,EAAE,eAAe;IACzB,6EAA6E;IAC7E,0EAA0E;IAC1E,kDAAkD;IAClD,sEAAsE;IACtE,yEAAyE;IACzE,yEAAyE;IACzE,KAAK,EAAE,YAAY;IACnB,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,YAAY;IACnB,UAAU,EAAE,iBAAiB;CAC9B,CAAA","sourcesContent":["// Which pair of predicates the one machine runs, per kind.\n//\n// The whole of what tells the four kinds apart. There is no second state\n// machine, no second event stream and no second grant channel —\n// `docs/research/gesture-detector.md` predicted that `Tap` and `LongPress`\n// would be an afternoon over slice 1's core, and this map is the shape of that\n// claim.\n//\n// `Native` stretched it by exactly one flag rather than one machine: it wants\n// the same progression without the responder grant, because taking the\n// interaction is the one thing a gesture that stands for the native widget\n// must not do. See ./native and `RecognizerDecider.claimsResponder`.\n//\n// Its own file, and free of every platform import, so a test can mount a\n// recognizer of any kind without pulling in GTK — which is what lets the\n// arbitration tests drive several real recognizers off Linux.\nimport { flingDecider } from \"./fling\"\nimport { forceTouchDecider } from \"./force-touch\"\nimport { hoverDecider } from \"./hover\"\nimport { longPressDecider } from \"./long-press\"\nimport { manualDecider } from \"./manual\"\nimport { nativeDecider } from \"./native\"\nimport { panDecider } from \"./pan\"\nimport type { RecognizerDecider } from \"./recognizer\"\nimport { tapDecider } from \"./tap\"\nimport { pinchDecider, rotationDecider } from \"./touchpad\"\nimport type { GestureKind } from \"./types\"\n\nexport const DECIDERS: Record<GestureKind, RecognizerDecider> = {\n pan: panDecider,\n tap: tapDecider,\n longPress: longPressDecider,\n native: nativeDecider,\n // Two predicates each and a `source`, which is the whole of what slice 5\n // added to the machine: `Pinch` and `Rotation` are fed by GTK because a\n // touchpad pinch is not in the pointer stream, and are otherwise ordinary.\n pinch: pinchDecider,\n rotation: rotationDecider,\n // The last four, and the map is the evidence for the claim the epic has been\n // making since slice 2: ten recognizers, one state machine. Each of these\n // is a pair of predicates and at most two flags —\n // `Fling` adds `endsOnActivate`, `Manual` adds a release outcome that\n // decides nothing, `Hover` and `ForceTouch` add a `source`. None of them\n // adds a state, an event stream, a grant channel or an arbitration rule.\n fling: flingDecider,\n manual: manualDecider,\n hover: hoverDecider,\n forceTouch: forceTouchDecider,\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type Gtk as GtkNs } from "../gtkx/bridge/index";
|
|
2
|
+
import type { GestureResponderEvent } from "../responder/types";
|
|
3
|
+
import type { PreparedGesture } from "./composition";
|
|
4
|
+
import { type Recognizer } from "./recognizer";
|
|
5
|
+
import { type GestureKind, type GestureSpec } from "./types";
|
|
6
|
+
/** The two responder props that answer a question rather than take an event. */
|
|
7
|
+
export declare const PREDICATES: Set<string>;
|
|
8
|
+
/** A ref in either of React's two spellings. */
|
|
9
|
+
type AnyRef = ((instance: unknown) => void) | {
|
|
10
|
+
current: unknown;
|
|
11
|
+
} | null | undefined;
|
|
12
|
+
type Mounted = {
|
|
13
|
+
/** The spec object this recognizer currently drives; rebound every render. */
|
|
14
|
+
spec: GestureSpec;
|
|
15
|
+
readonly kind: GestureKind;
|
|
16
|
+
readonly tag: number;
|
|
17
|
+
readonly recognizer: Recognizer;
|
|
18
|
+
/**
|
|
19
|
+
* The GTK controller feeding a kind the pointer stream cannot — a `Pinch`, a
|
|
20
|
+
* `Rotation`, a `Hover` or a `ForceTouch` — once the child has produced a
|
|
21
|
+
* widget to attach it to. Null for every pointer kind, and null until then:
|
|
22
|
+
* the child's ref lands before this parent's layout effect, but a child that
|
|
23
|
+
* carries no widget at all never gets one.
|
|
24
|
+
*
|
|
25
|
+
* Typed as the base controller rather than as `Gesture`, because `Hover`'s
|
|
26
|
+
* is a `GtkEventControllerMotion` and that is not a gesture at all.
|
|
27
|
+
*/
|
|
28
|
+
controller: GtkNs.EventController | null;
|
|
29
|
+
};
|
|
30
|
+
export type DetectorRuntime = {
|
|
31
|
+
/** The recognizer props to merge into the child. Stable for the mount. */
|
|
32
|
+
handlers: Record<string, (event: GestureResponderEvent) => boolean | void>;
|
|
33
|
+
/** The callback ref to put on the child. Stable for the detector's life. */
|
|
34
|
+
assignHandle: (instance: unknown) => void;
|
|
35
|
+
/** Called from a layout effect on every render. */
|
|
36
|
+
sync: (prepared: readonly PreparedGesture[], forwarded: AnyRef) => void;
|
|
37
|
+
/** Warns once if the child turned out to carry no widget. */
|
|
38
|
+
checkWidget: () => void;
|
|
39
|
+
/** Every mounted recognizer, in the order the gestures were written. */
|
|
40
|
+
gestures: () => readonly Mounted[];
|
|
41
|
+
dispose: () => void;
|
|
42
|
+
};
|
|
43
|
+
export declare const createDetectorRuntime: () => DetectorRuntime;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
// Everything a mounted `GestureDetector` has to remember between renders, in
|
|
2
|
+
// a closure rather than in the component.
|
|
3
|
+
//
|
|
4
|
+
// This file exists because the mutable state genuinely belongs here, and the
|
|
5
|
+
// React Compiler's lint rules are right to say so: a component may not read
|
|
6
|
+
// or write a ref while rendering. The detector needs things that outlive a
|
|
7
|
+
// render — the current configs, the child's handle, and the child's own
|
|
8
|
+
// forwarded ref — and every one of them is only ever touched from a callback,
|
|
9
|
+
// an effect or a GTK event. Keeping them behind functions makes that
|
|
10
|
+
// structural instead of a promise.
|
|
11
|
+
//
|
|
12
|
+
// It holds a LIST of recognizers rather than one, because a composed gesture
|
|
13
|
+
// is several recognizers on one child. The prop set the child is given is
|
|
14
|
+
// fixed and stable regardless: each entry is a trampoline over whatever
|
|
15
|
+
// recognizers the group currently has, so composing differently on a later
|
|
16
|
+
// render changes the list inside an effect and never during a render.
|
|
17
|
+
import { widgetForHandle } from "../components/measure";
|
|
18
|
+
import { computePointInWindow, Gdk, Gtk, } from "../gtkx/bridge/index";
|
|
19
|
+
import { requestResponder } from "../responder/use-responder";
|
|
20
|
+
import { DECIDERS } from "./deciders";
|
|
21
|
+
import { gestureOrchestrator } from "./orchestrator";
|
|
22
|
+
import { createRecognizer, } from "./recognizer";
|
|
23
|
+
import { bindGestureTag, unbindGestureTag } from "./relations";
|
|
24
|
+
import { mintHandlerTag } from "./types";
|
|
25
|
+
/**
|
|
26
|
+
* The props a recognizer contributes, named once.
|
|
27
|
+
*
|
|
28
|
+
* Fixed rather than read off a recognizer, because the trampolines have to
|
|
29
|
+
* exist before there is a recognizer to read them from: the child renders and
|
|
30
|
+
* registers with the responder system before the detector's own layout effect
|
|
31
|
+
* has run. All three kinds contribute exactly this set.
|
|
32
|
+
*/
|
|
33
|
+
const HANDLER_NAMES = [
|
|
34
|
+
"onStartShouldSetResponder",
|
|
35
|
+
"onMoveShouldSetResponder",
|
|
36
|
+
"onTouchStart",
|
|
37
|
+
"onTouchMove",
|
|
38
|
+
"onTouchEnd",
|
|
39
|
+
"onTouchCancel",
|
|
40
|
+
"onResponderGrant",
|
|
41
|
+
"onResponderMove",
|
|
42
|
+
"onResponderRelease",
|
|
43
|
+
"onResponderTerminate",
|
|
44
|
+
];
|
|
45
|
+
/** The two responder props that answer a question rather than take an event. */
|
|
46
|
+
export const PREDICATES = new Set([
|
|
47
|
+
"onStartShouldSetResponder",
|
|
48
|
+
"onMoveShouldSetResponder",
|
|
49
|
+
]);
|
|
50
|
+
let warnedWithoutWidget = false;
|
|
51
|
+
const warnNoWidget = () => {
|
|
52
|
+
if (warnedWithoutWidget) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
warnedWithoutWidget = true;
|
|
56
|
+
const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
|
|
57
|
+
if (!isProduction) {
|
|
58
|
+
console.warn("react-native-gtkx: `GestureDetector`'s child exposed no ref carrying a widget, so the gesture " +
|
|
59
|
+
"cannot measure its own view. `hitSlop`, `shouldCancelWhenOutside` and the `x`/`y` fields of " +
|
|
60
|
+
"every event will be wrong or ignored. Give the child a `ref` built with the platform's own " +
|
|
61
|
+
"measure handle, or wrap it in a `View`. See docs/api.md.");
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* `GtkGestureZoom` / `GtkGestureRotate` — `Pinch` and `Rotation`.
|
|
66
|
+
*
|
|
67
|
+
* The scale and the angle are taken from the SIGNAL ARGUMENTS rather than read
|
|
68
|
+
* back off the controller, and that is not a style preference:
|
|
69
|
+
* `gtk_gesture_zoom_get_scale_delta` returns 1 the instant the gesture stops
|
|
70
|
+
* being active, so anything asked after the fact reports the gesture undoing
|
|
71
|
+
* itself. The signal carries the same number GTK would have returned, at the
|
|
72
|
+
* moment it is true.
|
|
73
|
+
*
|
|
74
|
+
* The position is `gtk_gesture_get_bounding_box_center`, whose coordinates are
|
|
75
|
+
* relative to the widget the controller is attached to — which is the
|
|
76
|
+
* gesture's own view, so it is already the view-local space upstream's
|
|
77
|
+
* `focalX`/`anchorX` are in (`absoluteToLocal` in its web delegate). When GTK
|
|
78
|
+
* has no bounding box to report, the view's centre stands in: a touchpad
|
|
79
|
+
* pinch's focal point is the pointer, and a pinch with an unknown focal point
|
|
80
|
+
* is still a pinch.
|
|
81
|
+
*/
|
|
82
|
+
const touchpadController = (kind, widget, channel) => {
|
|
83
|
+
let scale = 1;
|
|
84
|
+
let rotation = 0;
|
|
85
|
+
const controller = kind === "pinch" ? new Gtk.GestureZoom() : new Gtk.GestureRotate();
|
|
86
|
+
const sample = () => {
|
|
87
|
+
let x = widget.getWidth() / 2;
|
|
88
|
+
let y = widget.getHeight() / 2;
|
|
89
|
+
const [known, centreX, centreY] = controller.getBoundingBoxCenter();
|
|
90
|
+
if (known) {
|
|
91
|
+
x = centreX;
|
|
92
|
+
y = centreY;
|
|
93
|
+
}
|
|
94
|
+
// `gtk_gesture_zoom_filter_event` only lets a touchpad pinch through at
|
|
95
|
+
// exactly two fingers, so this is a constant rather than a reading.
|
|
96
|
+
return { scale, rotation, x, y, pointers: 2 };
|
|
97
|
+
};
|
|
98
|
+
controller.on("begin", () => {
|
|
99
|
+
scale = 1;
|
|
100
|
+
rotation = 0;
|
|
101
|
+
channel.begin(sample());
|
|
102
|
+
});
|
|
103
|
+
if (kind === "pinch") {
|
|
104
|
+
;
|
|
105
|
+
controller.on("scale-changed", (value) => {
|
|
106
|
+
scale = value;
|
|
107
|
+
channel.update(sample());
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// `angle-changed` carries the current absolute angle first and the delta
|
|
112
|
+
// SINCE RECOGNITION second, which is upstream's `rotation` exactly:
|
|
113
|
+
// radians, cumulative, positive clockwise.
|
|
114
|
+
;
|
|
115
|
+
controller.on("angle-changed", (_angle, delta) => {
|
|
116
|
+
rotation = delta;
|
|
117
|
+
channel.update(sample());
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
controller.on("end", () => {
|
|
121
|
+
channel.end();
|
|
122
|
+
});
|
|
123
|
+
controller.on("cancel", () => {
|
|
124
|
+
channel.cancel();
|
|
125
|
+
});
|
|
126
|
+
return controller;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* `GtkEventControllerMotion` — `Hover`.
|
|
130
|
+
*
|
|
131
|
+
* The plainest of the three, and the reason this gesture turned out to be
|
|
132
|
+
* deliverable at all: `enter` and `motion` carry the pointer's position in the
|
|
133
|
+
* WIDGET's own coordinates, which is already the space every payload field
|
|
134
|
+
* wants, and `leave` carries nothing because there is nothing left to say.
|
|
135
|
+
*
|
|
136
|
+
* `leave` ends the gesture rather than cancelling it — upstream's
|
|
137
|
+
* `onPointerMoveOut` calls `end()`, and it is right to: the pointer leaving is
|
|
138
|
+
* the hover finishing normally, not something taking it away. GTK's own
|
|
139
|
+
* `cancel` has no counterpart on a motion controller, so the channel's
|
|
140
|
+
* `cancel` is reached only through the arbitration registry.
|
|
141
|
+
*
|
|
142
|
+
* Note that this is the same controller `components/pressable.tsx` attaches
|
|
143
|
+
* for its `hovered` state, on possibly the same widget. Two motion controllers
|
|
144
|
+
* on one widget both receive every crossing — they are observers, and neither
|
|
145
|
+
* claims anything — so a `Pressable` inside a `GestureDetector` keeps its own
|
|
146
|
+
* hover styling while the gesture runs.
|
|
147
|
+
*/
|
|
148
|
+
const hoverController = (channel) => {
|
|
149
|
+
const controller = new Gtk.EventControllerMotion();
|
|
150
|
+
controller.on("enter", (x, y) => {
|
|
151
|
+
channel.begin({ x, y, pointers: 1 });
|
|
152
|
+
});
|
|
153
|
+
controller.on("motion", (x, y) => {
|
|
154
|
+
channel.update({ x, y, pointers: 1 });
|
|
155
|
+
});
|
|
156
|
+
controller.on("leave", () => {
|
|
157
|
+
channel.end();
|
|
158
|
+
});
|
|
159
|
+
return controller;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* `GtkGestureStylus` — `ForceTouch`, and the one controller here that needs
|
|
163
|
+
* hardware nothing in the test suite can synthesize.
|
|
164
|
+
*
|
|
165
|
+
* `getAxis(Gdk.AxisUse.PRESSURE)` returns `[known, value]` and may ONLY be
|
|
166
|
+
* called from inside one of the four signal handlers, which is why the sample
|
|
167
|
+
* is built in each of them rather than from a shared reader. GDK normalises
|
|
168
|
+
* pressure to `[0, 1]`, which is already upstream's documented range for
|
|
169
|
+
* `minForce`/`maxForce`, so nothing is rescaled.
|
|
170
|
+
*
|
|
171
|
+
* `stylusOnly` is left at GTK's default (true), so this controller sees tablet
|
|
172
|
+
* tools and nothing else. A mouse dragging across the widget produces no
|
|
173
|
+
* events here at all — which is the behaviour that keeps a `ForceTouch` from
|
|
174
|
+
* quietly activating at force 0 on a machine with no tablet.
|
|
175
|
+
*/
|
|
176
|
+
const stylusController = (channel) => {
|
|
177
|
+
const controller = new Gtk.GestureStylus();
|
|
178
|
+
const pressure = () => {
|
|
179
|
+
const [known, value] = controller.getAxis(Gdk.AxisUse.PRESSURE);
|
|
180
|
+
// A tool with no pressure axis reports none, and 0 is the honest reading
|
|
181
|
+
// rather than a stand-in: a gesture whose `minForce` cannot be met simply
|
|
182
|
+
// never activates, which is what a device that cannot measure pressure
|
|
183
|
+
// should produce.
|
|
184
|
+
return known ? value : 0;
|
|
185
|
+
};
|
|
186
|
+
controller.on("down", (x, y) => {
|
|
187
|
+
channel.begin({ x, y, force: pressure(), pointers: 1 });
|
|
188
|
+
});
|
|
189
|
+
controller.on("motion", (x, y) => {
|
|
190
|
+
channel.update({ x, y, force: pressure(), pointers: 1 });
|
|
191
|
+
});
|
|
192
|
+
controller.on("up", () => {
|
|
193
|
+
channel.end();
|
|
194
|
+
});
|
|
195
|
+
controller.on("cancel", () => {
|
|
196
|
+
channel.cancel();
|
|
197
|
+
});
|
|
198
|
+
return controller;
|
|
199
|
+
};
|
|
200
|
+
export const createDetectorRuntime = () => {
|
|
201
|
+
let mounted = [];
|
|
202
|
+
let handle = null;
|
|
203
|
+
let forwardedRef = null;
|
|
204
|
+
const publish = (instance) => {
|
|
205
|
+
if (typeof forwardedRef === "function") {
|
|
206
|
+
forwardedRef(instance);
|
|
207
|
+
}
|
|
208
|
+
else if (forwardedRef) {
|
|
209
|
+
forwardedRef.current = instance;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const boundsInWindow = () => {
|
|
213
|
+
const widget = widgetForHandle(handle);
|
|
214
|
+
if (!widget) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const origin = computePointInWindow(widget, 0, 0);
|
|
218
|
+
if (!origin) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
x: origin.x,
|
|
223
|
+
y: origin.y,
|
|
224
|
+
width: widget.getWidth(),
|
|
225
|
+
height: widget.getHeight(),
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Attaches the GTK controller a non-pointer kind is fed by, and pumps it
|
|
230
|
+
* into the recognizer's controller channel.
|
|
231
|
+
*
|
|
232
|
+
* FOUR KINDS, THREE CONTROLLERS, ONE CHANNEL. `GtkGestureZoom` for `Pinch`,
|
|
233
|
+
* `GtkGestureRotate` for `Rotation`, `GtkEventControllerMotion` for `Hover`
|
|
234
|
+
* and `GtkGestureStylus` for `ForceTouch`. Everything past `channel.begin`
|
|
235
|
+
* is the same state machine every pointer kind runs on — see
|
|
236
|
+
* `ControllerChannel` in ./recognizer.
|
|
237
|
+
*/
|
|
238
|
+
const attachController = (gesture) => {
|
|
239
|
+
const channel = gesture.recognizer.controller;
|
|
240
|
+
if (channel === null || gesture.controller !== null) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const widget = widgetForHandle(handle);
|
|
244
|
+
if (!widget) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (gesture.kind === "pinch" || gesture.kind === "rotation") {
|
|
248
|
+
gesture.controller = touchpadController(gesture.kind, widget, channel);
|
|
249
|
+
}
|
|
250
|
+
else if (gesture.kind === "hover") {
|
|
251
|
+
gesture.controller = hoverController(channel);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
gesture.controller = stylusController(channel);
|
|
255
|
+
}
|
|
256
|
+
widget.addController(gesture.controller);
|
|
257
|
+
};
|
|
258
|
+
const detachController = (gesture) => {
|
|
259
|
+
const controller = gesture.controller;
|
|
260
|
+
if (controller === null) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
gesture.controller = null;
|
|
264
|
+
const widget = widgetForHandle(handle);
|
|
265
|
+
widget?.removeController(controller);
|
|
266
|
+
};
|
|
267
|
+
const create = (spec) => {
|
|
268
|
+
const tag = mintHandlerTag();
|
|
269
|
+
const gesture = {
|
|
270
|
+
spec,
|
|
271
|
+
kind: spec.kind,
|
|
272
|
+
tag,
|
|
273
|
+
controller: null,
|
|
274
|
+
// The config is read on every event rather than captured, so a re-render
|
|
275
|
+
// that hands the detector a fresh gesture object takes effect without
|
|
276
|
+
// swapping the handler set mid-drag.
|
|
277
|
+
recognizer: createRecognizer(tag, DECIDERS[spec.kind], () => gesture.spec.config, {
|
|
278
|
+
boundsInWindow,
|
|
279
|
+
requestResponder: () => {
|
|
280
|
+
const widget = widgetForHandle(handle);
|
|
281
|
+
return widget !== null && requestResponder(widget);
|
|
282
|
+
},
|
|
283
|
+
orchestrator: gestureOrchestrator,
|
|
284
|
+
}),
|
|
285
|
+
};
|
|
286
|
+
return gesture;
|
|
287
|
+
};
|
|
288
|
+
const handlers = {};
|
|
289
|
+
for (const name of HANDLER_NAMES) {
|
|
290
|
+
handlers[name] = PREDICATES.has(name)
|
|
291
|
+
? (event) => {
|
|
292
|
+
// Either recognizer saying yes is a yes, and every one of them is
|
|
293
|
+
// asked — the same rule RN's own bubbling uses when several views
|
|
294
|
+
// want the responder, and the reason a composed gesture's members
|
|
295
|
+
// all get to see the question.
|
|
296
|
+
let wanted = false;
|
|
297
|
+
for (const gesture of mounted) {
|
|
298
|
+
if (gesture.recognizer.handlers[name]?.(event) === true) {
|
|
299
|
+
wanted = true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return wanted;
|
|
303
|
+
}
|
|
304
|
+
: (event) => {
|
|
305
|
+
for (const gesture of mounted) {
|
|
306
|
+
gesture.recognizer.handlers[name]?.(event);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
handlers,
|
|
312
|
+
assignHandle: (instance) => {
|
|
313
|
+
handle = instance;
|
|
314
|
+
publish(instance);
|
|
315
|
+
// No return value: React 19 reads one as a callback-ref cleanup.
|
|
316
|
+
},
|
|
317
|
+
sync: (prepared, forwarded) => {
|
|
318
|
+
// A composition whose SHAPE changed is a different set of gestures, so
|
|
319
|
+
// the recognizers that no longer have a counterpart are disposed and
|
|
320
|
+
// the new ones minted. Doing it here rather than during render is what
|
|
321
|
+
// keeps tag minting out of the render phase; the child's props never
|
|
322
|
+
// change, because they are trampolines over this list.
|
|
323
|
+
const next = [];
|
|
324
|
+
const reusable = [...mounted];
|
|
325
|
+
for (const gesture of prepared) {
|
|
326
|
+
const index = reusable.findIndex((candidate) => candidate.kind === gesture.spec.kind);
|
|
327
|
+
const existing = index >= 0 ? reusable.splice(index, 1)[0] : create(gesture.spec);
|
|
328
|
+
existing.spec = gesture.spec;
|
|
329
|
+
next.push(existing);
|
|
330
|
+
// Identity for the relation maps: the spec object an app holds points
|
|
331
|
+
// at the tag this mount minted. Re-bound every render because both
|
|
332
|
+
// spellings rebuild the object, and never unbound on re-render, so a
|
|
333
|
+
// memoized gesture holding an earlier render's object still resolves.
|
|
334
|
+
bindGestureTag(gesture.spec, existing.tag);
|
|
335
|
+
gestureOrchestrator.relations.configure(existing.tag, gesture.relations);
|
|
336
|
+
// Idempotent, and attempted on every render rather than once: the
|
|
337
|
+
// child's ref lands before this effect, but a child that is not laid
|
|
338
|
+
// out yet has no widget to carry a controller. The first render that
|
|
339
|
+
// has one attaches it.
|
|
340
|
+
attachController(existing);
|
|
341
|
+
}
|
|
342
|
+
for (const dropped of reusable) {
|
|
343
|
+
unbindGestureTag(dropped.spec);
|
|
344
|
+
detachController(dropped);
|
|
345
|
+
dropped.recognizer.dispose();
|
|
346
|
+
}
|
|
347
|
+
mounted = next;
|
|
348
|
+
// The child's ref is attached BEFORE this parent's layout effect runs,
|
|
349
|
+
// so the first `assignHandle` happened while there was nothing to
|
|
350
|
+
// forward to. Publishing again here is what gets the handle into a ref
|
|
351
|
+
// the child was given on its very first mount.
|
|
352
|
+
const changed = forwarded !== forwardedRef;
|
|
353
|
+
forwardedRef = forwarded;
|
|
354
|
+
if (changed && handle !== null) {
|
|
355
|
+
publish(handle);
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
checkWidget: () => {
|
|
359
|
+
if (widgetForHandle(handle) === null) {
|
|
360
|
+
warnNoWidget();
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
gestures: () => mounted,
|
|
364
|
+
dispose: () => {
|
|
365
|
+
for (const gesture of mounted) {
|
|
366
|
+
unbindGestureTag(gesture.spec);
|
|
367
|
+
detachController(gesture);
|
|
368
|
+
gesture.recognizer.dispose();
|
|
369
|
+
}
|
|
370
|
+
mounted = [];
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
};
|
|
374
|
+
//# sourceMappingURL=detector-runtime.js.map
|