react-native-gtkx 0.3.0-alpha.1 → 0.4.0-alpha.2
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 +1 -1
- package/dist/adw/index.d.ts +3 -2
- package/dist/adw/index.js +21 -4
- package/dist/adw/index.js.map +1 -1
- package/dist/adw/widgets.generated.d.ts +1 -1
- package/dist/adw/widgets.generated.js +2 -2
- package/dist/adw/widgets.generated.js.map +1 -1
- package/dist/animated/create-animated.d.ts +2 -0
- package/dist/animated/create-animated.js +2 -0
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/event.d.ts +15 -0
- package/dist/animated/event.js +80 -0
- package/dist/animated/event.js.map +1 -0
- package/dist/animated/index.d.ts +4 -0
- package/dist/animated/index.js +8 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/spring.d.ts +10 -0
- package/dist/animated/spring.js +20 -6
- package/dist/animated/spring.js.map +1 -1
- package/dist/animated/timing.d.ts +10 -0
- package/dist/animated/timing.js +21 -13
- package/dist/animated/timing.js.map +1 -1
- package/dist/apis/host.gtkx.js +70 -11
- package/dist/apis/host.gtkx.js.map +1 -1
- package/dist/common/navigation-stack.d.ts +6 -3
- package/dist/common/navigation-stack.js +8 -1
- package/dist/common/navigation-stack.js.map +1 -1
- package/dist/common/widget.js +2 -11
- package/dist/common/widget.js.map +1 -1
- package/dist/components/animated.d.ts +1 -0
- package/dist/components/animated.js +28 -4
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.js +32 -8
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/request-animation-frame.d.ts +33 -0
- package/dist/components/request-animation-frame.js +83 -0
- package/dist/components/request-animation-frame.js.map +1 -0
- package/dist/components/scroll-view.js +60 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/virtualized-list.js +1 -4
- package/dist/components/virtualized-list.js.map +1 -1
- package/dist/dnd/autoscroll.d.ts +44 -0
- package/dist/dnd/autoscroll.js +176 -0
- package/dist/dnd/autoscroll.js.map +1 -0
- package/dist/dnd/grid-order.d.ts +45 -0
- package/dist/dnd/grid-order.js +151 -0
- package/dist/dnd/grid-order.js.map +1 -0
- package/dist/dnd/grid.d.ts +44 -0
- package/dist/dnd/grid.js +397 -0
- package/dist/dnd/grid.js.map +1 -0
- package/dist/dnd/gtk-controllers.d.ts +16 -2
- package/dist/dnd/gtk-controllers.js +23 -3
- package/dist/dnd/gtk-controllers.js.map +1 -1
- package/dist/dnd/index.d.ts +4 -2
- package/dist/dnd/index.js +9 -2
- package/dist/dnd/index.js.map +1 -1
- package/dist/dnd/order-state.d.ts +14 -0
- package/dist/dnd/order-state.js +55 -0
- package/dist/dnd/order-state.js.map +1 -0
- package/dist/dnd/order.d.ts +19 -0
- package/dist/dnd/order.js +19 -0
- package/dist/dnd/order.js.map +1 -1
- package/dist/dnd/sortable.d.ts +29 -4
- package/dist/dnd/sortable.js +308 -115
- package/dist/dnd/sortable.js.map +1 -1
- package/dist/dnd/types.d.ts +236 -14
- package/dist/dnd/types.js +30 -0
- package/dist/dnd/types.js.map +1 -1
- package/dist/gesture-handler-compat/attach-context.d.ts +29 -0
- package/dist/gesture-handler-compat/attach-context.js +90 -0
- package/dist/gesture-handler-compat/attach-context.js.map +1 -0
- package/dist/gesture-handler-compat/detector-runtime.d.ts +14 -3
- package/dist/gesture-handler-compat/detector-runtime.js +15 -5
- package/dist/gesture-handler-compat/detector-runtime.js.map +1 -1
- package/dist/gesture-handler-compat/detector.js +57 -2
- package/dist/gesture-handler-compat/detector.js.map +1 -1
- package/dist/gesture-handler-compat/gesture-state-manager.d.ts +5 -0
- package/dist/gesture-handler-compat/gesture-state-manager.js +77 -0
- package/dist/gesture-handler-compat/gesture-state-manager.js.map +1 -0
- package/dist/gesture-handler-compat/index.d.ts +2 -2
- package/dist/gesture-handler-compat/index.js +38 -33
- package/dist/gesture-handler-compat/index.js.map +1 -1
- package/dist/gesture-handler-compat/recognizer.d.ts +12 -1
- package/dist/gesture-handler-compat/recognizer.js +23 -9
- package/dist/gesture-handler-compat/recognizer.js.map +1 -1
- package/dist/gesture-handler-compat/tag-registry.d.ts +14 -0
- package/dist/gesture-handler-compat/tag-registry.js +19 -0
- package/dist/gesture-handler-compat/tag-registry.js.map +1 -0
- package/dist/globals/index.d.ts +47 -0
- package/dist/globals/index.js +181 -0
- package/dist/globals/index.js.map +1 -0
- package/dist/globals/install.d.ts +1 -0
- package/dist/globals/install.js +14 -0
- package/dist/globals/install.js.map +1 -0
- package/dist/gtkx/bridge/adw-namespace.d.ts +2 -0
- package/dist/gtkx/bridge/adw-namespace.js +26 -0
- package/dist/gtkx/bridge/adw-namespace.js.map +1 -0
- package/dist/gtkx/bridge/adw.d.ts +23 -0
- package/dist/gtkx/bridge/adw.js +93 -0
- package/dist/gtkx/bridge/adw.js.map +1 -0
- package/dist/gtkx/bridge/color-scheme-parse.d.ts +11 -0
- package/dist/gtkx/bridge/color-scheme-parse.js +42 -0
- package/dist/gtkx/bridge/color-scheme-parse.js.map +1 -0
- package/dist/gtkx/bridge/color-scheme-portal.d.ts +24 -0
- package/dist/gtkx/bridge/color-scheme-portal.js +172 -0
- package/dist/gtkx/bridge/color-scheme-portal.js.map +1 -0
- package/dist/gtkx/bridge/core.d.ts +25 -0
- package/dist/gtkx/bridge/core.js +69 -0
- package/dist/gtkx/bridge/core.js.map +1 -0
- package/dist/gtkx/bridge/drag-layer.d.ts +27 -0
- package/dist/gtkx/bridge/drag-layer.js +177 -0
- package/dist/gtkx/bridge/drag-layer.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +1 -27
- package/dist/gtkx/bridge/index.js +12 -71
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.adw.d.ts +1 -0
- package/dist/gtkx/bridge/widgets.generated.adw.js +16 -0
- package/dist/gtkx/bridge/widgets.generated.adw.js.map +1 -0
- package/dist/gtkx/bridge/widgets.generated.d.ts +0 -1
- package/dist/gtkx/bridge/widgets.generated.js +3 -3
- package/dist/gtkx/bridge/widgets.generated.js.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/data/generated.d.ts +41 -20
- package/dist/mcp/data/generated.js +46 -20
- package/dist/mcp/data/generated.js.map +1 -1
- package/dist/metro/index.d.ts +1 -0
- package/dist/metro/index.js +12 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/sidebar.js +2 -45
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/reanimated-compat/animatable-value.d.ts +158 -0
- package/dist/reanimated-compat/animatable-value.js +316 -0
- package/dist/reanimated-compat/animatable-value.js.map +1 -0
- package/dist/reanimated-compat/animation.d.ts +34 -13
- package/dist/reanimated-compat/animation.js +97 -25
- package/dist/reanimated-compat/animation.js.map +1 -1
- package/dist/reanimated-compat/mutable.js +6 -6
- package/dist/reanimated-compat/mutable.js.map +1 -1
- package/dist/reanimated-compat/updater-animations.js +32 -12
- package/dist/reanimated-compat/updater-animations.js.map +1 -1
- package/dist/runner/host-dev.js +5 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +9 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/vite/index.js +130 -0
- package/dist/vite/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/gtkx/bridge/theme.d.ts +0 -4
- package/dist/gtkx/bridge/theme.js +0 -5
- package/dist/gtkx/bridge/theme.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-animation-frame.js","sourceRoot":"","sources":["../../src/components/request-animation-frame.ts"],"names":[],"mappings":"AAyDA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,SAAyB,EACE,EAAE;IAC7B,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,OAAO,GAAG,IAAI,GAAG,EAAkC,CAAA;IACvD,IAAI,MAAM,GAAwB,IAAI,CAAA;IAEtC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAQ,EAAE;QACnC,MAAM,GAAG,IAAI,CAAA;QACb,kEAAkE;QAClE,uEAAuE;QACvE,mEAAmE;QACnE,MAAM,KAAK,GAAG,OAAO,CAAA;QACrB,OAAO,GAAG,IAAI,GAAG,EAAE,CAAA;QACnB,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,CAAA;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,iEAAiE;gBACjE,iEAAiE;gBACjE,8DAA8D;gBAC9D,kDAAkD;gBAClD,OAAO,CAAC,KAAK,CACX,2DAA2D,EAC3D,KAAK,CACN,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,MAAM,qBAAqB,GAA0B,CAAC,QAAQ,EAAE,EAAE;QAChE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;QAC3B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QAC7B,qEAAqE;QACrE,uEAAuE;QACvE,oDAAoD;QACpD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACpC,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IAED,MAAM,oBAAoB,GAAyB,CAAC,MAAM,EAAE,EAAE;QAC5D,qEAAqE;QACrE,oEAAoE;QACpE,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,OAAM;QACR,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACtB,gEAAgE;QAChE,mEAAmE;QACnE,sCAAsC;QACtC,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAC1C,MAAM,EAAE,CAAA;YACR,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;IACH,CAAC,CAAA;IAED,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAA;AACxD,CAAC,CAAA;AAED,IAAI,SAAS,GAAG,KAAK,CAAA;AAErB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAChD,SAAyB,EACnB,EAAE;IACR,IAAI,SAAS,EAAE,CAAC;QACd,OAAM;IACR,CAAC;IACD,SAAS,GAAG,IAAI,CAAA;IAChB,MAAM,IAAI,GAAG,2BAA2B,CAAC,SAAS,CAAC,CAAA;IACnD,UAAU,CAAC,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAA;IAC7D,UAAU,CAAC,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,CAAA;AAC7D,CAAC,CAAA","sourcesContent":["// requestAnimationFrame/cancelAnimationFrame as GLOBALS — RN provides both\n// from its own bootstrap on every platform (InitializeCore's JSTimers); this\n// one dropped that bootstrap entirely (../metro/index.ts: \"the runtime\n// environment IS Node, the host provides everything\"), which held for\n// everything except this one browser-ism library code reaches for directly.\n// docs/research/upstream-libraries.md's third experiment is what found the\n// gap: react-native-sortables crashes the whole process at mount on a bare\n// `requestAnimationFrame`, a `ReferenceError` — Node has no such global,\n// browsers and RN both do.\n//\n// Semantics match the browser spec's \"run the animation frame callbacks\"\n// algorithm (which is also what RN's own JSTimers mirrors): an id comes\n// back; a callback receives a monotonic, high-resolution timestamp in\n// milliseconds; a callback booked WHILE a batch is running lands on the\n// frame AFTER that one — the spec snapshots the callback list before\n// invoking it, so same-frame recursion never happens; cancelling before the\n// frame fires is silent; and one callback throwing is reported and does NOT\n// stop its siblings in the same batch (the spec's \"invoke callback\" for each\n// entry, un-nested — one throw ends one iteration, not the loop).\n//\n// PURE ON PURPOSE — no import of frame-scheduler.ts (and therefore no\n// @gtkx/* bridge, no GLib): both `createRequestAnimationFrame` and\n// `installGlobalRequestAnimationFrame` below take a `FrameScheduler`\n// argument rather than reaching for `glibScheduler` themselves, exactly the\n// split `animated/frame-loop.ts` (pure) already draws against\n// `components/animated.tsx` (the impure thing that wires it to the real\n// clock). That is what lets this file's own scheduling semantics be unit\n// tested off Linux, with no codegen store and no GTK — the caller (this\n// package's entry, src/index.ts) is what supplies the real\n// `glibScheduler`, the one frame driver on this platform (see\n// frame-scheduler.ts's header for why nothing else may add a second one).\nimport type { FrameScheduler } from \"../animated/index\"\n\n// No \"dom\" lib in this package's tsconfig (lib: [\"esnext\"]), so these are\n// stated by hand — matching react-native's OWN ambient declaration\n// (node_modules/react-native/src/types/globals.d.ts) exactly, parameter for\n// parameter, rather than inventing a narrower one: that file is what\n// `@react-navigation/native`'s types pull into this program transitively\n// (through its own `react-native` import), so a mismatched signature here\n// does not stay private — it becomes a real overload conflict the moment\n// both are ambient in the same program. `cancelAnimationFrame` accepting\n// `null | undefined` is not padding: it is what makes\n// `cancelAnimationFrame(handleRef.current)` — a ref that starts unset — a\n// no-op that also type-checks, the common cleanup shape in a `useEffect`.\ndeclare global {\n function requestAnimationFrame(callback: (time: number) => void): number\n function cancelAnimationFrame(handle: number | null | undefined): void\n}\n\nexport type RequestAnimationFrame = (callback: (time: number) => void) => number\nexport type CancelAnimationFrame = (handle: number | null | undefined) => void\n\nexport type RequestAnimationFramePair = {\n requestAnimationFrame: RequestAnimationFrame\n cancelAnimationFrame: CancelAnimationFrame\n}\n\n/**\n * Pure factory over an injected `FrameScheduler` — the part a test drives\n * directly, without touching `globalThis`. Every request made before a\n * batch flushes lands in that SAME batch and gets the SAME timestamp\n * (rAF's usual \"one paint, one time\" contract); a request made from inside\n * a running callback is queued fresh and therefore always lands one batch\n * later, because the batch already in flight was taken off `pending` before\n * the first callback in it ran.\n */\nexport const createRequestAnimationFrame = (\n scheduler: FrameScheduler,\n): RequestAnimationFramePair => {\n let nextHandle = 1\n let pending = new Map<number, (time: number) => void>()\n let unbook: (() => void) | null = null\n\n const flush = (time: number): void => {\n unbook = null\n // Snapshot-then-clear: a callback that re-requests itself — every\n // rAF-driven animation loop does exactly that — must land in the batch\n // that starts empty again below, not in the one currently running.\n const batch = pending\n pending = new Map()\n for (const callback of batch.values()) {\n try {\n callback(time)\n } catch (error) {\n // One throwing callback does not take its siblings down with it:\n // browsers report the exception and keep running the rest of the\n // batch, and RN's own JSTimers wraps each callback in its own\n // try/catch for the same reason. See docs/api.md.\n console.error(\n \"[react-native-gtkx] requestAnimationFrame callback threw:\",\n error,\n )\n }\n }\n }\n\n const requestAnimationFrame: RequestAnimationFrame = (callback) => {\n const handle = nextHandle++\n pending.set(handle, callback)\n // Only the FIRST request in an otherwise-empty queue books a frame —\n // every other one rides the booking already pending, so N calls in the\n // same tick cost one scheduler subscription, not N.\n if (unbook === null) {\n unbook = scheduler.schedule(flush)\n }\n return handle\n }\n\n const cancelAnimationFrame: CancelAnimationFrame = (handle) => {\n // `null`/`undefined` — an unset ref, the common cleanup shape — is a\n // silent no-op, the same as an unknown or already-delivered handle.\n if (handle == null) {\n return\n }\n pending.delete(handle)\n // No idle tick once the last pending callback is gone — mirrors\n // animated/frame-loop.ts's stop(), same reason: a booking held for\n // nothing is a tick nobody asked for.\n if (pending.size === 0 && unbook !== null) {\n unbook()\n unbook = null\n }\n }\n\n return { requestAnimationFrame, cancelAnimationFrame }\n}\n\nlet installed = false\n\n/**\n * Installs requestAnimationFrame/cancelAnimationFrame as globals, running\n * the batch above off `scheduler`. Called once from the package entry\n * (src/index.ts) with the real `glibScheduler` — every app importing\n * \"react-native\" (aliased here) pulls it in before any of its own code\n * runs, on both toolchains, the same way InitializeCore would have.\n * Idempotent, because `installed` is module state: re-importing the entry\n * module a second time in the same process must not orphan whatever the\n * first install has already queued behind a fresh, disconnected id\n * namespace.\n */\nexport const installGlobalRequestAnimationFrame = (\n scheduler: FrameScheduler,\n): void => {\n if (installed) {\n return\n }\n installed = true\n const pair = createRequestAnimationFrame(scheduler)\n globalThis.requestAnimationFrame = pair.requestAnimationFrame\n globalThis.cancelAnimationFrame = pair.cancelAnimationFrame\n}\n"]}
|
|
@@ -157,6 +157,55 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
|
|
|
157
157
|
// view against, and the widget whose own kinetics a `Gesture.Native()`
|
|
158
158
|
// over this scrollable is talking about.
|
|
159
159
|
useResponder(outerRef, responderProps);
|
|
160
|
+
// `scrollTo`/`scrollToEnd` can run before GTK's OWN widget-level
|
|
161
|
+
// measure/allocate has caught up to content the ENGINE already
|
|
162
|
+
// committed — Yoga's flush is a same-microtask JS computation, while a
|
|
163
|
+
// GtkScrolledWindow only learns its child's real size on its next native
|
|
164
|
+
// allocate, which is queued (a frame away) rather than synchronous.
|
|
165
|
+
// `Gtk.Adjustment.setValue()` clamps to ITS OWN `upper`/`page-size`, so a
|
|
166
|
+
// target beyond a still-stale (freshly mounted: 0/0) range is silently
|
|
167
|
+
// absorbed as a no-op — the value does not actually change, so
|
|
168
|
+
// `value-changed` never fires and the scroll this call was supposed to
|
|
169
|
+
// produce is dropped with no event at all. Reproduced deterministically
|
|
170
|
+
// by calling `scrollTo` immediately after `render()` with no extra
|
|
171
|
+
// settling tick: the adjustment reads upper=0/page=0 while
|
|
172
|
+
// `contentNode`/`outerNode` already have the real (correct) rect.
|
|
173
|
+
//
|
|
174
|
+
// The engine's own rects are the fix: they commit before GTK's real
|
|
175
|
+
// geometry ever could (same tick vs. next frame), so reconfiguring the
|
|
176
|
+
// adjustment's range from them — right before setting the value — closes
|
|
177
|
+
// the gap using truth this component already has in hand. Only these two
|
|
178
|
+
// rare, explicitly-invoked entry points pay for it; the per-event
|
|
179
|
+
// `onAdjustment`/`readScrollEvent` path (docs/research/scroll-phases.md)
|
|
180
|
+
// is untouched.
|
|
181
|
+
const syncAdjustmentRange = (adjustment, axis) => {
|
|
182
|
+
const content = contentNode.getRect();
|
|
183
|
+
const viewport = outerNode.getRect();
|
|
184
|
+
if (!content || !viewport) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const pageSize = viewport[axis];
|
|
188
|
+
// A viewport taller/wider than its content (a short list in a tall
|
|
189
|
+
// ScrollView — routine, not an edge case) must still satisfy GTK's own
|
|
190
|
+
// `gtk_adjustment_configure` precondition (`lower + page_size <=
|
|
191
|
+
// upper`): clamping `upper` up to at least `pageSize` is the standard
|
|
192
|
+
// "nothing to scroll" range, exactly like a GtkScrollbar whose thumb
|
|
193
|
+
// fills the whole trough. Without this, a page size bigger than the
|
|
194
|
+
// content invalidates every `configure()` call — GTK logs a
|
|
195
|
+
// Gtk-CRITICAL and, per `gtk_adjustment_configure`'s own
|
|
196
|
+
// `g_return_if_fail`, leaves the adjustment UNCHANGED, so the
|
|
197
|
+
// `getUpper()`/`getPageSize()` guard above never starts matching and a
|
|
198
|
+
// caller retrying every frame (`autoscroll.tsx`'s tick callback, while
|
|
199
|
+
// active) reissues the same invalid call on every tick for as long as
|
|
200
|
+
// it stays active — a burst of thousands of identical criticals from
|
|
201
|
+
// one stuck loop rather than one-off noise.
|
|
202
|
+
const upper = Math.max(content[axis], pageSize);
|
|
203
|
+
if (adjustment.getUpper() === upper &&
|
|
204
|
+
adjustment.getPageSize() === pageSize) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
adjustment.configure(adjustment.getValue(), 0, upper, adjustment.getStepIncrement(), adjustment.getPageIncrement(), pageSize);
|
|
208
|
+
};
|
|
160
209
|
useImperativeHandle(handleRef, () => {
|
|
161
210
|
const handle = {
|
|
162
211
|
...createMeasureHandle(outerRef, outerNode),
|
|
@@ -166,10 +215,18 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
|
|
|
166
215
|
return;
|
|
167
216
|
}
|
|
168
217
|
if (y !== undefined) {
|
|
169
|
-
widget.getVadjustment()
|
|
218
|
+
const vadjustment = widget.getVadjustment();
|
|
219
|
+
if (vadjustment) {
|
|
220
|
+
syncAdjustmentRange(vadjustment, "height");
|
|
221
|
+
vadjustment.setValue(y);
|
|
222
|
+
}
|
|
170
223
|
}
|
|
171
224
|
if (x !== undefined) {
|
|
172
|
-
widget.getHadjustment()
|
|
225
|
+
const hadjustment = widget.getHadjustment();
|
|
226
|
+
if (hadjustment) {
|
|
227
|
+
syncAdjustmentRange(hadjustment, "width");
|
|
228
|
+
hadjustment.setValue(x);
|
|
229
|
+
}
|
|
173
230
|
}
|
|
174
231
|
},
|
|
175
232
|
scrollToEnd() {
|
|
@@ -178,6 +235,7 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
|
|
|
178
235
|
? widget?.getHadjustment()
|
|
179
236
|
: widget?.getVadjustment();
|
|
180
237
|
if (adjustment) {
|
|
238
|
+
syncAdjustmentRange(adjustment, horizontal ? "width" : "height");
|
|
181
239
|
adjustment.setValue(adjustment.getUpper() - adjustment.getPageSize());
|
|
182
240
|
}
|
|
183
241
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scroll-view.js","sourceRoot":"","sources":["../../src/components/scroll-view.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Children,\n createContext,\n forwardRef,\n useContext,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { splitStyle, StyleSheet } from \"../style/index\"\nimport type { StyleProp } from \"../contracts\"\nimport {\n Gdk,\n Gtk,\n GtkBox,\n GtkScrolledWindow,\n queueAllocate,\n queueResize,\n useSignal,\n} from \"../gtkx/bridge/index\"\nimport {\n ensurePerfReporter,\n perfAddTime,\n perfCount,\n perfEnabled,\n perfFrameTick,\n perfNow,\n} from \"../perf\"\nimport type { ResponderProps } from \"../responder/types\"\nimport { useResponder } from \"../responder/use-responder\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n createMeasureHandle,\n registerHandleWidget,\n type MeasureHandle,\n} from \"./measure\"\nimport { deferDuringAllocate, setStoredOffset } from \"./rect-store\"\nimport { scrollPhaseSink, type ScrollPhase } from \"./scroll-phase\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\nimport { createWheelScrollSession } from \"./wheel-scroll-session\"\n\n/**\n * The scroll half on its own, because the windowed list core builds its own\n * handle on top of it and is a COMPOSITE — it owns no widget, so it has no\n * honest geometry to report and does not claim any.\n */\nexport type ScrollHandle = {\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void\n scrollToEnd(options?: { animated?: boolean }): void\n}\n\n// RN's ScrollView ref carries the geometry methods as well as the scroll\n// ones — it is a host component, and every host component has them. Here the\n// composition also carries the widget: `registerHandleWidget` below is what\n// lets `measureLayout(scrollViewRef, …)` resolve, and what lets\n// `Animated.ScrollView` find the scrolled window to write a transform to.\nexport type ScrollViewHandle = MeasureHandle & ScrollHandle\n\nexport type ScrollEvent = {\n nativeEvent: {\n contentOffset: { x: number; y: number }\n contentSize: { width: number; height: number }\n // RN's viewport size. GTK's adjustments call it the page size, which is\n // the same thing: the extent the scrolled window shows at once. Without\n // it nothing can compute \"how far from the end am I\" — autoscroll during\n // a drag, custom end-reached logic — which is why RN carries it.\n layoutMeasurement: { width: number; height: number }\n }\n}\n\n/**\n * `ResponderProps` because a `ScrollView` is a HOST component in RN, and every\n * host component takes them. It matters more here than it reads: a\n * `GestureDetector` reaches its child through the child's own responder\n * registration, so a scrollable that did not accept them could never carry a\n * gesture — and `Gesture.Native()` exists precisely to put a scrollable into\n * the arbitration (`@gorhom/bottom-sheet` wraps its scrollable in exactly that\n * shape). `useResponder` installs nothing unless some responder prop is\n * actually present, so a plain `ScrollView` gains no controller.\n */\nexport type ScrollViewProps = ResponderProps & {\n style?: StyleProp\n contentContainerStyle?: StyleProp\n horizontal?: boolean\n // RN sticky headers (vertical lists): the children at these indices pin to\n // the top while scrolled past, each pushed out by the next one.\n stickyHeaderIndices?: readonly number[]\n onScroll?: (event: ScrollEvent) => void\n // RN's four scroll PHASES. What each one is here — and which input devices\n // produce it — is measured in docs/research/scroll-phases.md. A wheel gets\n // one synthetic begin/end pair around a burst and no momentum; a touchpad\n // glide gets all four from its real GTK sequence. `onScroll` is unaffected.\n //\n // Every one of them is OPTIONAL in the strong sense: while no phase\n // handler is attached — neither a prop here nor a Reanimated handler\n // carrying a phase sink — no controller is installed, no signal is\n // connected, and a scroll costs exactly what it cost before they existed.\n onScrollBeginDrag?: (event: ScrollEvent) => void\n onScrollEndDrag?: (event: ScrollEvent) => void\n onMomentumScrollBegin?: (event: ScrollEvent) => void\n onMomentumScrollEnd?: (event: ScrollEvent) => void\n // RN onContentSizeChange(contentWidth, contentHeight): fires when the\n // scrollable content changes size. GTK's adjustments emit \"changed\" right\n // after the allocation that resized their range — exactly the moment the\n // new scroll extent is real, which windowed lists rely on to reposition.\n onContentSizeChange?: (width: number, height: number) => void\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n testID?: string\n}\n\ntype StickyRecord = { y: number; height: number; widget: Gtk.Widget }\n\n// The sticky registry lives on the nearest ScrollView: any descendant may\n// register a slot with its content-relative geometry (measured children of\n// the ScrollView itself, or windowed list cells with engine-known offsets).\nexport type StickyRegistry = {\n register(key: string, record: StickyRecord): void\n unregister(key: string): void\n}\n\nexport const StickyRegistryContext = createContext<StickyRegistry | null>(null)\n\n// A wrapper around a sticky child: a plain container that reports its layout\n// and widget into the registry, so the scroll handler can translate/reorder\n// the REAL instance (RN semantics — no duplicate is ever rendered, external\n// margins travel with the header exactly as designed). An explicit `top`\n// (windowed cells) overrides the measured position.\nexport const StickySlot = ({\n stickyKey,\n style,\n top,\n onLayout,\n children,\n}: {\n stickyKey: string\n style?: StyleProp\n top?: number\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n}) => {\n const registry = useContext(StickyRegistryContext)\n const widgetRef = useRef<Gtk.Box | null>(null)\n const { host, node } = useLayoutChild(widgetRef, {\n style,\n onLayout: (event) => {\n onLayout?.(event)\n const widget = widgetRef.current\n if (registry && widget) {\n registry.register(stickyKey, {\n y: top ?? event.nativeEvent.layout.y,\n height: event.nativeEvent.layout.height,\n widget,\n })\n }\n },\n })\n useRnContainer(widgetRef, node)\n useLayoutEffect(() => {\n return () => {\n registry?.unregister(stickyKey)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [stickyKey])\n return (\n <GtkBox ref={widgetRef}>\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\n// GtkScrolledWindow whose child is a content GtkBox backed by its own Yoga\n// node inside the same engine: the content's RnGtkxLayout measures the engine\n// content size (which grows past the viewport — that IS the scroll range;\n// overflow: scroll on the outer node) and the scrolled window pans it.\nexport const ScrollView = forwardRef<ScrollViewHandle, ScrollViewProps>(\n (\n {\n style,\n contentContainerStyle,\n horizontal = false,\n stickyHeaderIndices,\n onScroll,\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n onContentSizeChange,\n onLayout,\n children,\n testID,\n ...responderProps\n },\n handleRef,\n ) => {\n const outerRef = useRef<Gtk.ScrolledWindow | null>(null)\n const contentRef = useRef<Gtk.Box | null>(null)\n const [scrolled, setScrolled] = useState<Gtk.ScrolledWindow | null>(null)\n\n const { host, node: outerNode } = useLayoutChild(outerRef, {\n // Yoga unconstrains a scroll node's MAIN axis — align it with the\n // scroll direction or a horizontal list's content clamps to the\n // viewport width (hadjustment upper == page).\n //\n // `flexGrow: 1, flexShrink: 1` is RN's own base style for the scroller\n // (`styles.baseVertical` / `baseHorizontal` in ScrollView.js, applied as\n // `StyleSheet.compose(baseStyle, this.props.style)` — hence base first,\n // the app's style wins). The shrink half is what turns a scrollable with\n // NO style of its own into a viewport: a flex item defaults to\n // flexShrink 0 on RN's Yoga config, so without this the scroller sizes\n // to its content, outgrows a bounded parent and its scroll range stays\n // empty. It cost a whole investigation once: `@gorhom/bottom-sheet`\n // hands its list down unstyled, so the sheet's scroll lock had nothing\n // to lock — the list never received a scroll event because it had never\n // become a viewport.\n //\n // Composing base-first does NOT protect an explicit `height`: grow and\n // height are different properties, so nothing overrides anything — the\n // height is the flex BASIS and grow expands from it, and a scroller with\n // `height: 200` in a 400px column lays out at 400. RN does the same\n // (same base, same order, same node, same Yoga resolution), so this is\n // parity and not a bug; docs/api.md says so where people will look.\n // Bound the PARENT to bound the viewport.\n style: [\n { flexGrow: 1, flexShrink: 1 },\n style,\n { overflow: \"scroll\", flexDirection: horizontal ? \"row\" : \"column\" },\n ],\n onLayout,\n // RN semantics: a ScrollView CLIPS its content. Containers paint-\n // overflow by default, so without this the scrolled-away content (e.g.\n // the original of a pinned sticky header) keeps drawing past the scroll\n // area's edges. The `overflow: \"scroll\"` above would now clip the\n // viewport on its own, but a viewport that clips only while nobody\n // overrides the style is not a contract.\n alwaysClips: true,\n })\n\n const [contentNode] = useState(() => host.engine.createNode())\n // Sticky positions are computed INSIDE the same allocation pass that\n // places the children (beforeAllocate): the viewport has already settled\n // on this frame's scroll offset, so the pinned header lands exactly —\n // reacting to the adjustment signal instead lags a frame and jitters.\n useRnContainer(contentRef, contentNode, {\n beforeAllocate: () => {\n if (stickyRecords.current.size > 0) {\n updateSticky(outerRef.current?.getVadjustment()?.getValue() ?? 0)\n }\n },\n })\n\n useLayoutEffect(() => {\n outerNode.insertChild(contentNode, 0)\n contentNode.setCommit(() => {\n // The content size IS this widget's measure (scroll range) — the\n // ScrolledWindow itself allocates the content widget.\n const widget = contentRef.current\n if (!widget) {\n return\n }\n const queue = (): void => {\n queueResize(widget)\n }\n if (!deferDuringAllocate(widget, queue)) {\n queue()\n }\n })\n return () => {\n contentNode.setCommit(null)\n outerNode.removeChild(contentNode)\n contentNode.free()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const contentLayout = splitStyle(\n StyleSheet.flatten(contentContainerStyle),\n ).layout\n const contentKey = JSON.stringify({ horizontal, contentLayout })\n useLayoutEffect(() => {\n contentNode.setStyle({\n flexDirection: horizontal ? \"row\" : \"column\",\n // RN's content container is a plain `View`, and a `View`'s default\n // `alignItems` is `stretch` — so a vertical ScrollView's children are\n // as wide as the viewport unless they say otherwise. This used to be\n // `flex-start`, which made every child shrink to its intrinsic width\n // and any `flex: 1` inside it collapse to zero.\n //\n // It was found twice. First by `Sortable`, whose rows rendered as\n // bare drag handles with no text (docs/research/drag-and-drop.md\n // recorded it as a probable ScrollView parity bug and did not act on\n // it, because changing a shared default under every example was not\n // that epic's business). Then by porting\n // `react-native-reanimated-dnd`'s example app, where it broke\n // seventeen screens at once — at which point \"a ported app changes\n // nothing in its source\" was simply false, and the default was the\n // thing that was wrong.\n //\n // An app that wants the old behaviour writes it, exactly as it would\n // on iOS and Android: `contentContainerStyle={{ alignItems:\n // \"flex-start\" }}`.\n alignItems: \"stretch\",\n ...contentLayout,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [contentNode, contentKey])\n\n // On the SCROLLED WINDOW, not on the content box: that is the widget the\n // handle stands for, the widget a `GestureDetector` measures its gesture's\n // view against, and the widget whose own kinetics a `Gesture.Native()`\n // over this scrollable is talking about.\n useResponder(outerRef, responderProps)\n\n useImperativeHandle(handleRef, () => {\n const handle: ScrollViewHandle = {\n ...createMeasureHandle(outerRef, outerNode),\n\n scrollTo({ x, y }) {\n const widget = outerRef.current\n if (!widget) {\n return\n }\n if (y !== undefined) {\n widget.getVadjustment()?.setValue(y)\n }\n if (x !== undefined) {\n widget.getHadjustment()?.setValue(x)\n }\n },\n scrollToEnd() {\n const widget = outerRef.current\n const adjustment = horizontal\n ? widget?.getHadjustment()\n : widget?.getVadjustment()\n if (adjustment) {\n adjustment.setValue(\n adjustment.getUpper() - adjustment.getPageSize(),\n )\n }\n },\n }\n // Spreading built a NEW object, and the widget lookup is keyed by\n // handle identity — without this the composed handle is a stranger to\n // measure.ts.\n registerHandleWidget(handle, outerRef)\n return handle\n })\n\n // --- sticky headers -------------------------------------------------\n // The RN model, faithfully: the REAL child is translated (no duplicate,\n // state preserved). RN gives the pinned cell a zIndex; this reorders the\n // active slot to be the LAST content child while pinned and restores it\n // afterwards, which predates `zIndex` working (gtkx/bridge/view-box.ts)\n // and is kept because it is exact: the slots here are the ScrollView's own\n // and their Yoga order is rebuilt from the same list, so the reorder that\n // would be a hazard for arbitrary app children is not one for these. Per-frame movement\n // goes through the rect-store offset fast path — zero React work while\n // scrolling.\n const stickySet = stickyHeaderIndices ?? []\n const stickyRecords = useRef(new Map<string, StickyRecord>())\n const restoreSibling = useRef(new Map<string, Gtk.Widget | null>())\n const activeStickyRef = useRef<string | null>(null)\n const [hasSticky, setHasSticky] = useState(false)\n\n const stickyRegistry = useRef<StickyRegistry>({\n register: (key, record) => {\n stickyRecords.current.set(key, record)\n setHasSticky(true)\n const parent = record.widget.getParent()\n if (parent) {\n queueAllocate(parent)\n }\n },\n unregister: (key) => {\n stickyRecords.current.delete(key)\n restoreSibling.current.delete(key)\n if (activeStickyRef.current === key) {\n activeStickyRef.current = null\n }\n if (stickyRecords.current.size === 0) {\n setHasSticky(false)\n }\n },\n })\n\n const updateSticky = (rawScrollTop: number): void => {\n if (stickyRecords.current.size === 0) {\n return\n }\n // The viewport translates the content by the FRACTIONAL adjustment\n // value while widget allocations are integer — an unquantized pin\n // disagrees with the translation by ±1px and shimmers every frame.\n // Frame telemetry (docs/research/sticky-probe.md) proved the viewport quantizes\n // like floor: with Math.floor the header's window-relative position is\n // a flat 0.00 across every frame; round oscillates 0/1, ceil sits at 1.\n const scrollTop = Math.floor(rawScrollTop)\n let active: string | null = null\n let next: StickyRecord | null = null\n for (const [key, record] of stickyRecords.current) {\n if (record.y <= scrollTop) {\n if (\n active === null ||\n record.y >= stickyRecords.current.get(active)!.y\n ) {\n active = key\n }\n } else if (next === null || record.y < next.y) {\n next = record\n }\n }\n\n const previous = activeStickyRef.current\n if (previous !== active) {\n activeStickyRef.current = active\n if (previous !== null) {\n const record = stickyRecords.current.get(previous)\n if (record) {\n setStoredOffset(record.widget, 0, 0)\n }\n }\n // Reordering widgets queues GTK work — not allowed mid-allocation.\n // The z-switch lands a frame later; the POSITION is already exact.\n setTimeout(() => {\n applyStickyReorder(previous, active)\n }, 0)\n }\n\n if (active !== null) {\n const record = stickyRecords.current.get(active)!\n // Pin at the viewport top; the next sticky header pushes it out.\n const pinned = next\n ? Math.min(scrollTop, next.y - record.height)\n : scrollTop\n setStoredOffset(record.widget, 0, Math.max(0, pinned - record.y))\n }\n }\n\n const applyStickyReorder = (\n previous: string | null,\n active: string | null,\n ): void => {\n if (previous !== null) {\n const record = stickyRecords.current.get(previous)\n // Slot parents are always our GtkBox containers.\n const parent = record?.widget.getParent() as Gtk.Box | null\n if (record && parent) {\n // Put the slot back where the reconciler expects it.\n parent.reorderChildAfter(\n record.widget,\n restoreSibling.current.get(previous) ?? null,\n )\n queueAllocate(parent)\n }\n }\n if (active !== null) {\n const record = stickyRecords.current.get(active)\n const parent = record?.widget.getParent() as Gtk.Box | null\n if (record && parent) {\n restoreSibling.current.set(active, record.widget.getPrevSibling())\n parent.reorderChildAfter(record.widget, parent.getLastChild())\n queueAllocate(parent)\n }\n }\n }\n\n // The one place the payload is read off the widget, shared by `onScroll`\n // and by all four phases — RN hands the same `ScrollEvent` to every one\n // of them, and reading it twice would be two sets of FFI calls for one\n // truth. Null when there is nothing honest to report yet (no widget, or\n // a content size the engine has not committed).\n const readScrollEvent = (): ScrollEvent | null => {\n const widget = outerRef.current\n const contentRect = contentNode.getRect()\n if (!widget || !contentRect) {\n return null\n }\n const hadjustment = widget.getHadjustment()\n const vadjustment = widget.getVadjustment()\n return {\n nativeEvent: {\n contentOffset: {\n x: hadjustment?.getValue() ?? 0,\n y: vadjustment?.getValue() ?? 0,\n },\n contentSize: { width: contentRect.width, height: contentRect.height },\n layoutMeasurement: {\n width: hadjustment?.getPageSize() ?? 0,\n height: vadjustment?.getPageSize() ?? 0,\n },\n },\n }\n }\n\n const emitScroll = (): void => {\n if (!onScroll) {\n return\n }\n const event = readScrollEvent()\n if (event) {\n onScroll(event)\n }\n }\n\n const adjustment = horizontal\n ? (scrolled?.getHadjustment() ?? null)\n : (scrolled?.getVadjustment() ?? null)\n const onAdjustment = (): void => {\n if (!perfEnabled) {\n emitScroll()\n return\n }\n // Perf: full wall time of one scroll tick's JS work — the FFI reads in\n // emitScroll plus the app/VirtualizedList onScroll handler (windowing,\n // viewability, endReached) — everything that blocks the GTK frame.\n perfCount(\"scroll.events\")\n const start = perfNow()\n emitScroll()\n perfAddTime(\"scroll.js\", perfNow() - start)\n }\n\n // --- the four scroll phases -----------------------------------------\n //\n // What GTK actually reports, measured on 4.22.4 under a real pointer\n // (docs/research/scroll-phases.md, and the trace is in\n // tests/gtk/components/scroll-phases.gtk.test.tsx):\n //\n // - a mouse WHEEL emits `::scroll` per detent and NOTHING around it.\n // GTK gives us no sequence, so while a consumer asks for begin/end we\n // synthesize a desktop scroll SESSION: begin before the first detent\n // mutates the adjustment, end after a measured idle window, and no\n // momentum. This is an intentional desktop extension — RN has no\n // wheel — and is what lets a phase-aware consumer capture the offset\n // it is about to constrain instead of discovering it one event late.\n // - a touchpad GLIDE emits `::scroll-begin`, a stream of `::scroll`,\n // `::scroll-end` and `::decelerate` — a real sequence with a real\n // beginning and end — and the scrolled window's own kinetic\n // animation then carries the adjustment on for another second or so.\n // All four phases exist, and all four are reported.\n //\n // Drag maps onto the scroll SEQUENCE rather than onto a finger on the\n // content: `::scroll-begin` is \"the user started driving this scroller\",\n // which is what a consumer of `onScrollBeginDrag` acts on and is as close\n // as a device that never touches the content can get. That is the one\n // approximation here, and it is named in docs/api.md.\n //\n // Momentum is read off the ADJUSTMENT, not off `::decelerate`. The\n // controller emits `decelerate` at every `scroll-end` — it reports the\n // velocity it measured, not a decision to coast — while the scrolled\n // window decides for itself whether that velocity is worth animating. RN\n // fires `onMomentumScrollBegin` only when the view actually keeps\n // moving, so the honest source is the movement itself.\n const phaseSink = scrollPhaseSink(onScroll)\n // `wants()` rather than the sink's mere presence: every Reanimated scroll\n // handler carries one, and most of them ask for nothing but `onScroll`.\n // Asking is what keeps the common case free.\n const wantsPhases = Boolean(\n onScrollBeginDrag ||\n onScrollEndDrag ||\n onMomentumScrollBegin ||\n onMomentumScrollEnd ||\n phaseSink?.wants(),\n )\n // GTK brackets touchpad gestures for us. A wheel has only detents, so the\n // begin/end extension below needs to see those detents — but only when a\n // consumer asks for either end of the session. A momentum-only handler\n // still pays no JS call per wheel event, because a wheel never coasts.\n const wantsWheelSessions = Boolean(\n onScrollBeginDrag ||\n onScrollEndDrag ||\n phaseSink?.wants(\"beginDrag\") ||\n phaseSink?.wants(\"endDrag\"),\n )\n\n // Read per event rather than captured, exactly as `useAnimatedScrollHandler`\n // does it: a re-render that changed a handler must be picked up without\n // tearing the GTK controller down and building a new one.\n const phaseHandlers = useRef({\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n phaseSink,\n readScrollEvent,\n })\n phaseHandlers.current = {\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n phaseSink,\n readScrollEvent,\n }\n\n useLayoutEffect(() => {\n // The whole cost story is this early return. No phase handler means no\n // controller, no signal, no tick callback and no bookkeeping — the\n // widget is byte-for-byte the one this component built before the\n // phases existed.\n if (!wantsPhases) {\n return\n }\n const widget = scrolled\n if (!widget) {\n return\n }\n\n const emitPhase = (phase: ScrollPhase): void => {\n const handlers = phaseHandlers.current\n const event = handlers.readScrollEvent()\n if (!event) {\n return\n }\n if (phase === \"beginDrag\") {\n handlers.onScrollBeginDrag?.(event)\n } else if (phase === \"endDrag\") {\n handlers.onScrollEndDrag?.(event)\n } else if (phase === \"momentumBegin\") {\n handlers.onMomentumScrollBegin?.(event)\n } else {\n handlers.onMomentumScrollEnd?.(event)\n }\n handlers.phaseSink?.deliver(phase, event)\n }\n\n const axis = horizontal\n ? widget.getHadjustment()\n : widget.getVadjustment()\n\n // Momentum is watched on the ADJUSTMENT, with two timers and no tick\n // callback. A frame-polling watcher was the first shape and it was\n // wrong twice over: it burns a callback per frame for the length of\n // every coast, and it counts FRAMES, which is not a unit — under the\n // headless compositor the frame clock free-runs at ~106 ticks per\n // millisecond (measured, docs/research/scroll-phases.md), so a\n // four-frame grace expired in microseconds. Wall time means the same\n // thing everywhere.\n //\n // Milliseconds to wait after `::scroll-end` for the scrolled window's\n // kinetic animation to take over before concluding it never will.\n // Measured handoff: `::scroll-end` at 1921.9 ms, first inertial value\n // change at 1927.5 ms — 5.6 ms. 120 is slack, not a guess.\n const handoffMs = 120\n // Milliseconds of a motionless adjustment that end the momentum. GTK's\n // deceleration steps the value every frame right to the end (16.7 ms\n // apart in the trace), so 60 ms is three and a half missed steps.\n const restMs = 60\n\n let watching = false\n let coasting = false\n let lastMoveAt = 0\n let handoffTimer: ReturnType<typeof setTimeout> | null = null\n let restTimer: ReturnType<typeof setTimeout> | null = null\n\n const clearTimers = (): void => {\n if (handoffTimer !== null) {\n clearTimeout(handoffTimer)\n handoffTimer = null\n }\n if (restTimer !== null) {\n clearTimeout(restTimer)\n restTimer = null\n }\n }\n\n // Re-armed for the REMAINING idle time rather than reset on every step,\n // so the number of timers a coast costs is bounded by its duration\n // (one per `restMs`) and not by how many times the adjustment moved.\n // The difference is not academic: under the headless compositor's\n // free-running frame clock a single coast steps the value ~150,000\n // times, and a clear-and-rearm per step is 150,000 timers.\n const checkRest = (): void => {\n const idle = perfNow() - lastMoveAt\n if (idle < restMs) {\n restTimer = setTimeout(checkRest, restMs - idle)\n return\n }\n restTimer = null\n stopMomentumWatch()\n emitPhase(\"momentumEnd\")\n }\n\n const onCoastStep = (): void => {\n lastMoveAt = perfNow()\n if (coasting) {\n return\n }\n coasting = true\n if (handoffTimer !== null) {\n clearTimeout(handoffTimer)\n handoffTimer = null\n }\n emitPhase(\"momentumBegin\")\n restTimer = setTimeout(checkRest, restMs)\n }\n\n const stopMomentumWatch = (): void => {\n clearTimers()\n if (watching) {\n axis?.off(\"value-changed\", onCoastStep)\n watching = false\n }\n coasting = false\n }\n\n const watchMomentum = (): void => {\n if (watching) {\n return\n }\n watching = true\n axis?.on(\"value-changed\", onCoastStep)\n handoffTimer = setTimeout(() => {\n handoffTimer = null\n if (!coasting) {\n // The scroller came to rest with the drag. No inertia, so no\n // momentum pair — which is RN's behaviour too.\n stopMomentumWatch()\n }\n }, handoffMs)\n }\n\n const controller = Gtk.EventControllerScroll.new(\n // An axis flag is REQUIRED, and not for the deltas: measured, a\n // controller created with `KINETIC` alone emits nothing at all — not\n // even `::scroll-begin`. `::scroll` is therefore emitted. It stays\n // unconnected unless somebody asks for begin/end: only then does a\n // wheel detent enter JS to maintain the desktop session below; a\n // momentum-only or untracked scroller keeps the old empty C signal\n // path.\n Gtk.EventControllerScrollFlags.BOTH_AXES |\n Gtk.EventControllerScrollFlags.KINETIC,\n )\n // CAPTURE, so the phase is reported before the scrolled window's own\n // controller acts on the same event and moves the adjustment. Nothing\n // here handles the event, so propagation is untouched either way.\n controller.setPropagationPhase(Gtk.PropagationPhase.CAPTURE)\n\n const beginDrag = (): void => {\n // A new drag during a coast: RN ends the momentum before beginning\n // the drag, so a consumer never sees two live phases at once.\n if (coasting) {\n stopMomentumWatch()\n emitPhase(\"momentumEnd\")\n } else {\n stopMomentumWatch()\n }\n emitPhase(\"beginDrag\")\n }\n // A wheel gives GTK no begin/end signals. Group its detents into one\n // user-driven session, ending after an idle interval longer than the\n // ordinary detent spacing measured in docs/research/scroll-phases.md\n // (20–33 ms in the trace). The callback runs in CAPTURE, so beginDrag\n // sees the PRE-scroll offset before GtkScrolledWindow mutates its\n // adjustment and emits the onScroll callback that consumes the context.\n const wheelSession = createWheelScrollSession(beginDrag, () =>\n emitPhase(\"endDrag\"),\n )\n // A touchpad brackets itself, so its sequence owns the session while it\n // runs. Two live sessions at once would hand a consumer a second\n // `beginDrag` before the first `endDrag` — which is exactly the state\n // gorhom's lock reads to decide where to pin its list.\n let nativeSequence = false\n const onScrollBegin = (): void => {\n nativeSequence = true\n // A wheel burst may still be waiting out its idle timer.\n wheelSession.finish()\n beginDrag()\n }\n const onScrollEnd = (): void => {\n nativeSequence = false\n emitPhase(\"endDrag\")\n watchMomentum()\n }\n const onScroll = (): boolean => {\n if (\n !wantsWheelSessions ||\n nativeSequence ||\n controller.getUnit() !== Gdk.ScrollUnit.WHEEL\n ) {\n return false\n }\n wheelSession.detent()\n // Observe only. GtkScrolledWindow still receives and applies the\n // detent, which is what makes this phase seam transparent.\n return false\n }\n\n controller.on(\"scroll-begin\", onScrollBegin)\n controller.on(\"scroll-end\", onScrollEnd)\n if (wantsWheelSessions) {\n controller.on(\"scroll\", onScroll)\n }\n widget.addController(controller)\n\n return () => {\n controller.off(\"scroll-begin\", onScrollBegin)\n controller.off(\"scroll-end\", onScrollEnd)\n if (wantsWheelSessions) {\n controller.off(\"scroll\", onScroll)\n }\n wheelSession.dispose()\n widget.removeController(controller)\n stopMomentumWatch()\n }\n }, [scrolled, wantsPhases, wantsWheelSessions, horizontal])\n\n // Content-size reports dedupe on the engine rect: \"changed\" also fires\n // for pure viewport (page-size) changes, which RN does not report.\n const lastContentSize = useRef({ width: -1, height: -1 })\n const onRangeChanged = (): void => {\n if (!onContentSizeChange) {\n return\n }\n const rect = contentNode.getRect()\n if (!rect) {\n return\n }\n const last = lastContentSize.current\n if (rect.width === last.width && rect.height === last.height) {\n return\n }\n lastContentSize.current = { width: rect.width, height: rect.height }\n onContentSizeChange(rect.width, rect.height)\n }\n\n // Frame-synced sticky driver: a pure scroll TRANSLATES the content\n // without re-allocating it (same size → GTK skips the vfunc), so signal\n // handlers land a frame late and the pinned header jitters. A tick\n // callback runs in the frame's UPDATE phase, before layout: when the\n // scroll offset moved, queue an allocation for THIS frame — the\n // translation and the pin correction then paint atomically.\n useLayoutEffect(() => {\n if (!hasSticky || horizontal) {\n return\n }\n const widget = scrolled\n if (!widget) {\n return\n }\n const vadjustment = widget.getVadjustment()\n let lastValue = vadjustment?.getValue() ?? 0\n // Frames to keep requeueing after the last observed move. The scrolled\n // window's own kinetic tick may run AFTER ours, so this tick's read can\n // be one frame stale: without the grace window the first frame of a\n // deceleration (its value change lands after our read) would miss its\n // correction. Kinetic deceleration changes the value every frame, so\n // three frames of slack cover the handoff at both ends of the motion.\n const graceFrames = 3\n let remaining = graceFrames\n const id = widget.addTickCallback(() => {\n perfCount(\"sticky.ticks\")\n const value = vadjustment?.getValue() ?? 0\n if (value !== lastValue) {\n lastValue = value\n remaining = graceFrames\n } else if (remaining > 0) {\n remaining -= 1\n } else {\n // At rest: the pin is already correct and nothing translated it,\n // so an allocation pass per frame would be pure idle cost.\n return true\n }\n perfCount(\"sticky.queues\")\n const content = contentRef.current\n if (content) {\n queueAllocate(content)\n }\n return true\n })\n return () => {\n widget.removeTickCallback(id)\n }\n }, [scrolled, hasSticky, horizontal])\n\n // Perf: observe the GTK frame clock through a tick callback — the delta\n // between consecutive ticks is the real frame interval (late frames show\n // up directly). Note: registering a tick callback keeps the frame clock\n // running, so idle seconds show ~60 ticks too; the reporter separates\n // load by the accompanying counters.\n useLayoutEffect(() => {\n if (!perfEnabled) {\n return\n }\n ensurePerfReporter()\n const widget = scrolled\n if (!widget) {\n return\n }\n const id = widget.addTickCallback(() => {\n perfFrameTick(perfNow())\n return true\n })\n return () => {\n widget.removeTickCallback(id)\n }\n }, [scrolled])\n useSignal(adjustment, \"value-changed\", onAdjustment)\n // Both axes report into the same handler — RN's callback carries both\n // dimensions no matter which one moved.\n useSignal(scrolled?.getVadjustment() ?? null, \"changed\", onRangeChanged)\n useSignal(scrolled?.getHadjustment() ?? null, \"changed\", onRangeChanged)\n\n return (\n <GtkScrolledWindow\n ref={(widget: Gtk.ScrolledWindow | null) => {\n outerRef.current = widget\n setScrolled(widget)\n }}\n name={testID}\n >\n <GtkBox ref={contentRef}>\n <HostNodeContext.Provider\n value={{\n engine: host.engine,\n node: contentNode,\n widgetRef: contentRef,\n }}\n >\n <StickyRegistryContext.Provider value={stickyRegistry.current}>\n {stickySet.length === 0\n ? children\n : Children.toArray(children).map((child, index) =>\n stickySet.includes(index) ? (\n <StickySlot\n key={`sticky-slot-${index}`}\n stickyKey={`index-${index}`}\n >\n {child}\n </StickySlot>\n ) : (\n child\n ),\n )}\n </StickyRegistryContext.Provider>\n </HostNodeContext.Provider>\n </GtkBox>\n </GtkScrolledWindow>\n )\n },\n)\nScrollView.displayName = \"ScrollView\"\n"]}
|
|
1
|
+
{"version":3,"file":"scroll-view.js","sourceRoot":"","sources":["../../src/components/scroll-view.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Children,\n createContext,\n forwardRef,\n useContext,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { splitStyle, StyleSheet } from \"../style/index\"\nimport type { StyleProp } from \"../contracts\"\nimport {\n Gdk,\n Gtk,\n GtkBox,\n GtkScrolledWindow,\n queueAllocate,\n queueResize,\n useSignal,\n} from \"../gtkx/bridge/index\"\nimport {\n ensurePerfReporter,\n perfAddTime,\n perfCount,\n perfEnabled,\n perfFrameTick,\n perfNow,\n} from \"../perf\"\nimport type { ResponderProps } from \"../responder/types\"\nimport { useResponder } from \"../responder/use-responder\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n createMeasureHandle,\n registerHandleWidget,\n type MeasureHandle,\n} from \"./measure\"\nimport { deferDuringAllocate, setStoredOffset } from \"./rect-store\"\nimport { scrollPhaseSink, type ScrollPhase } from \"./scroll-phase\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\nimport { createWheelScrollSession } from \"./wheel-scroll-session\"\n\n/**\n * The scroll half on its own, because the windowed list core builds its own\n * handle on top of it and is a COMPOSITE — it owns no widget, so it has no\n * honest geometry to report and does not claim any.\n */\nexport type ScrollHandle = {\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void\n scrollToEnd(options?: { animated?: boolean }): void\n}\n\n// RN's ScrollView ref carries the geometry methods as well as the scroll\n// ones — it is a host component, and every host component has them. Here the\n// composition also carries the widget: `registerHandleWidget` below is what\n// lets `measureLayout(scrollViewRef, …)` resolve, and what lets\n// `Animated.ScrollView` find the scrolled window to write a transform to.\nexport type ScrollViewHandle = MeasureHandle & ScrollHandle\n\nexport type ScrollEvent = {\n nativeEvent: {\n contentOffset: { x: number; y: number }\n contentSize: { width: number; height: number }\n // RN's viewport size. GTK's adjustments call it the page size, which is\n // the same thing: the extent the scrolled window shows at once. Without\n // it nothing can compute \"how far from the end am I\" — autoscroll during\n // a drag, custom end-reached logic — which is why RN carries it.\n layoutMeasurement: { width: number; height: number }\n }\n}\n\n/**\n * `ResponderProps` because a `ScrollView` is a HOST component in RN, and every\n * host component takes them. It matters more here than it reads: a\n * `GestureDetector` reaches its child through the child's own responder\n * registration, so a scrollable that did not accept them could never carry a\n * gesture — and `Gesture.Native()` exists precisely to put a scrollable into\n * the arbitration (`@gorhom/bottom-sheet` wraps its scrollable in exactly that\n * shape). `useResponder` installs nothing unless some responder prop is\n * actually present, so a plain `ScrollView` gains no controller.\n */\nexport type ScrollViewProps = ResponderProps & {\n style?: StyleProp\n contentContainerStyle?: StyleProp\n horizontal?: boolean\n // RN sticky headers (vertical lists): the children at these indices pin to\n // the top while scrolled past, each pushed out by the next one.\n stickyHeaderIndices?: readonly number[]\n onScroll?: (event: ScrollEvent) => void\n // RN's four scroll PHASES. What each one is here — and which input devices\n // produce it — is measured in docs/research/scroll-phases.md. A wheel gets\n // one synthetic begin/end pair around a burst and no momentum; a touchpad\n // glide gets all four from its real GTK sequence. `onScroll` is unaffected.\n //\n // Every one of them is OPTIONAL in the strong sense: while no phase\n // handler is attached — neither a prop here nor a Reanimated handler\n // carrying a phase sink — no controller is installed, no signal is\n // connected, and a scroll costs exactly what it cost before they existed.\n onScrollBeginDrag?: (event: ScrollEvent) => void\n onScrollEndDrag?: (event: ScrollEvent) => void\n onMomentumScrollBegin?: (event: ScrollEvent) => void\n onMomentumScrollEnd?: (event: ScrollEvent) => void\n // RN onContentSizeChange(contentWidth, contentHeight): fires when the\n // scrollable content changes size. GTK's adjustments emit \"changed\" right\n // after the allocation that resized their range — exactly the moment the\n // new scroll extent is real, which windowed lists rely on to reposition.\n onContentSizeChange?: (width: number, height: number) => void\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n testID?: string\n}\n\ntype StickyRecord = { y: number; height: number; widget: Gtk.Widget }\n\n// The sticky registry lives on the nearest ScrollView: any descendant may\n// register a slot with its content-relative geometry (measured children of\n// the ScrollView itself, or windowed list cells with engine-known offsets).\nexport type StickyRegistry = {\n register(key: string, record: StickyRecord): void\n unregister(key: string): void\n}\n\nexport const StickyRegistryContext = createContext<StickyRegistry | null>(null)\n\n// A wrapper around a sticky child: a plain container that reports its layout\n// and widget into the registry, so the scroll handler can translate/reorder\n// the REAL instance (RN semantics — no duplicate is ever rendered, external\n// margins travel with the header exactly as designed). An explicit `top`\n// (windowed cells) overrides the measured position.\nexport const StickySlot = ({\n stickyKey,\n style,\n top,\n onLayout,\n children,\n}: {\n stickyKey: string\n style?: StyleProp\n top?: number\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n}) => {\n const registry = useContext(StickyRegistryContext)\n const widgetRef = useRef<Gtk.Box | null>(null)\n const { host, node } = useLayoutChild(widgetRef, {\n style,\n onLayout: (event) => {\n onLayout?.(event)\n const widget = widgetRef.current\n if (registry && widget) {\n registry.register(stickyKey, {\n y: top ?? event.nativeEvent.layout.y,\n height: event.nativeEvent.layout.height,\n widget,\n })\n }\n },\n })\n useRnContainer(widgetRef, node)\n useLayoutEffect(() => {\n return () => {\n registry?.unregister(stickyKey)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [stickyKey])\n return (\n <GtkBox ref={widgetRef}>\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\n// GtkScrolledWindow whose child is a content GtkBox backed by its own Yoga\n// node inside the same engine: the content's RnGtkxLayout measures the engine\n// content size (which grows past the viewport — that IS the scroll range;\n// overflow: scroll on the outer node) and the scrolled window pans it.\nexport const ScrollView = forwardRef<ScrollViewHandle, ScrollViewProps>(\n (\n {\n style,\n contentContainerStyle,\n horizontal = false,\n stickyHeaderIndices,\n onScroll,\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n onContentSizeChange,\n onLayout,\n children,\n testID,\n ...responderProps\n },\n handleRef,\n ) => {\n const outerRef = useRef<Gtk.ScrolledWindow | null>(null)\n const contentRef = useRef<Gtk.Box | null>(null)\n const [scrolled, setScrolled] = useState<Gtk.ScrolledWindow | null>(null)\n\n const { host, node: outerNode } = useLayoutChild(outerRef, {\n // Yoga unconstrains a scroll node's MAIN axis — align it with the\n // scroll direction or a horizontal list's content clamps to the\n // viewport width (hadjustment upper == page).\n //\n // `flexGrow: 1, flexShrink: 1` is RN's own base style for the scroller\n // (`styles.baseVertical` / `baseHorizontal` in ScrollView.js, applied as\n // `StyleSheet.compose(baseStyle, this.props.style)` — hence base first,\n // the app's style wins). The shrink half is what turns a scrollable with\n // NO style of its own into a viewport: a flex item defaults to\n // flexShrink 0 on RN's Yoga config, so without this the scroller sizes\n // to its content, outgrows a bounded parent and its scroll range stays\n // empty. It cost a whole investigation once: `@gorhom/bottom-sheet`\n // hands its list down unstyled, so the sheet's scroll lock had nothing\n // to lock — the list never received a scroll event because it had never\n // become a viewport.\n //\n // Composing base-first does NOT protect an explicit `height`: grow and\n // height are different properties, so nothing overrides anything — the\n // height is the flex BASIS and grow expands from it, and a scroller with\n // `height: 200` in a 400px column lays out at 400. RN does the same\n // (same base, same order, same node, same Yoga resolution), so this is\n // parity and not a bug; docs/api.md says so where people will look.\n // Bound the PARENT to bound the viewport.\n style: [\n { flexGrow: 1, flexShrink: 1 },\n style,\n { overflow: \"scroll\", flexDirection: horizontal ? \"row\" : \"column\" },\n ],\n onLayout,\n // RN semantics: a ScrollView CLIPS its content. Containers paint-\n // overflow by default, so without this the scrolled-away content (e.g.\n // the original of a pinned sticky header) keeps drawing past the scroll\n // area's edges. The `overflow: \"scroll\"` above would now clip the\n // viewport on its own, but a viewport that clips only while nobody\n // overrides the style is not a contract.\n alwaysClips: true,\n })\n\n const [contentNode] = useState(() => host.engine.createNode())\n // Sticky positions are computed INSIDE the same allocation pass that\n // places the children (beforeAllocate): the viewport has already settled\n // on this frame's scroll offset, so the pinned header lands exactly —\n // reacting to the adjustment signal instead lags a frame and jitters.\n useRnContainer(contentRef, contentNode, {\n beforeAllocate: () => {\n if (stickyRecords.current.size > 0) {\n updateSticky(outerRef.current?.getVadjustment()?.getValue() ?? 0)\n }\n },\n })\n\n useLayoutEffect(() => {\n outerNode.insertChild(contentNode, 0)\n contentNode.setCommit(() => {\n // The content size IS this widget's measure (scroll range) — the\n // ScrolledWindow itself allocates the content widget.\n const widget = contentRef.current\n if (!widget) {\n return\n }\n const queue = (): void => {\n queueResize(widget)\n }\n if (!deferDuringAllocate(widget, queue)) {\n queue()\n }\n })\n return () => {\n contentNode.setCommit(null)\n outerNode.removeChild(contentNode)\n contentNode.free()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const contentLayout = splitStyle(\n StyleSheet.flatten(contentContainerStyle),\n ).layout\n const contentKey = JSON.stringify({ horizontal, contentLayout })\n useLayoutEffect(() => {\n contentNode.setStyle({\n flexDirection: horizontal ? \"row\" : \"column\",\n // RN's content container is a plain `View`, and a `View`'s default\n // `alignItems` is `stretch` — so a vertical ScrollView's children are\n // as wide as the viewport unless they say otherwise. This used to be\n // `flex-start`, which made every child shrink to its intrinsic width\n // and any `flex: 1` inside it collapse to zero.\n //\n // It was found twice. First by `Sortable`, whose rows rendered as\n // bare drag handles with no text (docs/research/drag-and-drop.md\n // recorded it as a probable ScrollView parity bug and did not act on\n // it, because changing a shared default under every example was not\n // that epic's business). Then by porting\n // `react-native-reanimated-dnd`'s example app, where it broke\n // seventeen screens at once — at which point \"a ported app changes\n // nothing in its source\" was simply false, and the default was the\n // thing that was wrong.\n //\n // An app that wants the old behaviour writes it, exactly as it would\n // on iOS and Android: `contentContainerStyle={{ alignItems:\n // \"flex-start\" }}`.\n alignItems: \"stretch\",\n ...contentLayout,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [contentNode, contentKey])\n\n // On the SCROLLED WINDOW, not on the content box: that is the widget the\n // handle stands for, the widget a `GestureDetector` measures its gesture's\n // view against, and the widget whose own kinetics a `Gesture.Native()`\n // over this scrollable is talking about.\n useResponder(outerRef, responderProps)\n\n // `scrollTo`/`scrollToEnd` can run before GTK's OWN widget-level\n // measure/allocate has caught up to content the ENGINE already\n // committed — Yoga's flush is a same-microtask JS computation, while a\n // GtkScrolledWindow only learns its child's real size on its next native\n // allocate, which is queued (a frame away) rather than synchronous.\n // `Gtk.Adjustment.setValue()` clamps to ITS OWN `upper`/`page-size`, so a\n // target beyond a still-stale (freshly mounted: 0/0) range is silently\n // absorbed as a no-op — the value does not actually change, so\n // `value-changed` never fires and the scroll this call was supposed to\n // produce is dropped with no event at all. Reproduced deterministically\n // by calling `scrollTo` immediately after `render()` with no extra\n // settling tick: the adjustment reads upper=0/page=0 while\n // `contentNode`/`outerNode` already have the real (correct) rect.\n //\n // The engine's own rects are the fix: they commit before GTK's real\n // geometry ever could (same tick vs. next frame), so reconfiguring the\n // adjustment's range from them — right before setting the value — closes\n // the gap using truth this component already has in hand. Only these two\n // rare, explicitly-invoked entry points pay for it; the per-event\n // `onAdjustment`/`readScrollEvent` path (docs/research/scroll-phases.md)\n // is untouched.\n const syncAdjustmentRange = (\n adjustment: Gtk.Adjustment,\n axis: \"width\" | \"height\",\n ): void => {\n const content = contentNode.getRect()\n const viewport = outerNode.getRect()\n if (!content || !viewport) {\n return\n }\n const pageSize = viewport[axis]\n // A viewport taller/wider than its content (a short list in a tall\n // ScrollView — routine, not an edge case) must still satisfy GTK's own\n // `gtk_adjustment_configure` precondition (`lower + page_size <=\n // upper`): clamping `upper` up to at least `pageSize` is the standard\n // \"nothing to scroll\" range, exactly like a GtkScrollbar whose thumb\n // fills the whole trough. Without this, a page size bigger than the\n // content invalidates every `configure()` call — GTK logs a\n // Gtk-CRITICAL and, per `gtk_adjustment_configure`'s own\n // `g_return_if_fail`, leaves the adjustment UNCHANGED, so the\n // `getUpper()`/`getPageSize()` guard above never starts matching and a\n // caller retrying every frame (`autoscroll.tsx`'s tick callback, while\n // active) reissues the same invalid call on every tick for as long as\n // it stays active — a burst of thousands of identical criticals from\n // one stuck loop rather than one-off noise.\n const upper = Math.max(content[axis], pageSize)\n if (\n adjustment.getUpper() === upper &&\n adjustment.getPageSize() === pageSize\n ) {\n return\n }\n adjustment.configure(\n adjustment.getValue(),\n 0,\n upper,\n adjustment.getStepIncrement(),\n adjustment.getPageIncrement(),\n pageSize,\n )\n }\n\n useImperativeHandle(handleRef, () => {\n const handle: ScrollViewHandle = {\n ...createMeasureHandle(outerRef, outerNode),\n\n scrollTo({ x, y }) {\n const widget = outerRef.current\n if (!widget) {\n return\n }\n if (y !== undefined) {\n const vadjustment = widget.getVadjustment()\n if (vadjustment) {\n syncAdjustmentRange(vadjustment, \"height\")\n vadjustment.setValue(y)\n }\n }\n if (x !== undefined) {\n const hadjustment = widget.getHadjustment()\n if (hadjustment) {\n syncAdjustmentRange(hadjustment, \"width\")\n hadjustment.setValue(x)\n }\n }\n },\n scrollToEnd() {\n const widget = outerRef.current\n const adjustment = horizontal\n ? widget?.getHadjustment()\n : widget?.getVadjustment()\n if (adjustment) {\n syncAdjustmentRange(adjustment, horizontal ? \"width\" : \"height\")\n adjustment.setValue(\n adjustment.getUpper() - adjustment.getPageSize(),\n )\n }\n },\n }\n // Spreading built a NEW object, and the widget lookup is keyed by\n // handle identity — without this the composed handle is a stranger to\n // measure.ts.\n registerHandleWidget(handle, outerRef)\n return handle\n })\n\n // --- sticky headers -------------------------------------------------\n // The RN model, faithfully: the REAL child is translated (no duplicate,\n // state preserved). RN gives the pinned cell a zIndex; this reorders the\n // active slot to be the LAST content child while pinned and restores it\n // afterwards, which predates `zIndex` working (gtkx/bridge/view-box.ts)\n // and is kept because it is exact: the slots here are the ScrollView's own\n // and their Yoga order is rebuilt from the same list, so the reorder that\n // would be a hazard for arbitrary app children is not one for these. Per-frame movement\n // goes through the rect-store offset fast path — zero React work while\n // scrolling.\n const stickySet = stickyHeaderIndices ?? []\n const stickyRecords = useRef(new Map<string, StickyRecord>())\n const restoreSibling = useRef(new Map<string, Gtk.Widget | null>())\n const activeStickyRef = useRef<string | null>(null)\n const [hasSticky, setHasSticky] = useState(false)\n\n const stickyRegistry = useRef<StickyRegistry>({\n register: (key, record) => {\n stickyRecords.current.set(key, record)\n setHasSticky(true)\n const parent = record.widget.getParent()\n if (parent) {\n queueAllocate(parent)\n }\n },\n unregister: (key) => {\n stickyRecords.current.delete(key)\n restoreSibling.current.delete(key)\n if (activeStickyRef.current === key) {\n activeStickyRef.current = null\n }\n if (stickyRecords.current.size === 0) {\n setHasSticky(false)\n }\n },\n })\n\n const updateSticky = (rawScrollTop: number): void => {\n if (stickyRecords.current.size === 0) {\n return\n }\n // The viewport translates the content by the FRACTIONAL adjustment\n // value while widget allocations are integer — an unquantized pin\n // disagrees with the translation by ±1px and shimmers every frame.\n // Frame telemetry (docs/research/sticky-probe.md) proved the viewport quantizes\n // like floor: with Math.floor the header's window-relative position is\n // a flat 0.00 across every frame; round oscillates 0/1, ceil sits at 1.\n const scrollTop = Math.floor(rawScrollTop)\n let active: string | null = null\n let next: StickyRecord | null = null\n for (const [key, record] of stickyRecords.current) {\n if (record.y <= scrollTop) {\n if (\n active === null ||\n record.y >= stickyRecords.current.get(active)!.y\n ) {\n active = key\n }\n } else if (next === null || record.y < next.y) {\n next = record\n }\n }\n\n const previous = activeStickyRef.current\n if (previous !== active) {\n activeStickyRef.current = active\n if (previous !== null) {\n const record = stickyRecords.current.get(previous)\n if (record) {\n setStoredOffset(record.widget, 0, 0)\n }\n }\n // Reordering widgets queues GTK work — not allowed mid-allocation.\n // The z-switch lands a frame later; the POSITION is already exact.\n setTimeout(() => {\n applyStickyReorder(previous, active)\n }, 0)\n }\n\n if (active !== null) {\n const record = stickyRecords.current.get(active)!\n // Pin at the viewport top; the next sticky header pushes it out.\n const pinned = next\n ? Math.min(scrollTop, next.y - record.height)\n : scrollTop\n setStoredOffset(record.widget, 0, Math.max(0, pinned - record.y))\n }\n }\n\n const applyStickyReorder = (\n previous: string | null,\n active: string | null,\n ): void => {\n if (previous !== null) {\n const record = stickyRecords.current.get(previous)\n // Slot parents are always our GtkBox containers.\n const parent = record?.widget.getParent() as Gtk.Box | null\n if (record && parent) {\n // Put the slot back where the reconciler expects it.\n parent.reorderChildAfter(\n record.widget,\n restoreSibling.current.get(previous) ?? null,\n )\n queueAllocate(parent)\n }\n }\n if (active !== null) {\n const record = stickyRecords.current.get(active)\n const parent = record?.widget.getParent() as Gtk.Box | null\n if (record && parent) {\n restoreSibling.current.set(active, record.widget.getPrevSibling())\n parent.reorderChildAfter(record.widget, parent.getLastChild())\n queueAllocate(parent)\n }\n }\n }\n\n // The one place the payload is read off the widget, shared by `onScroll`\n // and by all four phases — RN hands the same `ScrollEvent` to every one\n // of them, and reading it twice would be two sets of FFI calls for one\n // truth. Null when there is nothing honest to report yet (no widget, or\n // a content size the engine has not committed).\n const readScrollEvent = (): ScrollEvent | null => {\n const widget = outerRef.current\n const contentRect = contentNode.getRect()\n if (!widget || !contentRect) {\n return null\n }\n const hadjustment = widget.getHadjustment()\n const vadjustment = widget.getVadjustment()\n return {\n nativeEvent: {\n contentOffset: {\n x: hadjustment?.getValue() ?? 0,\n y: vadjustment?.getValue() ?? 0,\n },\n contentSize: { width: contentRect.width, height: contentRect.height },\n layoutMeasurement: {\n width: hadjustment?.getPageSize() ?? 0,\n height: vadjustment?.getPageSize() ?? 0,\n },\n },\n }\n }\n\n const emitScroll = (): void => {\n if (!onScroll) {\n return\n }\n const event = readScrollEvent()\n if (event) {\n onScroll(event)\n }\n }\n\n const adjustment = horizontal\n ? (scrolled?.getHadjustment() ?? null)\n : (scrolled?.getVadjustment() ?? null)\n const onAdjustment = (): void => {\n if (!perfEnabled) {\n emitScroll()\n return\n }\n // Perf: full wall time of one scroll tick's JS work — the FFI reads in\n // emitScroll plus the app/VirtualizedList onScroll handler (windowing,\n // viewability, endReached) — everything that blocks the GTK frame.\n perfCount(\"scroll.events\")\n const start = perfNow()\n emitScroll()\n perfAddTime(\"scroll.js\", perfNow() - start)\n }\n\n // --- the four scroll phases -----------------------------------------\n //\n // What GTK actually reports, measured on 4.22.4 under a real pointer\n // (docs/research/scroll-phases.md, and the trace is in\n // tests/gtk/components/scroll-phases.gtk.test.tsx):\n //\n // - a mouse WHEEL emits `::scroll` per detent and NOTHING around it.\n // GTK gives us no sequence, so while a consumer asks for begin/end we\n // synthesize a desktop scroll SESSION: begin before the first detent\n // mutates the adjustment, end after a measured idle window, and no\n // momentum. This is an intentional desktop extension — RN has no\n // wheel — and is what lets a phase-aware consumer capture the offset\n // it is about to constrain instead of discovering it one event late.\n // - a touchpad GLIDE emits `::scroll-begin`, a stream of `::scroll`,\n // `::scroll-end` and `::decelerate` — a real sequence with a real\n // beginning and end — and the scrolled window's own kinetic\n // animation then carries the adjustment on for another second or so.\n // All four phases exist, and all four are reported.\n //\n // Drag maps onto the scroll SEQUENCE rather than onto a finger on the\n // content: `::scroll-begin` is \"the user started driving this scroller\",\n // which is what a consumer of `onScrollBeginDrag` acts on and is as close\n // as a device that never touches the content can get. That is the one\n // approximation here, and it is named in docs/api.md.\n //\n // Momentum is read off the ADJUSTMENT, not off `::decelerate`. The\n // controller emits `decelerate` at every `scroll-end` — it reports the\n // velocity it measured, not a decision to coast — while the scrolled\n // window decides for itself whether that velocity is worth animating. RN\n // fires `onMomentumScrollBegin` only when the view actually keeps\n // moving, so the honest source is the movement itself.\n const phaseSink = scrollPhaseSink(onScroll)\n // `wants()` rather than the sink's mere presence: every Reanimated scroll\n // handler carries one, and most of them ask for nothing but `onScroll`.\n // Asking is what keeps the common case free.\n const wantsPhases = Boolean(\n onScrollBeginDrag ||\n onScrollEndDrag ||\n onMomentumScrollBegin ||\n onMomentumScrollEnd ||\n phaseSink?.wants(),\n )\n // GTK brackets touchpad gestures for us. A wheel has only detents, so the\n // begin/end extension below needs to see those detents — but only when a\n // consumer asks for either end of the session. A momentum-only handler\n // still pays no JS call per wheel event, because a wheel never coasts.\n const wantsWheelSessions = Boolean(\n onScrollBeginDrag ||\n onScrollEndDrag ||\n phaseSink?.wants(\"beginDrag\") ||\n phaseSink?.wants(\"endDrag\"),\n )\n\n // Read per event rather than captured, exactly as `useAnimatedScrollHandler`\n // does it: a re-render that changed a handler must be picked up without\n // tearing the GTK controller down and building a new one.\n const phaseHandlers = useRef({\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n phaseSink,\n readScrollEvent,\n })\n phaseHandlers.current = {\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n phaseSink,\n readScrollEvent,\n }\n\n useLayoutEffect(() => {\n // The whole cost story is this early return. No phase handler means no\n // controller, no signal, no tick callback and no bookkeeping — the\n // widget is byte-for-byte the one this component built before the\n // phases existed.\n if (!wantsPhases) {\n return\n }\n const widget = scrolled\n if (!widget) {\n return\n }\n\n const emitPhase = (phase: ScrollPhase): void => {\n const handlers = phaseHandlers.current\n const event = handlers.readScrollEvent()\n if (!event) {\n return\n }\n if (phase === \"beginDrag\") {\n handlers.onScrollBeginDrag?.(event)\n } else if (phase === \"endDrag\") {\n handlers.onScrollEndDrag?.(event)\n } else if (phase === \"momentumBegin\") {\n handlers.onMomentumScrollBegin?.(event)\n } else {\n handlers.onMomentumScrollEnd?.(event)\n }\n handlers.phaseSink?.deliver(phase, event)\n }\n\n const axis = horizontal\n ? widget.getHadjustment()\n : widget.getVadjustment()\n\n // Momentum is watched on the ADJUSTMENT, with two timers and no tick\n // callback. A frame-polling watcher was the first shape and it was\n // wrong twice over: it burns a callback per frame for the length of\n // every coast, and it counts FRAMES, which is not a unit — under the\n // headless compositor the frame clock free-runs at ~106 ticks per\n // millisecond (measured, docs/research/scroll-phases.md), so a\n // four-frame grace expired in microseconds. Wall time means the same\n // thing everywhere.\n //\n // Milliseconds to wait after `::scroll-end` for the scrolled window's\n // kinetic animation to take over before concluding it never will.\n // Measured handoff: `::scroll-end` at 1921.9 ms, first inertial value\n // change at 1927.5 ms — 5.6 ms. 120 is slack, not a guess.\n const handoffMs = 120\n // Milliseconds of a motionless adjustment that end the momentum. GTK's\n // deceleration steps the value every frame right to the end (16.7 ms\n // apart in the trace), so 60 ms is three and a half missed steps.\n const restMs = 60\n\n let watching = false\n let coasting = false\n let lastMoveAt = 0\n let handoffTimer: ReturnType<typeof setTimeout> | null = null\n let restTimer: ReturnType<typeof setTimeout> | null = null\n\n const clearTimers = (): void => {\n if (handoffTimer !== null) {\n clearTimeout(handoffTimer)\n handoffTimer = null\n }\n if (restTimer !== null) {\n clearTimeout(restTimer)\n restTimer = null\n }\n }\n\n // Re-armed for the REMAINING idle time rather than reset on every step,\n // so the number of timers a coast costs is bounded by its duration\n // (one per `restMs`) and not by how many times the adjustment moved.\n // The difference is not academic: under the headless compositor's\n // free-running frame clock a single coast steps the value ~150,000\n // times, and a clear-and-rearm per step is 150,000 timers.\n const checkRest = (): void => {\n const idle = perfNow() - lastMoveAt\n if (idle < restMs) {\n restTimer = setTimeout(checkRest, restMs - idle)\n return\n }\n restTimer = null\n stopMomentumWatch()\n emitPhase(\"momentumEnd\")\n }\n\n const onCoastStep = (): void => {\n lastMoveAt = perfNow()\n if (coasting) {\n return\n }\n coasting = true\n if (handoffTimer !== null) {\n clearTimeout(handoffTimer)\n handoffTimer = null\n }\n emitPhase(\"momentumBegin\")\n restTimer = setTimeout(checkRest, restMs)\n }\n\n const stopMomentumWatch = (): void => {\n clearTimers()\n if (watching) {\n axis?.off(\"value-changed\", onCoastStep)\n watching = false\n }\n coasting = false\n }\n\n const watchMomentum = (): void => {\n if (watching) {\n return\n }\n watching = true\n axis?.on(\"value-changed\", onCoastStep)\n handoffTimer = setTimeout(() => {\n handoffTimer = null\n if (!coasting) {\n // The scroller came to rest with the drag. No inertia, so no\n // momentum pair — which is RN's behaviour too.\n stopMomentumWatch()\n }\n }, handoffMs)\n }\n\n const controller = Gtk.EventControllerScroll.new(\n // An axis flag is REQUIRED, and not for the deltas: measured, a\n // controller created with `KINETIC` alone emits nothing at all — not\n // even `::scroll-begin`. `::scroll` is therefore emitted. It stays\n // unconnected unless somebody asks for begin/end: only then does a\n // wheel detent enter JS to maintain the desktop session below; a\n // momentum-only or untracked scroller keeps the old empty C signal\n // path.\n Gtk.EventControllerScrollFlags.BOTH_AXES |\n Gtk.EventControllerScrollFlags.KINETIC,\n )\n // CAPTURE, so the phase is reported before the scrolled window's own\n // controller acts on the same event and moves the adjustment. Nothing\n // here handles the event, so propagation is untouched either way.\n controller.setPropagationPhase(Gtk.PropagationPhase.CAPTURE)\n\n const beginDrag = (): void => {\n // A new drag during a coast: RN ends the momentum before beginning\n // the drag, so a consumer never sees two live phases at once.\n if (coasting) {\n stopMomentumWatch()\n emitPhase(\"momentumEnd\")\n } else {\n stopMomentumWatch()\n }\n emitPhase(\"beginDrag\")\n }\n // A wheel gives GTK no begin/end signals. Group its detents into one\n // user-driven session, ending after an idle interval longer than the\n // ordinary detent spacing measured in docs/research/scroll-phases.md\n // (20–33 ms in the trace). The callback runs in CAPTURE, so beginDrag\n // sees the PRE-scroll offset before GtkScrolledWindow mutates its\n // adjustment and emits the onScroll callback that consumes the context.\n const wheelSession = createWheelScrollSession(beginDrag, () =>\n emitPhase(\"endDrag\"),\n )\n // A touchpad brackets itself, so its sequence owns the session while it\n // runs. Two live sessions at once would hand a consumer a second\n // `beginDrag` before the first `endDrag` — which is exactly the state\n // gorhom's lock reads to decide where to pin its list.\n let nativeSequence = false\n const onScrollBegin = (): void => {\n nativeSequence = true\n // A wheel burst may still be waiting out its idle timer.\n wheelSession.finish()\n beginDrag()\n }\n const onScrollEnd = (): void => {\n nativeSequence = false\n emitPhase(\"endDrag\")\n watchMomentum()\n }\n const onScroll = (): boolean => {\n if (\n !wantsWheelSessions ||\n nativeSequence ||\n controller.getUnit() !== Gdk.ScrollUnit.WHEEL\n ) {\n return false\n }\n wheelSession.detent()\n // Observe only. GtkScrolledWindow still receives and applies the\n // detent, which is what makes this phase seam transparent.\n return false\n }\n\n controller.on(\"scroll-begin\", onScrollBegin)\n controller.on(\"scroll-end\", onScrollEnd)\n if (wantsWheelSessions) {\n controller.on(\"scroll\", onScroll)\n }\n widget.addController(controller)\n\n return () => {\n controller.off(\"scroll-begin\", onScrollBegin)\n controller.off(\"scroll-end\", onScrollEnd)\n if (wantsWheelSessions) {\n controller.off(\"scroll\", onScroll)\n }\n wheelSession.dispose()\n widget.removeController(controller)\n stopMomentumWatch()\n }\n }, [scrolled, wantsPhases, wantsWheelSessions, horizontal])\n\n // Content-size reports dedupe on the engine rect: \"changed\" also fires\n // for pure viewport (page-size) changes, which RN does not report.\n const lastContentSize = useRef({ width: -1, height: -1 })\n const onRangeChanged = (): void => {\n if (!onContentSizeChange) {\n return\n }\n const rect = contentNode.getRect()\n if (!rect) {\n return\n }\n const last = lastContentSize.current\n if (rect.width === last.width && rect.height === last.height) {\n return\n }\n lastContentSize.current = { width: rect.width, height: rect.height }\n onContentSizeChange(rect.width, rect.height)\n }\n\n // Frame-synced sticky driver: a pure scroll TRANSLATES the content\n // without re-allocating it (same size → GTK skips the vfunc), so signal\n // handlers land a frame late and the pinned header jitters. A tick\n // callback runs in the frame's UPDATE phase, before layout: when the\n // scroll offset moved, queue an allocation for THIS frame — the\n // translation and the pin correction then paint atomically.\n useLayoutEffect(() => {\n if (!hasSticky || horizontal) {\n return\n }\n const widget = scrolled\n if (!widget) {\n return\n }\n const vadjustment = widget.getVadjustment()\n let lastValue = vadjustment?.getValue() ?? 0\n // Frames to keep requeueing after the last observed move. The scrolled\n // window's own kinetic tick may run AFTER ours, so this tick's read can\n // be one frame stale: without the grace window the first frame of a\n // deceleration (its value change lands after our read) would miss its\n // correction. Kinetic deceleration changes the value every frame, so\n // three frames of slack cover the handoff at both ends of the motion.\n const graceFrames = 3\n let remaining = graceFrames\n const id = widget.addTickCallback(() => {\n perfCount(\"sticky.ticks\")\n const value = vadjustment?.getValue() ?? 0\n if (value !== lastValue) {\n lastValue = value\n remaining = graceFrames\n } else if (remaining > 0) {\n remaining -= 1\n } else {\n // At rest: the pin is already correct and nothing translated it,\n // so an allocation pass per frame would be pure idle cost.\n return true\n }\n perfCount(\"sticky.queues\")\n const content = contentRef.current\n if (content) {\n queueAllocate(content)\n }\n return true\n })\n return () => {\n widget.removeTickCallback(id)\n }\n }, [scrolled, hasSticky, horizontal])\n\n // Perf: observe the GTK frame clock through a tick callback — the delta\n // between consecutive ticks is the real frame interval (late frames show\n // up directly). Note: registering a tick callback keeps the frame clock\n // running, so idle seconds show ~60 ticks too; the reporter separates\n // load by the accompanying counters.\n useLayoutEffect(() => {\n if (!perfEnabled) {\n return\n }\n ensurePerfReporter()\n const widget = scrolled\n if (!widget) {\n return\n }\n const id = widget.addTickCallback(() => {\n perfFrameTick(perfNow())\n return true\n })\n return () => {\n widget.removeTickCallback(id)\n }\n }, [scrolled])\n useSignal(adjustment, \"value-changed\", onAdjustment)\n // Both axes report into the same handler — RN's callback carries both\n // dimensions no matter which one moved.\n useSignal(scrolled?.getVadjustment() ?? null, \"changed\", onRangeChanged)\n useSignal(scrolled?.getHadjustment() ?? null, \"changed\", onRangeChanged)\n\n return (\n <GtkScrolledWindow\n ref={(widget: Gtk.ScrolledWindow | null) => {\n outerRef.current = widget\n setScrolled(widget)\n }}\n name={testID}\n >\n <GtkBox ref={contentRef}>\n <HostNodeContext.Provider\n value={{\n engine: host.engine,\n node: contentNode,\n widgetRef: contentRef,\n }}\n >\n <StickyRegistryContext.Provider value={stickyRegistry.current}>\n {stickySet.length === 0\n ? children\n : Children.toArray(children).map((child, index) =>\n stickySet.includes(index) ? (\n <StickySlot\n key={`sticky-slot-${index}`}\n stickyKey={`index-${index}`}\n >\n {child}\n </StickySlot>\n ) : (\n child\n ),\n )}\n </StickyRegistryContext.Provider>\n </HostNodeContext.Provider>\n </GtkBox>\n </GtkScrolledWindow>\n )\n },\n)\nScrollView.displayName = \"ScrollView\"\n"]}
|
|
@@ -695,10 +695,7 @@ const VirtualizedListInner = forwardRef(({ data, getItem, getItemCount, renderIt
|
|
|
695
695
|
void extraData;
|
|
696
696
|
// Accepted for RN parity only; no desktop gesture ever calls it.
|
|
697
697
|
void onRefresh;
|
|
698
|
-
return (_jsxs(ScrollView, { ...scrollProps, horizontal: horizontal, ref: scrollRef, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: handleLayout, children: [refreshing ? (
|
|
699
|
-
// Desktop RefreshControl: no pull gesture exists, so `refreshing`
|
|
700
|
-
// simply shows an in-flow spinner row above the list content.
|
|
701
|
-
_jsx(View, { style: { alignItems: "center", padding: 8 }, children: _jsx(ActivityIndicator, {}) })) : null, renderAux(inverted ? ListFooterComponent : ListHeaderComponent), count === 0 && renderAux(ListEmptyComponent), count > 0 && (_jsx(View, { style: horizontal
|
|
698
|
+
return (_jsxs(ScrollView, { ...scrollProps, horizontal: horizontal, ref: scrollRef, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: handleLayout, children: [refreshing ? (_jsx(View, { style: { alignItems: "center", padding: 8 }, children: _jsx(ActivityIndicator, {}) })) : null, renderAux(inverted ? ListFooterComponent : ListHeaderComponent), count === 0 && renderAux(ListEmptyComponent), count > 0 && (_jsx(View, { style: horizontal
|
|
702
699
|
? { width: offsets[count] }
|
|
703
700
|
: { height: offsets[count] }, children: cells })), renderAux(inverted ? ListHeaderComponent : ListFooterComponent)] }));
|
|
704
701
|
});
|