react-native-gtkx 0.2.0-alpha.1 → 0.2.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 +11 -2
- package/dist/adw/widgets.generated.d.ts +4 -4
- package/dist/adw/widgets.generated.js +51 -51
- 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/index.d.ts +2 -0
- package/dist/animated/index.js +1 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/value-xy.d.ts +41 -0
- package/dist/animated/value-xy.js +90 -0
- package/dist/animated/value-xy.js.map +1 -0
- package/dist/animated/value.d.ts +9 -0
- package/dist/animated/value.js +30 -2
- package/dist/animated/value.js.map +1 -1
- package/dist/common/icon.d.ts +23 -0
- package/dist/common/icon.js +14 -0
- package/dist/common/icon.js.map +1 -0
- package/dist/common/index.d.ts +2 -0
- package/dist/common/index.js +2 -0
- package/dist/common/index.js.map +1 -1
- package/dist/common/list.d.ts +82 -0
- package/dist/common/list.js +166 -0
- package/dist/common/list.js.map +1 -0
- package/dist/common/widget.d.ts +1 -22
- package/dist/common/widget.js +75 -8
- package/dist/common/widget.js.map +1 -1
- package/dist/components/animated.d.ts +4 -2
- package/dist/components/animated.js +3 -1
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.d.ts +26 -0
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/host-node.d.ts +7 -0
- package/dist/components/host-node.js +26 -1
- package/dist/components/host-node.js.map +1 -1
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/measure.d.ts +13 -0
- package/dist/components/measure.js +51 -0
- package/dist/components/measure.js.map +1 -0
- package/dist/components/modal.d.ts +1 -1
- package/dist/components/press-event.d.ts +28 -0
- package/dist/components/press-event.js +46 -0
- package/dist/components/press-event.js.map +1 -0
- package/dist/components/pressable.d.ts +2 -6
- package/dist/components/pressable.js +14 -8
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/scroll-view.d.ts +4 -0
- package/dist/components/scroll-view.js +8 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/text-input.js +1 -1
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.js +25 -1
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-layout-child.js +17 -14
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/components/view.d.ts +9 -4
- package/dist/components/view.js +8 -2
- package/dist/components/view.js.map +1 -1
- package/dist/contracts.d.ts +19 -3
- package/dist/contracts.js.map +1 -1
- package/dist/gtk/index.d.ts +2 -1
- package/dist/gtk/index.js +8 -1
- package/dist/gtk/index.js.map +1 -1
- package/dist/gtk/widgets.generated.d.ts +14 -14
- package/dist/gtk/widgets.generated.js +101 -102
- package/dist/gtk/widgets.generated.js.map +1 -1
- package/dist/gtk/window-actions.d.ts +77 -0
- package/dist/gtk/window-actions.js +193 -0
- package/dist/gtk/window-actions.js.map +1 -0
- package/dist/gtkx/bridge/geometry.d.ts +15 -0
- package/dist/gtkx/bridge/geometry.js +38 -0
- package/dist/gtkx/bridge/geometry.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +2 -0
- package/dist/gtkx/bridge/index.js +7 -2
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/slot-portal.d.ts +12 -0
- package/dist/gtkx/bridge/slot-portal.js +17 -0
- package/dist/gtkx/bridge/slot-portal.js.map +1 -0
- package/dist/gtkx/bridge/svg-node.js +10 -54
- package/dist/gtkx/bridge/svg-node.js.map +1 -1
- package/dist/gtkx/bridge/use-signal.js +2 -2
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/data/generated.d.ts +99 -77
- package/dist/mcp/data/generated.js +107 -76
- package/dist/mcp/data/generated.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +31 -1
- package/dist/navigation/sidebar.js +12 -2
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/responder/system.d.ts +27 -0
- package/dist/responder/system.js +184 -0
- package/dist/responder/system.js.map +1 -0
- package/dist/responder/touch-history.d.ts +10 -0
- package/dist/responder/touch-history.js +78 -0
- package/dist/responder/touch-history.js.map +1 -0
- package/dist/responder/types.d.ts +63 -0
- package/dist/responder/types.js +29 -0
- package/dist/responder/types.js.map +1 -0
- package/dist/responder/use-responder.d.ts +12 -0
- package/dist/responder/use-responder.js +98 -0
- package/dist/responder/use-responder.js.map +1 -0
- package/dist/runner/host-dev.js +3 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +5 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.js +1 -1
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/gtkx-config-module.js +1 -1
- package/dist/sea/gtkx-config-module.js.map +1 -1
- package/dist/style/box-shadow.d.ts +7 -0
- package/dist/style/box-shadow.js +197 -0
- package/dist/style/box-shadow.js.map +1 -0
- package/dist/style/index.d.ts +2 -0
- package/dist/style/index.js +2 -0
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.d.ts +5 -2
- package/dist/style/split-style.js +31 -3
- package/dist/style/split-style.js.map +1 -1
- package/dist/style/text-decoration.d.ts +15 -0
- package/dist/style/text-decoration.js +26 -0
- package/dist/style/text-decoration.js.map +1 -0
- package/dist/style/visual-css.js +37 -1
- package/dist/style/visual-css.js.map +1 -1
- package/dist/svg/gradient-geometry.js +2 -5
- package/dist/svg/gradient-geometry.js.map +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/vendor/react-native/pan-responder.d.ts +102 -0
- package/dist/vendor/react-native/pan-responder.js +196 -0
- package/dist/vendor/react-native/pan-responder.js.map +1 -0
- package/dist/vendor/react-native/touch-history-math.d.ts +18 -0
- package/dist/vendor/react-native/touch-history-math.js +102 -0
- package/dist/vendor/react-native/touch-history-math.js.map +1 -0
- package/dist/vite/index.js +1 -1
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +1 -1
- package/dist/vitest/index.js.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pressable.js","sourceRoot":"","sources":["../../src/components/pressable.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useLayoutEffect,\n useReducer,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { splitStyle, StyleSheet } from \"../style/index\"\nimport { defaultCssRegistry } from \"../style/registry.gtkx\"\nimport type { StyleProp } from \"../contracts\"\nimport { Gtk, GtkBox } from \"../gtkx/bridge/index\"\nimport { perfAddTime, perfCount, perfEnabled, perfNow } from \"../perf\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type PressableStateCallbackType = {\n pressed: boolean\n hovered: boolean\n}\n\nexport type PressEvent = { nativeEvent: { x: number; y: number } }\n\nexport type PressableProps = {\n style?: StyleProp | ((state: PressableStateCallbackType) => StyleProp)\n children?: ReactNode | ((state: PressableStateCallbackType) => ReactNode)\n onPress?: (event: PressEvent) => void\n onPressIn?: (event: PressEvent) => void\n onPressOut?: (event: PressEvent) => void\n onLongPress?: (event: PressEvent) => void\n onHoverIn?: () => void\n onHoverOut?: () => void\n disabled?: boolean\n delayLongPress?: number\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\nconst pressEvent = (x: number, y: number): PressEvent => ({\n nativeEvent: { x, y },\n})\n\n// A View with a click gesture and hover tracking. State-dependent style and\n// children follow the RN Pressable function-prop contract.\nexport const Pressable = ({\n style,\n children,\n onPress,\n onPressIn,\n onPressOut,\n onLongPress,\n onHoverIn,\n onHoverOut,\n disabled = false,\n delayLongPress = 500,\n onLayout,\n testID,\n}: PressableProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const [pressed, setPressed] = useState(false)\n // Hover is not React state: a boundary crossing should cost what it costs\n // a native GtkListBox row to swap a CSS class, not a setState + render +\n // Yoga + allocate cycle (measured: roughly 500x the latency of a native\n // row at rest, see docs/research/scroll-performance.md, task 007).\n // `hoveredRef` is the single source of truth, read directly at render\n // time — safe here because this component already mutates\n // `handlersRef.current` in the render body below, so render was never\n // React-DOM-pure to begin with. `forceRender` is used only for the two\n // cases that genuinely need a real render: `children` reads `hovered`,\n // or hovering would itself change layout (not just paint).\n const hoveredRef = useRef(false)\n const [, forceRender] = useReducer((n: number) => n + 1, 0)\n const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const longPressFired = useRef(false)\n // TEMPORARY perf instrumentation (task 007, see\n // docs/research/scroll-performance.md): timestamp the native enter/leave\n // signal so hover's real signal-to-applied latency can be reported —\n // synchronously for the fast path below, after the resulting commit for\n // the slow one. perf.ts hooks are no-ops unless GTKX_PERF=1.\n const hoverSignalAt = useRef<number | null>(null)\n\n const state: PressableStateCallbackType = {\n pressed,\n hovered: hoveredRef.current,\n }\n\n // Gestures are attached imperatively even though rc.2 has a declarative\n // `controllers` slot: the controllers are wired exactly once per widget and\n // the handlers read the latest props through a ref, so a re-render never\n // detaches and re-adds a gesture mid-press.\n const handlersRef = useRef({\n handlePressed: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleReleased: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleCancel: (): void => {},\n handleEnter: (): void => {},\n handleLeave: (): void => {},\n })\n const resolvedStyle = typeof style === \"function\" ? style(state) : style\n const resolvedChildren =\n typeof children === \"function\" ? children(state) : children\n\n const { host, node, cssClass, flat } = useLayoutChild(widgetRef, {\n style: resolvedStyle,\n onLayout,\n })\n useRnContainer(widgetRef, node)\n\n // Hover fast path: when `children` does not read state, hovering can\n // never change what is rendered — only `style` might. Precompute the CSS\n // class for the OTHER hover value so the native enter/leave handlers\n // below can swap classes directly on the widget, with no React involved\n // at all. If hovering would also change LAYOUT (rare — every hover style\n // in this codebase's own examples is background/border) that swap is\n // unsafe, since a real reflow is required; fall back to the ordinary\n // render in that case, exactly as before this change.\n let hoverOnClass: string | null = null\n let hoverOffClass: string | null = null\n let canSwapHoverClass = typeof children !== \"function\"\n if (canSwapHoverClass && typeof style === \"function\") {\n const otherState: PressableStateCallbackType = {\n pressed,\n hovered: !hoveredRef.current,\n }\n const otherFlat = StyleSheet.flatten(style(otherState))\n const otherSplit = splitStyle(otherFlat)\n const currentLayout = splitStyle(flat).layout\n if (JSON.stringify(currentLayout) !== JSON.stringify(otherSplit.layout)) {\n // Hovering resizes or repositions this widget — a CSS class alone\n // cannot do that, it needs Yoga. Fall back to a real render.\n canSwapHoverClass = false\n } else {\n const otherClass = defaultCssRegistry.getClassName(otherSplit.visual)\n hoverOnClass = hoveredRef.current ? cssClass : otherClass\n hoverOffClass = hoveredRef.current ? otherClass : cssClass\n }\n }\n\n // TEMPORARY perf instrumentation (task 007): runs after every commit, so\n // it always lands right after whatever render the SLOW hover path\n // triggered. A no-op when no hover signal is pending (the common case:\n // renders triggered by anything else, or the fast path above, which\n // measures itself synchronously) or when GTKX_PERF is unset.\n useLayoutEffect(() => {\n if (!perfEnabled || hoverSignalAt.current === null) {\n return\n }\n perfAddTime(\"pressable.hoverApply\", perfNow() - hoverSignalAt.current)\n perfCount(\"pressable.hoverFullCycle\")\n hoverSignalAt.current = null\n })\n\n const clearLongPress = (): void => {\n if (longPressTimer.current !== null) {\n clearTimeout(longPressTimer.current)\n longPressTimer.current = null\n }\n }\n\n const handlePressed = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n longPressFired.current = false\n setPressed(true)\n onPressIn?.(pressEvent(x, y))\n if (onLongPress) {\n clearLongPress()\n longPressTimer.current = setTimeout(() => {\n longPressFired.current = true\n onLongPress(pressEvent(x, y))\n }, delayLongPress)\n }\n }\n\n const handleReleased = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n clearLongPress()\n setPressed(false)\n onPressOut?.(pressEvent(x, y))\n if (!longPressFired.current) {\n onPress?.(pressEvent(x, y))\n }\n }\n\n const handleCancel = (): void => {\n clearLongPress()\n setPressed(false)\n }\n\n // Applies a hover transition: fires the RN callback, then either swaps\n // the precomputed CSS class directly on the widget (fast path) or forces\n // the ordinary render (slow path) — see the `canSwapHoverClass` block\n // above for which one applies this render.\n const applyHover = (next: boolean, fire: (() => void) | undefined): void => {\n const t0 = perfEnabled ? perfNow() : 0\n hoveredRef.current = next\n fire?.()\n if (!canSwapHoverClass) {\n if (perfEnabled) {\n hoverSignalAt.current = t0\n perfCount(\"pressable.hoverEvent\")\n }\n forceRender()\n return\n }\n const widget = widgetRef.current\n const applyClass = next ? hoverOnClass : hoverOffClass\n const removeClass = next ? hoverOffClass : hoverOnClass\n if (widget && hoverOnClass !== hoverOffClass) {\n if (removeClass) {\n widget.removeCssClass(removeClass)\n }\n if (applyClass) {\n widget.addCssClass(applyClass)\n }\n }\n if (perfEnabled) {\n perfAddTime(\"pressable.hoverApply\", perfNow() - t0)\n perfCount(\"pressable.hoverEvent\")\n perfCount(\"pressable.hoverFast\")\n }\n }\n\n handlersRef.current = {\n handlePressed,\n handleReleased,\n handleCancel,\n handleEnter: () => applyHover(true, onHoverIn),\n handleLeave: () => applyHover(false, onHoverOut),\n }\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const click = new Gtk.GestureClick()\n click.on(\"pressed\", (n: number, x: number, y: number) =>\n handlersRef.current.handlePressed(n, x, y),\n )\n click.on(\"released\", (n: number, x: number, y: number) =>\n handlersRef.current.handleReleased(n, x, y),\n )\n click.on(\"cancel\", () => handlersRef.current.handleCancel())\n widget.addController(click)\n\n const motion = new Gtk.EventControllerMotion()\n motion.on(\"enter\", () => handlersRef.current.handleEnter())\n motion.on(\"leave\", () => handlersRef.current.handleLeave())\n widget.addController(motion)\n\n return () => {\n widget.removeController(click)\n widget.removeController(motion)\n }\n }, [])\n\n return (\n <GtkBox\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {resolvedChildren}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\nexport type TouchableOpacityProps = Omit<PressableProps, \"style\"> & {\n style?: StyleProp\n activeOpacity?: number\n}\n\n// Classic RN touchable: dims itself while pressed via widget opacity.\nexport const TouchableOpacity = ({\n style,\n activeOpacity = 0.5,\n ...rest\n}: TouchableOpacityProps) => (\n <Pressable\n {...rest}\n style={({ pressed }) => [style, pressed && { opacity: activeOpacity }]}\n />\n)\n"]}
|
|
1
|
+
{"version":3,"file":"pressable.js","sourceRoot":"","sources":["../../src/components/pressable.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useLayoutEffect,\n useReducer,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { splitStyle, StyleSheet } from \"../style/index\"\nimport { defaultCssRegistry } from \"../style/registry.gtkx\"\nimport type { StyleProp } from \"../contracts\"\nimport { Gtk, GtkBox } from \"../gtkx/bridge/index\"\nimport { perfAddTime, perfCount, perfEnabled, perfNow } from \"../perf\"\nimport { HostNodeContext } from \"./host-node\"\nimport { createPressEvent, createTouch, type PressEvent } from \"./press-event\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type PressableStateCallbackType = {\n pressed: boolean\n hovered: boolean\n}\n\nexport type { NativeTouch, PressEvent } from \"./press-event\"\n\nexport type PressableProps = {\n style?: StyleProp | ((state: PressableStateCallbackType) => StyleProp)\n children?: ReactNode | ((state: PressableStateCallbackType) => ReactNode)\n onPress?: (event: PressEvent) => void\n onPressIn?: (event: PressEvent) => void\n onPressOut?: (event: PressEvent) => void\n onLongPress?: (event: PressEvent) => void\n onHoverIn?: () => void\n onHoverOut?: () => void\n disabled?: boolean\n delayLongPress?: number\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\n// Coordinates arrive from GtkGestureClick in the widget's own space, which\n// is exactly RN's locationX/locationY; createTouch translates them into the\n// window for pageX/pageY.\nconst pressEvent = (\n widget: Gtk.Widget | null,\n x: number,\n y: number,\n): PressEvent => createPressEvent(createTouch(widget, x, y))\n\n// A View with a click gesture and hover tracking. State-dependent style and\n// children follow the RN Pressable function-prop contract.\nexport const Pressable = ({\n style,\n children,\n onPress,\n onPressIn,\n onPressOut,\n onLongPress,\n onHoverIn,\n onHoverOut,\n disabled = false,\n delayLongPress = 500,\n onLayout,\n testID,\n}: PressableProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const [pressed, setPressed] = useState(false)\n // Hover is not React state: a boundary crossing should cost what it costs\n // a native GtkListBox row to swap a CSS class, not a setState + render +\n // Yoga + allocate cycle (measured: roughly 500x the latency of a native\n // row at rest, see docs/research/scroll-performance.md, task 007).\n // `hoveredRef` is the single source of truth, read directly at render\n // time — safe here because this component already mutates\n // `handlersRef.current` in the render body below, so render was never\n // React-DOM-pure to begin with. `forceRender` is used only for the two\n // cases that genuinely need a real render: `children` reads `hovered`,\n // or hovering would itself change layout (not just paint).\n const hoveredRef = useRef(false)\n const [, forceRender] = useReducer((n: number) => n + 1, 0)\n const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const longPressFired = useRef(false)\n // TEMPORARY perf instrumentation (task 007, see\n // docs/research/scroll-performance.md): timestamp the native enter/leave\n // signal so hover's real signal-to-applied latency can be reported —\n // synchronously for the fast path below, after the resulting commit for\n // the slow one. perf.ts hooks are no-ops unless GTKX_PERF=1.\n const hoverSignalAt = useRef<number | null>(null)\n\n const state: PressableStateCallbackType = {\n pressed,\n hovered: hoveredRef.current,\n }\n\n // Gestures are attached imperatively even though rc.3 has a declarative\n // `controllers` slot: the controllers are wired exactly once per widget and\n // the handlers read the latest props through a ref, so a re-render never\n // detaches and re-adds a gesture mid-press.\n const handlersRef = useRef({\n handlePressed: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleReleased: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleCancel: (): void => {},\n handleEnter: (): void => {},\n handleLeave: (): void => {},\n })\n const resolvedStyle = typeof style === \"function\" ? style(state) : style\n const resolvedChildren =\n typeof children === \"function\" ? children(state) : children\n\n const { host, node, cssClass, flat } = useLayoutChild(widgetRef, {\n style: resolvedStyle,\n onLayout,\n })\n useRnContainer(widgetRef, node)\n\n // Hover fast path: when `children` does not read state, hovering can\n // never change what is rendered — only `style` might. Precompute the CSS\n // class for the OTHER hover value so the native enter/leave handlers\n // below can swap classes directly on the widget, with no React involved\n // at all. If hovering would also change LAYOUT (rare — every hover style\n // in this codebase's own examples is background/border) that swap is\n // unsafe, since a real reflow is required; fall back to the ordinary\n // render in that case, exactly as before this change.\n let hoverOnClass: string | null = null\n let hoverOffClass: string | null = null\n let canSwapHoverClass = typeof children !== \"function\"\n if (canSwapHoverClass && typeof style === \"function\") {\n const otherState: PressableStateCallbackType = {\n pressed,\n hovered: !hoveredRef.current,\n }\n const otherFlat = StyleSheet.flatten(style(otherState))\n const otherSplit = splitStyle(otherFlat)\n const currentLayout = splitStyle(flat).layout\n if (JSON.stringify(currentLayout) !== JSON.stringify(otherSplit.layout)) {\n // Hovering resizes or repositions this widget — a CSS class alone\n // cannot do that, it needs Yoga. Fall back to a real render.\n canSwapHoverClass = false\n } else {\n const otherClass = defaultCssRegistry.getClassName(otherSplit.visual)\n hoverOnClass = hoveredRef.current ? cssClass : otherClass\n hoverOffClass = hoveredRef.current ? otherClass : cssClass\n }\n }\n\n // TEMPORARY perf instrumentation (task 007): runs after every commit, so\n // it always lands right after whatever render the SLOW hover path\n // triggered. A no-op when no hover signal is pending (the common case:\n // renders triggered by anything else, or the fast path above, which\n // measures itself synchronously) or when GTKX_PERF is unset.\n useLayoutEffect(() => {\n if (!perfEnabled || hoverSignalAt.current === null) {\n return\n }\n perfAddTime(\"pressable.hoverApply\", perfNow() - hoverSignalAt.current)\n perfCount(\"pressable.hoverFullCycle\")\n hoverSignalAt.current = null\n })\n\n const clearLongPress = (): void => {\n if (longPressTimer.current !== null) {\n clearTimeout(longPressTimer.current)\n longPressTimer.current = null\n }\n }\n\n const handlePressed = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n longPressFired.current = false\n setPressed(true)\n const widget = widgetRef.current\n onPressIn?.(pressEvent(widget, x, y))\n if (onLongPress) {\n clearLongPress()\n longPressTimer.current = setTimeout(() => {\n longPressFired.current = true\n // Timestamped when it FIRES, not when the press started — the delay\n // is the whole point of the event.\n onLongPress(pressEvent(widgetRef.current, x, y))\n }, delayLongPress)\n }\n }\n\n const handleReleased = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n clearLongPress()\n setPressed(false)\n const widget = widgetRef.current\n onPressOut?.(pressEvent(widget, x, y))\n if (!longPressFired.current) {\n onPress?.(pressEvent(widget, x, y))\n }\n }\n\n const handleCancel = (): void => {\n clearLongPress()\n setPressed(false)\n }\n\n // Applies a hover transition: fires the RN callback, then either swaps\n // the precomputed CSS class directly on the widget (fast path) or forces\n // the ordinary render (slow path) — see the `canSwapHoverClass` block\n // above for which one applies this render.\n const applyHover = (next: boolean, fire: (() => void) | undefined): void => {\n const t0 = perfEnabled ? perfNow() : 0\n hoveredRef.current = next\n fire?.()\n if (!canSwapHoverClass) {\n if (perfEnabled) {\n hoverSignalAt.current = t0\n perfCount(\"pressable.hoverEvent\")\n }\n forceRender()\n return\n }\n const widget = widgetRef.current\n const applyClass = next ? hoverOnClass : hoverOffClass\n const removeClass = next ? hoverOffClass : hoverOnClass\n if (widget && hoverOnClass !== hoverOffClass) {\n if (removeClass) {\n widget.removeCssClass(removeClass)\n }\n if (applyClass) {\n widget.addCssClass(applyClass)\n }\n }\n if (perfEnabled) {\n perfAddTime(\"pressable.hoverApply\", perfNow() - t0)\n perfCount(\"pressable.hoverEvent\")\n perfCount(\"pressable.hoverFast\")\n }\n }\n\n handlersRef.current = {\n handlePressed,\n handleReleased,\n handleCancel,\n handleEnter: () => applyHover(true, onHoverIn),\n handleLeave: () => applyHover(false, onHoverOut),\n }\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const click = new Gtk.GestureClick()\n click.on(\"pressed\", (n: number, x: number, y: number) =>\n handlersRef.current.handlePressed(n, x, y),\n )\n click.on(\"released\", (n: number, x: number, y: number) =>\n handlersRef.current.handleReleased(n, x, y),\n )\n click.on(\"cancel\", () => handlersRef.current.handleCancel())\n widget.addController(click)\n\n const motion = new Gtk.EventControllerMotion()\n motion.on(\"enter\", () => handlersRef.current.handleEnter())\n motion.on(\"leave\", () => handlersRef.current.handleLeave())\n widget.addController(motion)\n\n return () => {\n widget.removeController(click)\n widget.removeController(motion)\n }\n }, [])\n\n return (\n <GtkBox\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {resolvedChildren}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\nexport type TouchableOpacityProps = Omit<PressableProps, \"style\"> & {\n style?: StyleProp\n activeOpacity?: number\n}\n\n// Classic RN touchable: dims itself while pressed via widget opacity.\nexport const TouchableOpacity = ({\n style,\n activeOpacity = 0.5,\n ...rest\n}: TouchableOpacityProps) => (\n <Pressable\n {...rest}\n style={({ pressed }) => [style, pressed && { opacity: activeOpacity }]}\n />\n)\n"]}
|
|
@@ -241,13 +241,19 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
|
|
|
241
241
|
if (!widget || !contentRect) {
|
|
242
242
|
return;
|
|
243
243
|
}
|
|
244
|
+
const hadjustment = widget.getHadjustment();
|
|
245
|
+
const vadjustment = widget.getVadjustment();
|
|
244
246
|
onScroll({
|
|
245
247
|
nativeEvent: {
|
|
246
248
|
contentOffset: {
|
|
247
|
-
x:
|
|
248
|
-
y:
|
|
249
|
+
x: hadjustment?.getValue() ?? 0,
|
|
250
|
+
y: vadjustment?.getValue() ?? 0,
|
|
249
251
|
},
|
|
250
252
|
contentSize: { width: contentRect.width, height: contentRect.height },
|
|
253
|
+
layoutMeasurement: {
|
|
254
|
+
width: hadjustment?.getPageSize() ?? 0,
|
|
255
|
+
height: vadjustment?.getPageSize() ?? 0,
|
|
256
|
+
},
|
|
251
257
|
},
|
|
252
258
|
});
|
|
253
259
|
};
|
|
@@ -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 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 { HostNodeContext } from \"./host-node\"\nimport { deferDuringAllocate, setStoredOffset } from \"./rect-store\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type ScrollViewHandle = {\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void\n scrollToEnd(options?: { animated?: boolean }): void\n}\n\nexport type ScrollEvent = {\n nativeEvent: {\n contentOffset: { x: number; y: number }\n contentSize: { width: number; height: number }\n }\n}\n\nexport type ScrollViewProps = {\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 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 onContentSizeChange,\n onLayout,\n children,\n testID,\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 style: [\n style,\n { overflow: \"scroll\", flexDirection: horizontal ? \"row\" : \"column\" },\n ],\n onLayout,\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 // RN semantics: a ScrollView CLIPS its content. Branch B made containers\n // paint-overflow by default, so without this the scrolled-away content\n // (e.g. the original of a pinned sticky header) keeps drawing past the\n // scroll area's edges.\n useLayoutEffect(() => {\n outerRef.current?.setOverflow(Gtk.Overflow.HIDDEN)\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 alignItems: \"flex-start\",\n ...contentLayout,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [contentNode, contentKey])\n\n useImperativeHandle(handleRef, () => ({\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(adjustment.getUpper() - adjustment.getPageSize())\n }\n },\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; GTK's z-order is\n // sibling paint order, so the active slot is reordered to be the LAST\n // content child while pinned and restored afterwards. 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 const emitScroll = (): void => {\n if (!onScroll) {\n return\n }\n const widget = outerRef.current\n const contentRect = contentNode.getRect()\n if (!widget || !contentRect) {\n return\n }\n onScroll({\n nativeEvent: {\n contentOffset: {\n x: widget.getHadjustment()?.getValue() ?? 0,\n y: widget.getVadjustment()?.getValue() ?? 0,\n },\n contentSize: { width: contentRect.width, height: contentRect.height },\n },\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 // 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 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 { HostNodeContext } from \"./host-node\"\nimport { deferDuringAllocate, setStoredOffset } from \"./rect-store\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type ScrollViewHandle = {\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void\n scrollToEnd(options?: { animated?: boolean }): void\n}\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\nexport type ScrollViewProps = {\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 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 onContentSizeChange,\n onLayout,\n children,\n testID,\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 style: [\n style,\n { overflow: \"scroll\", flexDirection: horizontal ? \"row\" : \"column\" },\n ],\n onLayout,\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 // RN semantics: a ScrollView CLIPS its content. Branch B made containers\n // paint-overflow by default, so without this the scrolled-away content\n // (e.g. the original of a pinned sticky header) keeps drawing past the\n // scroll area's edges.\n useLayoutEffect(() => {\n outerRef.current?.setOverflow(Gtk.Overflow.HIDDEN)\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 alignItems: \"flex-start\",\n ...contentLayout,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [contentNode, contentKey])\n\n useImperativeHandle(handleRef, () => ({\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(adjustment.getUpper() - adjustment.getPageSize())\n }\n },\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; GTK's z-order is\n // sibling paint order, so the active slot is reordered to be the LAST\n // content child while pinned and restored afterwards. 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 const emitScroll = (): void => {\n if (!onScroll) {\n return\n }\n const widget = outerRef.current\n const contentRect = contentNode.getRect()\n if (!widget || !contentRect) {\n return\n }\n const hadjustment = widget.getHadjustment()\n const vadjustment = widget.getVadjustment()\n onScroll({\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 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 // 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"]}
|
|
@@ -10,7 +10,7 @@ const INPUT_PURPOSE = {
|
|
|
10
10
|
"phone-pad": Gtk.InputPurpose.PHONE,
|
|
11
11
|
url: Gtk.InputPurpose.URL,
|
|
12
12
|
};
|
|
13
|
-
// The focus controller is attached imperatively (rc.
|
|
13
|
+
// The focus controller is attached imperatively (rc.3 also offers a
|
|
14
14
|
// declarative `controllers` slot): one wiring per widget, handlers read from a
|
|
15
15
|
// ref, so changing onFocus/onBlur never re-creates the controller.
|
|
16
16
|
const useFocusController = (widgetRef, onFocus, onBlur) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-input.js","sourceRoot":"","sources":["../../src/components/text-input.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, useRef, useState } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport {\n Gtk,\n GtkEntry,\n GtkLabel,\n GtkScrolledWindow,\n GtkTextView,\n type Gtk as GtkNs,\n} from \"../gtkx/bridge/index\"\nimport { useLayoutChild, type LayoutEvent } from \"./use-layout-child\"\nimport { View } from \"./view\"\n\nexport type TextInputProps = {\n value?: string\n defaultValue?: string\n onChangeText?: (text: string) => void\n onSubmitEditing?: (event: { nativeEvent: { text: string } }) => void\n onFocus?: () => void\n onBlur?: () => void\n placeholder?: string\n secureTextEntry?: boolean\n editable?: boolean\n keyboardType?: \"default\" | \"numeric\" | \"email-address\" | \"phone-pad\" | \"url\"\n // RN: a multiline input needs a height in the style; Enter inserts a\n // newline and never fires onSubmitEditing.\n multiline?: boolean\n // RN's iOS-only clear affordance, native here: GtkEntry's built-in\n // secondary icon (inside the field, so nothing shifts around it).\n // Single-line only, like RN.\n clearButtonMode?: \"never\" | \"while-editing\" | \"unless-editing\" | \"always\"\n style?: StyleProp\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\nconst INPUT_PURPOSE: Record<\n NonNullable<TextInputProps[\"keyboardType\"]>,\n Gtk.InputPurpose\n> = {\n default: Gtk.InputPurpose.FREE_FORM,\n numeric: Gtk.InputPurpose.NUMBER,\n \"email-address\": Gtk.InputPurpose.EMAIL,\n \"phone-pad\": Gtk.InputPurpose.PHONE,\n url: Gtk.InputPurpose.URL,\n}\n\n// The focus controller is attached imperatively (rc.2 also offers a\n// declarative `controllers` slot): one wiring per widget, handlers read from a\n// ref, so changing onFocus/onBlur never re-creates the controller.\nconst useFocusController = (\n widgetRef: React.RefObject<GtkNs.Widget | null>,\n onFocus?: () => void,\n onBlur?: () => void,\n): void => {\n const handlers = useRef({ onFocus, onBlur })\n handlers.current = { onFocus, onBlur }\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const focus = new Gtk.EventControllerFocus()\n const enter = (): void => handlers.current.onFocus?.()\n const leave = (): void => handlers.current.onBlur?.()\n focus.on(\"enter\", enter)\n focus.on(\"leave\", leave)\n widget.addController(focus)\n return () => {\n widget.removeController(focus)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n}\n\n// Single-line input over GtkEntry. gtkx's controlled-text behavior keeps the\n// widget in sync with the `text` prop (RN controlled semantics); uncontrolled\n// usage passes defaultValue once.\nconst SingleLineTextInput = ({\n value,\n defaultValue,\n onChangeText,\n onSubmitEditing,\n onFocus,\n onBlur,\n placeholder,\n secureTextEntry = false,\n editable = true,\n keyboardType = \"default\",\n clearButtonMode = \"never\",\n style,\n onLayout,\n testID,\n}: TextInputProps) => {\n const widgetRef = useRef<GtkNs.Entry | null>(null)\n const [focused, setFocused] = useState(false)\n\n useLayoutChild(widgetRef, {\n style,\n onLayout,\n // The entry measures itself (theme-accurate height); width is driven by\n // the style (flex/width), with a small floor for usability.\n measureFromWidget: true,\n extraLayout: { minWidth: 60 },\n })\n\n const controlled = value !== undefined\n\n useFocusController(\n widgetRef,\n () => {\n setFocused(true)\n onFocus?.()\n },\n () => {\n setFocused(false)\n onBlur?.()\n },\n )\n\n // RN semantics: the affordance only shows when there is something to\n // clear, and while-editing / unless-editing follow focus.\n const text = controlled ? value : undefined\n const hasText =\n text !== undefined\n ? text.length > 0\n : (widgetRef.current?.getText().length ?? 0) > 0\n const showClear =\n hasText &&\n (clearButtonMode === \"always\" ||\n (clearButtonMode === \"while-editing\" && focused) ||\n (clearButtonMode === \"unless-editing\" && !focused))\n\n return (\n <GtkEntry\n ref={widgetRef}\n name={testID}\n secondaryIconName={showClear ? \"edit-clear-symbolic\" : null}\n secondaryIconActivatable={showClear}\n secondaryIconTooltipText={showClear ? \"Clear\" : null}\n onIconRelease={() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n // Uncontrolled inputs clear themselves; controlled ones report an\n // empty string and let the owner drive the value (RN contract).\n if (!controlled) {\n widget.setText(\"\")\n }\n onChangeText?.(\"\")\n }}\n text={controlled ? value : (defaultValue ?? \"\")}\n placeholderText={placeholder ?? \"\"}\n visibility={!secureTextEntry}\n editable={editable}\n sensitive={editable}\n inputPurpose={INPUT_PURPOSE[keyboardType]}\n onChanged={() => {\n const widget = widgetRef.current\n if (widget) {\n onChangeText?.(widget.getText())\n }\n }}\n onActivate={() => {\n const widget = widgetRef.current\n if (widget) {\n onSubmitEditing?.({ nativeEvent: { text: widget.getText() } })\n }\n }}\n />\n )\n}\n\nconst readBuffer = (buffer: GtkNs.TextBuffer): string =>\n buffer.getText(buffer.getStartIter(), buffer.getEndIter(), true)\n\n// GtkTextView has no native placeholder — a dim, click-transparent label\n// sits over the empty view (RN shows the placeholder while empty, focused\n// or not).\nconst MultilinePlaceholder = ({ text }: { text: string }) => {\n const labelRef = useRef<GtkNs.Label | null>(null)\n useLayoutChild(labelRef, {\n style: { position: \"absolute\", left: 12, top: 8 },\n onLayout: undefined,\n })\n useLayoutEffect(() => {\n labelRef.current?.setCanTarget(false)\n labelRef.current?.addCssClass(\"dim-label\")\n }, [])\n return (\n <GtkLabel\n ref={labelRef}\n label={text}\n />\n )\n}\n\n// Multiline input over GtkTextView in a GtkScrolledWindow: word wrap, the\n// style sets the box (RN requires a height for multiline), overflowing\n// content scrolls inside. The buffer syncs with `value` under RN controlled\n// semantics; Enter inserts a newline (onSubmitEditing never fires — RN\n// multiline behavior).\nconst MultilineTextInput = ({\n value,\n defaultValue,\n onChangeText,\n onFocus,\n onBlur,\n placeholder,\n editable = true,\n style,\n onLayout,\n testID,\n}: TextInputProps) => {\n const scrolledRef = useRef<GtkNs.ScrolledWindow | null>(null)\n const viewRef = useRef<GtkNs.TextView | null>(null)\n const controlled = value !== undefined\n const [empty, setEmpty] = useState(\n (controlled ? (value ?? \"\") : (defaultValue ?? \"\")) === \"\",\n )\n\n const suppressEcho = useRef(false)\n const latest = useRef({ onChangeText })\n latest.current = { onChangeText }\n\n useLayoutEffect(() => {\n const view = viewRef.current\n if (!view) {\n return\n }\n // Breathing room akin to GtkEntry's built-in padding.\n view.setLeftMargin(10)\n view.setRightMargin(10)\n view.setTopMargin(8)\n view.setBottomMargin(8)\n const buffer = view.getBuffer()\n const initial = controlled ? (value ?? \"\") : (defaultValue ?? \"\")\n if (initial !== \"\") {\n suppressEcho.current = true\n buffer.setText(initial, -1)\n suppressEcho.current = false\n }\n const changed = (): void => {\n const text = readBuffer(buffer)\n setEmpty(text === \"\")\n if (!suppressEcho.current) {\n latest.current.onChangeText?.(text)\n }\n }\n buffer.on(\"changed\", changed)\n return () => {\n buffer.off(\"changed\", changed)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // RN controlled semantics: the prop wins over whatever was typed.\n useLayoutEffect(() => {\n if (!controlled) {\n return\n }\n const view = viewRef.current\n if (!view) {\n return\n }\n const buffer = view.getBuffer()\n const next = value ?? \"\"\n if (readBuffer(buffer) !== next) {\n suppressEcho.current = true\n buffer.setText(next, -1)\n suppressEcho.current = false\n setEmpty(next === \"\")\n }\n }, [controlled, value])\n\n useFocusController(viewRef, onFocus, onBlur)\n\n return (\n <View\n style={[style, { minHeight: 36, minWidth: 60 }]}\n onLayout={onLayout}\n >\n <ScrolledFill scrolledRef={scrolledRef}>\n <GtkTextView\n ref={viewRef}\n name={testID}\n editable={editable}\n sensitive={editable}\n wrapMode={Gtk.WrapMode.WORD_CHAR}\n />\n </ScrolledFill>\n {empty && placeholder ? (\n <MultilinePlaceholder text={placeholder} />\n ) : null}\n </View>\n )\n}\n\n// The scrolled window fills the styled box exactly (its own layout child).\nconst ScrolledFill = ({\n scrolledRef,\n children,\n}: {\n scrolledRef: React.RefObject<GtkNs.ScrolledWindow | null>\n children: React.ReactNode\n}) => {\n useLayoutChild(scrolledRef, {\n style: { position: \"absolute\", left: 0, right: 0, top: 0, bottom: 0 },\n onLayout: undefined,\n })\n return <GtkScrolledWindow ref={scrolledRef}>{children}</GtkScrolledWindow>\n}\n\nexport const TextInput = (props: TextInputProps) =>\n props.multiline ? (\n <MultilineTextInput {...props} />\n ) : (\n <SingleLineTextInput {...props} />\n )\n"]}
|
|
1
|
+
{"version":3,"file":"text-input.js","sourceRoot":"","sources":["../../src/components/text-input.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, useRef, useState } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport {\n Gtk,\n GtkEntry,\n GtkLabel,\n GtkScrolledWindow,\n GtkTextView,\n type Gtk as GtkNs,\n} from \"../gtkx/bridge/index\"\nimport { useLayoutChild, type LayoutEvent } from \"./use-layout-child\"\nimport { View } from \"./view\"\n\nexport type TextInputProps = {\n value?: string\n defaultValue?: string\n onChangeText?: (text: string) => void\n onSubmitEditing?: (event: { nativeEvent: { text: string } }) => void\n onFocus?: () => void\n onBlur?: () => void\n placeholder?: string\n secureTextEntry?: boolean\n editable?: boolean\n keyboardType?: \"default\" | \"numeric\" | \"email-address\" | \"phone-pad\" | \"url\"\n // RN: a multiline input needs a height in the style; Enter inserts a\n // newline and never fires onSubmitEditing.\n multiline?: boolean\n // RN's iOS-only clear affordance, native here: GtkEntry's built-in\n // secondary icon (inside the field, so nothing shifts around it).\n // Single-line only, like RN.\n clearButtonMode?: \"never\" | \"while-editing\" | \"unless-editing\" | \"always\"\n style?: StyleProp\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\nconst INPUT_PURPOSE: Record<\n NonNullable<TextInputProps[\"keyboardType\"]>,\n Gtk.InputPurpose\n> = {\n default: Gtk.InputPurpose.FREE_FORM,\n numeric: Gtk.InputPurpose.NUMBER,\n \"email-address\": Gtk.InputPurpose.EMAIL,\n \"phone-pad\": Gtk.InputPurpose.PHONE,\n url: Gtk.InputPurpose.URL,\n}\n\n// The focus controller is attached imperatively (rc.3 also offers a\n// declarative `controllers` slot): one wiring per widget, handlers read from a\n// ref, so changing onFocus/onBlur never re-creates the controller.\nconst useFocusController = (\n widgetRef: React.RefObject<GtkNs.Widget | null>,\n onFocus?: () => void,\n onBlur?: () => void,\n): void => {\n const handlers = useRef({ onFocus, onBlur })\n handlers.current = { onFocus, onBlur }\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const focus = new Gtk.EventControllerFocus()\n const enter = (): void => handlers.current.onFocus?.()\n const leave = (): void => handlers.current.onBlur?.()\n focus.on(\"enter\", enter)\n focus.on(\"leave\", leave)\n widget.addController(focus)\n return () => {\n widget.removeController(focus)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n}\n\n// Single-line input over GtkEntry. gtkx's controlled-text behavior keeps the\n// widget in sync with the `text` prop (RN controlled semantics); uncontrolled\n// usage passes defaultValue once.\nconst SingleLineTextInput = ({\n value,\n defaultValue,\n onChangeText,\n onSubmitEditing,\n onFocus,\n onBlur,\n placeholder,\n secureTextEntry = false,\n editable = true,\n keyboardType = \"default\",\n clearButtonMode = \"never\",\n style,\n onLayout,\n testID,\n}: TextInputProps) => {\n const widgetRef = useRef<GtkNs.Entry | null>(null)\n const [focused, setFocused] = useState(false)\n\n useLayoutChild(widgetRef, {\n style,\n onLayout,\n // The entry measures itself (theme-accurate height); width is driven by\n // the style (flex/width), with a small floor for usability.\n measureFromWidget: true,\n extraLayout: { minWidth: 60 },\n })\n\n const controlled = value !== undefined\n\n useFocusController(\n widgetRef,\n () => {\n setFocused(true)\n onFocus?.()\n },\n () => {\n setFocused(false)\n onBlur?.()\n },\n )\n\n // RN semantics: the affordance only shows when there is something to\n // clear, and while-editing / unless-editing follow focus.\n const text = controlled ? value : undefined\n const hasText =\n text !== undefined\n ? text.length > 0\n : (widgetRef.current?.getText().length ?? 0) > 0\n const showClear =\n hasText &&\n (clearButtonMode === \"always\" ||\n (clearButtonMode === \"while-editing\" && focused) ||\n (clearButtonMode === \"unless-editing\" && !focused))\n\n return (\n <GtkEntry\n ref={widgetRef}\n name={testID}\n secondaryIconName={showClear ? \"edit-clear-symbolic\" : null}\n secondaryIconActivatable={showClear}\n secondaryIconTooltipText={showClear ? \"Clear\" : null}\n onIconRelease={() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n // Uncontrolled inputs clear themselves; controlled ones report an\n // empty string and let the owner drive the value (RN contract).\n if (!controlled) {\n widget.setText(\"\")\n }\n onChangeText?.(\"\")\n }}\n text={controlled ? value : (defaultValue ?? \"\")}\n placeholderText={placeholder ?? \"\"}\n visibility={!secureTextEntry}\n editable={editable}\n sensitive={editable}\n inputPurpose={INPUT_PURPOSE[keyboardType]}\n onChanged={() => {\n const widget = widgetRef.current\n if (widget) {\n onChangeText?.(widget.getText())\n }\n }}\n onActivate={() => {\n const widget = widgetRef.current\n if (widget) {\n onSubmitEditing?.({ nativeEvent: { text: widget.getText() } })\n }\n }}\n />\n )\n}\n\nconst readBuffer = (buffer: GtkNs.TextBuffer): string =>\n buffer.getText(buffer.getStartIter(), buffer.getEndIter(), true)\n\n// GtkTextView has no native placeholder — a dim, click-transparent label\n// sits over the empty view (RN shows the placeholder while empty, focused\n// or not).\nconst MultilinePlaceholder = ({ text }: { text: string }) => {\n const labelRef = useRef<GtkNs.Label | null>(null)\n useLayoutChild(labelRef, {\n style: { position: \"absolute\", left: 12, top: 8 },\n onLayout: undefined,\n })\n useLayoutEffect(() => {\n labelRef.current?.setCanTarget(false)\n labelRef.current?.addCssClass(\"dim-label\")\n }, [])\n return (\n <GtkLabel\n ref={labelRef}\n label={text}\n />\n )\n}\n\n// Multiline input over GtkTextView in a GtkScrolledWindow: word wrap, the\n// style sets the box (RN requires a height for multiline), overflowing\n// content scrolls inside. The buffer syncs with `value` under RN controlled\n// semantics; Enter inserts a newline (onSubmitEditing never fires — RN\n// multiline behavior).\nconst MultilineTextInput = ({\n value,\n defaultValue,\n onChangeText,\n onFocus,\n onBlur,\n placeholder,\n editable = true,\n style,\n onLayout,\n testID,\n}: TextInputProps) => {\n const scrolledRef = useRef<GtkNs.ScrolledWindow | null>(null)\n const viewRef = useRef<GtkNs.TextView | null>(null)\n const controlled = value !== undefined\n const [empty, setEmpty] = useState(\n (controlled ? (value ?? \"\") : (defaultValue ?? \"\")) === \"\",\n )\n\n const suppressEcho = useRef(false)\n const latest = useRef({ onChangeText })\n latest.current = { onChangeText }\n\n useLayoutEffect(() => {\n const view = viewRef.current\n if (!view) {\n return\n }\n // Breathing room akin to GtkEntry's built-in padding.\n view.setLeftMargin(10)\n view.setRightMargin(10)\n view.setTopMargin(8)\n view.setBottomMargin(8)\n const buffer = view.getBuffer()\n const initial = controlled ? (value ?? \"\") : (defaultValue ?? \"\")\n if (initial !== \"\") {\n suppressEcho.current = true\n buffer.setText(initial, -1)\n suppressEcho.current = false\n }\n const changed = (): void => {\n const text = readBuffer(buffer)\n setEmpty(text === \"\")\n if (!suppressEcho.current) {\n latest.current.onChangeText?.(text)\n }\n }\n buffer.on(\"changed\", changed)\n return () => {\n buffer.off(\"changed\", changed)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // RN controlled semantics: the prop wins over whatever was typed.\n useLayoutEffect(() => {\n if (!controlled) {\n return\n }\n const view = viewRef.current\n if (!view) {\n return\n }\n const buffer = view.getBuffer()\n const next = value ?? \"\"\n if (readBuffer(buffer) !== next) {\n suppressEcho.current = true\n buffer.setText(next, -1)\n suppressEcho.current = false\n setEmpty(next === \"\")\n }\n }, [controlled, value])\n\n useFocusController(viewRef, onFocus, onBlur)\n\n return (\n <View\n style={[style, { minHeight: 36, minWidth: 60 }]}\n onLayout={onLayout}\n >\n <ScrolledFill scrolledRef={scrolledRef}>\n <GtkTextView\n ref={viewRef}\n name={testID}\n editable={editable}\n sensitive={editable}\n wrapMode={Gtk.WrapMode.WORD_CHAR}\n />\n </ScrolledFill>\n {empty && placeholder ? (\n <MultilinePlaceholder text={placeholder} />\n ) : null}\n </View>\n )\n}\n\n// The scrolled window fills the styled box exactly (its own layout child).\nconst ScrolledFill = ({\n scrolledRef,\n children,\n}: {\n scrolledRef: React.RefObject<GtkNs.ScrolledWindow | null>\n children: React.ReactNode\n}) => {\n useLayoutChild(scrolledRef, {\n style: { position: \"absolute\", left: 0, right: 0, top: 0, bottom: 0 },\n onLayout: undefined,\n })\n return <GtkScrolledWindow ref={scrolledRef}>{children}</GtkScrolledWindow>\n}\n\nexport const TextInput = (props: TextInputProps) =>\n props.multiline ? (\n <MultilineTextInput {...props} />\n ) : (\n <SingleLineTextInput {...props} />\n )\n"]}
|
package/dist/components/text.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
3
|
-
import { textAlignToLabelProps } from "../style/index";
|
|
3
|
+
import { textAlignToLabelProps, textDecorationToAttrs } from "../style/index";
|
|
4
4
|
import { createTextProbe, Gtk, GtkLabel, measureWidget, Pango, } from "../gtkx/bridge/index";
|
|
5
5
|
import { useLayoutChild } from "./use-layout-child";
|
|
6
6
|
const flattenToString = (children) => {
|
|
@@ -97,6 +97,30 @@ export const Text = ({ style, onLayout, numberOfLines, children, testID, }) => {
|
|
|
97
97
|
node.markDirty();
|
|
98
98
|
}, [probe, node, text, numberOfLines, cssClass]);
|
|
99
99
|
const align = textAlignToLabelProps(flat.textAlign);
|
|
100
|
+
// textDecorationLine goes through Pango, not CSS: GTK4 has no widget
|
|
101
|
+
// `text-decoration`. Built here rather than in the style module so that
|
|
102
|
+
// module stays bridge-free and unit-testable off Linux — the same split
|
|
103
|
+
// textAlign already uses.
|
|
104
|
+
//
|
|
105
|
+
// The attribute list is applied to the PROBE as well, because it is what
|
|
106
|
+
// Yoga measures: Pango reserves room for an underline below the baseline,
|
|
107
|
+
// so a decorated label measured undecorated would be a pixel short.
|
|
108
|
+
const decoration = textDecorationToAttrs(flat.textDecorationLine);
|
|
109
|
+
const decorated = decoration.underline || decoration.strikethrough;
|
|
110
|
+
useLayoutEffect(() => {
|
|
111
|
+
const attrs = decorated ? Pango.AttrList.new() : null;
|
|
112
|
+
if (attrs) {
|
|
113
|
+
if (decoration.underline) {
|
|
114
|
+
attrs.insert(Pango.attrUnderlineNew(Pango.Underline.SINGLE));
|
|
115
|
+
}
|
|
116
|
+
if (decoration.strikethrough) {
|
|
117
|
+
attrs.insert(Pango.attrStrikethroughNew(true));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
widgetRef.current?.setAttributes(attrs);
|
|
121
|
+
probe.setAttributes(attrs);
|
|
122
|
+
node.markDirty();
|
|
123
|
+
}, [probe, node, decorated, decoration.underline, decoration.strikethrough]);
|
|
100
124
|
return (_jsx(GtkLabel, { ref: widgetRef, name: testID, label: text, wrap: true, xalign: align.xalign, yalign: 0, justify: JUSTIFICATION[align.justification], lines: numberOfLines,
|
|
101
125
|
// RN semantics: the ellipsis is opt-in via numberOfLines. Plain text
|
|
102
126
|
// wraps naturally; an unbreakable word wider than the rect just clips
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/components/text.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { textAlignToLabelProps } from \"../style/index\"\nimport type { MeasureFn, StyleProp } from \"../contracts\"\nimport {\n createTextProbe,\n Gtk,\n GtkLabel,\n measureWidget,\n Pango,\n} from \"../gtkx/bridge/index\"\nimport { useLayoutChild, type LayoutEvent } from \"./use-layout-child\"\n\nexport type TextProps = {\n style?: StyleProp\n onLayout?: (event: LayoutEvent) => void\n numberOfLines?: number\n children?: ReactNode\n testID?: string\n}\n\nconst flattenToString = (children: ReactNode): string => {\n if (children === null || children === undefined || children === false) {\n return \"\"\n }\n if (Array.isArray(children)) {\n return children.map(flattenToString).join(\"\")\n }\n return String(children)\n}\n\nconst JUSTIFICATION = {\n left: Gtk.Justification.LEFT,\n right: Gtk.Justification.RIGHT,\n center: Gtk.Justification.CENTER,\n fill: Gtk.Justification.FILL,\n} as const\n\n// Text is a measured Yoga leaf: an offscreen probe label (with the same CSS\n// class, so fonts match) feeds Pango metrics into the layout engine; the\n// visible GtkLabel is positioned by the commit hook like any other widget.\nexport const Text = ({\n style,\n onLayout,\n numberOfLines,\n children,\n testID,\n}: TextProps) => {\n const widgetRef = useRef<Gtk.Label | null>(null)\n const text = flattenToString(children)\n\n // useState lazy init — see the note in use-layout-child.ts (React Compiler).\n const [probe] = useState<Gtk.Label>(() => createTextProbe())\n\n const lastProbeClass = useRef<string | null>(null)\n\n // GTK collapses wrap+ellipsize labels to a single line unless `lines` is\n // set explicitly — remember how many lines the probe wrapped into, the\n // layout callback pushes it onto the visible label.\n const measuredLines = useRef(1)\n\n const measure = useMemo<MeasureFn>(() => {\n return (width, widthMode) => {\n const { minimum, natural } = measureWidget(probe, \"horizontal\")\n // Floor at the probe's own minimum width, not at 1: gtk_widget_measure()\n // clamps a height-for-width query up to the widget's minimum before\n // computing regardless of what we pass (logging \"Trying to measure\n // GtkLabel for width of N, but it needs at least M\" while doing it), so\n // asking for anything below `minimum` was never honored — the height\n // below was already being computed at `minimum`, not at our `used`.\n // Clamping here ourselves gets the same height GTK would give us\n // anyway, keeps the returned width consistent with it, and drops the\n // warning as a side effect rather than the goal.\n const used =\n widthMode === \"undefined\"\n ? natural\n : Math.min(natural, Math.max(minimum, Math.floor(width)))\n const height = measureWidget(probe, \"vertical\", used).natural\n const singleLine = measureWidget(probe, \"vertical\", natural).natural\n measuredLines.current =\n singleLine > 0 ? Math.max(1, Math.round(height / singleLine)) : 1\n return { width: used, height }\n }\n // The probe is stable; re-measure is triggered via markDirty below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const layoutWithLines = useMemo(\n () => (event: LayoutEvent) => {\n const label = widgetRef.current\n if (label) {\n label.setLines(numberOfLines ?? measuredLines.current)\n }\n onLayout?.(event)\n },\n [numberOfLines, onLayout],\n )\n\n const { node, cssClass, flat } = useLayoutChild(widgetRef, {\n style,\n onLayout: layoutWithLines,\n measure,\n })\n\n // RN text clips to its box: an under-allocated GtkLabel would otherwise\n // paint its full text past the allocation (spike finding). Containers keep\n // overflow VISIBLE (paint-overflow); the text leaf clips.\n useLayoutEffect(() => {\n widgetRef.current?.setOverflow(Gtk.Overflow.HIDDEN)\n }, [])\n\n // Keep the probe in sync with everything that affects metrics, then\n // invalidate the Yoga leaf so the next pass re-measures.\n useLayoutEffect(() => {\n probe.setText(text)\n if (numberOfLines !== undefined) {\n probe.setLines(numberOfLines)\n probe.setEllipsize(Pango.EllipsizeMode.END)\n } else {\n probe.setLines(-1)\n probe.setEllipsize(Pango.EllipsizeMode.NONE)\n }\n if (lastProbeClass.current !== cssClass) {\n if (lastProbeClass.current) {\n probe.removeCssClass(lastProbeClass.current)\n }\n if (cssClass) {\n probe.addCssClass(cssClass)\n }\n lastProbeClass.current = cssClass\n }\n node.markDirty()\n }, [probe, node, text, numberOfLines, cssClass])\n\n const align = textAlignToLabelProps(flat.textAlign)\n\n return (\n <GtkLabel\n ref={widgetRef}\n name={testID}\n label={text}\n wrap\n xalign={align.xalign}\n yalign={0}\n justify={JUSTIFICATION[align.justification]}\n lines={numberOfLines}\n // RN semantics: the ellipsis is opt-in via numberOfLines. Plain text\n // wraps naturally; an unbreakable word wider than the rect just clips\n // (overflow HIDDEN above) — the layout manager allocates the rect\n // regardless of the label's own minimum, so nothing pushes siblings.\n ellipsize={\n numberOfLines !== undefined\n ? Pango.EllipsizeMode.END\n : Pango.EllipsizeMode.NONE\n }\n cssClasses={cssClass ? [cssClass] : []}\n />\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/components/text.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { textAlignToLabelProps, textDecorationToAttrs } from \"../style/index\"\nimport type { MeasureFn, StyleProp } from \"../contracts\"\nimport {\n createTextProbe,\n Gtk,\n GtkLabel,\n measureWidget,\n Pango,\n} from \"../gtkx/bridge/index\"\nimport { useLayoutChild, type LayoutEvent } from \"./use-layout-child\"\n\nexport type TextProps = {\n style?: StyleProp\n onLayout?: (event: LayoutEvent) => void\n numberOfLines?: number\n children?: ReactNode\n testID?: string\n}\n\nconst flattenToString = (children: ReactNode): string => {\n if (children === null || children === undefined || children === false) {\n return \"\"\n }\n if (Array.isArray(children)) {\n return children.map(flattenToString).join(\"\")\n }\n return String(children)\n}\n\nconst JUSTIFICATION = {\n left: Gtk.Justification.LEFT,\n right: Gtk.Justification.RIGHT,\n center: Gtk.Justification.CENTER,\n fill: Gtk.Justification.FILL,\n} as const\n\n// Text is a measured Yoga leaf: an offscreen probe label (with the same CSS\n// class, so fonts match) feeds Pango metrics into the layout engine; the\n// visible GtkLabel is positioned by the commit hook like any other widget.\nexport const Text = ({\n style,\n onLayout,\n numberOfLines,\n children,\n testID,\n}: TextProps) => {\n const widgetRef = useRef<Gtk.Label | null>(null)\n const text = flattenToString(children)\n\n // useState lazy init — see the note in use-layout-child.ts (React Compiler).\n const [probe] = useState<Gtk.Label>(() => createTextProbe())\n\n const lastProbeClass = useRef<string | null>(null)\n\n // GTK collapses wrap+ellipsize labels to a single line unless `lines` is\n // set explicitly — remember how many lines the probe wrapped into, the\n // layout callback pushes it onto the visible label.\n const measuredLines = useRef(1)\n\n const measure = useMemo<MeasureFn>(() => {\n return (width, widthMode) => {\n const { minimum, natural } = measureWidget(probe, \"horizontal\")\n // Floor at the probe's own minimum width, not at 1: gtk_widget_measure()\n // clamps a height-for-width query up to the widget's minimum before\n // computing regardless of what we pass (logging \"Trying to measure\n // GtkLabel for width of N, but it needs at least M\" while doing it), so\n // asking for anything below `minimum` was never honored — the height\n // below was already being computed at `minimum`, not at our `used`.\n // Clamping here ourselves gets the same height GTK would give us\n // anyway, keeps the returned width consistent with it, and drops the\n // warning as a side effect rather than the goal.\n const used =\n widthMode === \"undefined\"\n ? natural\n : Math.min(natural, Math.max(minimum, Math.floor(width)))\n const height = measureWidget(probe, \"vertical\", used).natural\n const singleLine = measureWidget(probe, \"vertical\", natural).natural\n measuredLines.current =\n singleLine > 0 ? Math.max(1, Math.round(height / singleLine)) : 1\n return { width: used, height }\n }\n // The probe is stable; re-measure is triggered via markDirty below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const layoutWithLines = useMemo(\n () => (event: LayoutEvent) => {\n const label = widgetRef.current\n if (label) {\n label.setLines(numberOfLines ?? measuredLines.current)\n }\n onLayout?.(event)\n },\n [numberOfLines, onLayout],\n )\n\n const { node, cssClass, flat } = useLayoutChild(widgetRef, {\n style,\n onLayout: layoutWithLines,\n measure,\n })\n\n // RN text clips to its box: an under-allocated GtkLabel would otherwise\n // paint its full text past the allocation (spike finding). Containers keep\n // overflow VISIBLE (paint-overflow); the text leaf clips.\n useLayoutEffect(() => {\n widgetRef.current?.setOverflow(Gtk.Overflow.HIDDEN)\n }, [])\n\n // Keep the probe in sync with everything that affects metrics, then\n // invalidate the Yoga leaf so the next pass re-measures.\n useLayoutEffect(() => {\n probe.setText(text)\n if (numberOfLines !== undefined) {\n probe.setLines(numberOfLines)\n probe.setEllipsize(Pango.EllipsizeMode.END)\n } else {\n probe.setLines(-1)\n probe.setEllipsize(Pango.EllipsizeMode.NONE)\n }\n if (lastProbeClass.current !== cssClass) {\n if (lastProbeClass.current) {\n probe.removeCssClass(lastProbeClass.current)\n }\n if (cssClass) {\n probe.addCssClass(cssClass)\n }\n lastProbeClass.current = cssClass\n }\n node.markDirty()\n }, [probe, node, text, numberOfLines, cssClass])\n\n const align = textAlignToLabelProps(flat.textAlign)\n\n // textDecorationLine goes through Pango, not CSS: GTK4 has no widget\n // `text-decoration`. Built here rather than in the style module so that\n // module stays bridge-free and unit-testable off Linux — the same split\n // textAlign already uses.\n //\n // The attribute list is applied to the PROBE as well, because it is what\n // Yoga measures: Pango reserves room for an underline below the baseline,\n // so a decorated label measured undecorated would be a pixel short.\n const decoration = textDecorationToAttrs(flat.textDecorationLine)\n const decorated = decoration.underline || decoration.strikethrough\n useLayoutEffect(() => {\n const attrs = decorated ? Pango.AttrList.new() : null\n if (attrs) {\n if (decoration.underline) {\n attrs.insert(Pango.attrUnderlineNew(Pango.Underline.SINGLE))\n }\n if (decoration.strikethrough) {\n attrs.insert(Pango.attrStrikethroughNew(true))\n }\n }\n widgetRef.current?.setAttributes(attrs)\n probe.setAttributes(attrs)\n node.markDirty()\n }, [probe, node, decorated, decoration.underline, decoration.strikethrough])\n\n return (\n <GtkLabel\n ref={widgetRef}\n name={testID}\n label={text}\n wrap\n xalign={align.xalign}\n yalign={0}\n justify={JUSTIFICATION[align.justification]}\n lines={numberOfLines}\n // RN semantics: the ellipsis is opt-in via numberOfLines. Plain text\n // wraps naturally; an unbreakable word wider than the rect just clips\n // (overflow HIDDEN above) — the layout manager allocates the rect\n // regardless of the label's own minimum, so nothing pushes siblings.\n ellipsize={\n numberOfLines !== undefined\n ? Pango.EllipsizeMode.END\n : Pango.EllipsizeMode.NONE\n }\n cssClasses={cssClass ? [cssClass] : []}\n />\n )\n}\n"]}
|
|
@@ -78,24 +78,27 @@ export const useLayoutChild = (widgetRef, options) => {
|
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
const previous = getStoredRect(widget);
|
|
81
|
-
// A commit does not imply a change. The engine
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
// allocation for them makes GTK re-allocate and re-snapshot the whole
|
|
86
|
-
// container for nothing.
|
|
81
|
+
// A commit does not imply a change. The engine may hand us the rect a
|
|
82
|
+
// widget already has — nodes carrying a measure function are visited
|
|
83
|
+
// even when Yoga did not move them — and queueing an allocation for
|
|
84
|
+
// those makes GTK re-allocate and re-snapshot the container for nothing.
|
|
87
85
|
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
|
|
86
|
+
// `rect.skip` counts how often this guard actually fires and
|
|
87
|
+
// `rect.change` how often it does not, because a guard nobody can see
|
|
88
|
+
// firing is a guard nobody can attribute a measurement to: this one was
|
|
89
|
+
// once credited with a large maximized-window win that a later, properly
|
|
90
|
+
// geometry-controlled run could not reproduce (docs/research/
|
|
91
|
+
// scroll-performance.md, round six). Keep it — it is correct and free —
|
|
92
|
+
// but read the counter before crediting it with anything.
|
|
93
|
+
const unchanged = previous !== undefined &&
|
|
95
94
|
previous.x === rect.x &&
|
|
96
95
|
previous.y === rect.y &&
|
|
97
96
|
previous.width === rect.width &&
|
|
98
|
-
previous.height === rect.height
|
|
97
|
+
previous.height === rect.height;
|
|
98
|
+
if (perfEnabled) {
|
|
99
|
+
perfCount(unchanged ? "rect.skip" : "rect.change");
|
|
100
|
+
}
|
|
101
|
+
if (unchanged) {
|
|
99
102
|
return;
|
|
100
103
|
}
|
|
101
104
|
setStoredRect(widget, rect);
|