react-native-gtkx 0.1.0-alpha.4 → 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 +22 -9
- package/dist/adw/index.d.ts +3 -0
- package/dist/adw/index.js +41 -0
- package/dist/adw/index.js.map +1 -0
- package/dist/adw/widgets.generated.d.ts +63 -0
- package/dist/adw/widgets.generated.js +125 -0
- package/dist/adw/widgets.generated.js.map +1 -0
- 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/content.d.ts +69 -0
- package/dist/common/content.js +84 -0
- package/dist/common/content.js.map +1 -0
- 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 +5 -0
- package/dist/common/index.js +21 -0
- package/dist/common/index.js.map +1 -0
- 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/navigation-stack.d.ts +84 -0
- package/dist/common/navigation-stack.js +313 -0
- package/dist/common/navigation-stack.js.map +1 -0
- package/dist/common/widget.d.ts +62 -0
- package/dist/common/widget.js +209 -0
- package/dist/common/widget.js.map +1 -0
- package/dist/components/animated.d.ts +7 -4
- package/dist/components/animated.js +88 -67
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.d.ts +36 -1
- package/dist/components/app-registry.js +8 -3
- 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 +119 -20
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/rect-store.d.ts +11 -1
- package/dist/components/rect-store.js +27 -3
- package/dist/components/rect-store.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/svg/animated-support.d.ts +19 -0
- package/dist/components/svg/animated-support.js +64 -0
- package/dist/components/svg/animated-support.js.map +1 -0
- package/dist/components/svg/context.d.ts +8 -0
- package/dist/components/svg/context.js +15 -0
- package/dist/components/svg/context.js.map +1 -0
- package/dist/components/svg/gradients.d.ts +31 -0
- package/dist/components/svg/gradients.js +93 -0
- package/dist/components/svg/gradients.js.map +1 -0
- package/dist/components/svg/group.d.ts +8 -0
- package/dist/components/svg/group.js +30 -0
- package/dist/components/svg/group.js.map +1 -0
- package/dist/components/svg/index.d.ts +6 -0
- package/dist/components/svg/index.js +5 -0
- package/dist/components/svg/index.js.map +1 -0
- package/dist/components/svg/node.d.ts +7 -0
- package/dist/components/svg/node.js +7 -0
- package/dist/components/svg/node.js.map +1 -0
- package/dist/components/svg/paint.d.ts +24 -0
- package/dist/components/svg/paint.js +57 -0
- package/dist/components/svg/paint.js.map +1 -0
- package/dist/components/svg/shapes.d.ts +47 -0
- package/dist/components/svg/shapes.js +51 -0
- package/dist/components/svg/shapes.js.map +1 -0
- package/dist/components/svg/svg.d.ts +14 -0
- package/dist/components/svg/svg.js +48 -0
- package/dist/components/svg/svg.js.map +1 -0
- package/dist/components/svg/use-shape-node.d.ts +12 -0
- package/dist/components/svg/use-shape-node.js +52 -0
- package/dist/components/svg/use-shape-node.js.map +1 -0
- package/dist/components/text-input.js +1 -1
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.js +36 -3
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-layout-child.js +50 -4
- 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 +31 -4
- package/dist/contracts.js.map +1 -1
- package/dist/gtk/index.d.ts +7 -0
- package/dist/gtk/index.js +46 -0
- package/dist/gtk/index.js.map +1 -0
- package/dist/gtk/widgets.generated.d.ts +103 -0
- package/dist/gtk/widgets.generated.js +205 -0
- package/dist/gtk/widgets.generated.js.map +1 -0
- 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 +11 -4
- package/dist/gtkx/bridge/index.js +47 -6
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/layout-manager.d.ts +2 -1
- package/dist/gtkx/bridge/layout-manager.js +31 -9
- package/dist/gtkx/bridge/layout-manager.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.d.ts +79 -0
- package/dist/gtkx/bridge/svg-node.js +323 -0
- package/dist/gtkx/bridge/svg-node.js.map +1 -0
- package/dist/gtkx/bridge/use-signal.js +19 -9
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.d.ts +2 -0
- package/dist/gtkx/bridge/widgets.generated.js +14 -0
- package/dist/gtkx/bridge/widgets.generated.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/bin.d.ts +2 -0
- package/dist/mcp/bin.js +17 -0
- package/dist/mcp/bin.js.map +1 -0
- package/dist/mcp/data/generated.d.ts +1304 -0
- package/dist/mcp/data/generated.js +1179 -0
- package/dist/mcp/data/generated.js.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/list.d.ts +4 -0
- package/dist/mcp/list.js +56 -0
- package/dist/mcp/list.js.map +1 -0
- package/dist/mcp/resolve.d.ts +55 -0
- package/dist/mcp/resolve.js +151 -0
- package/dist/mcp/resolve.js.map +1 -0
- package/dist/mcp/search.d.ts +13 -0
- package/dist/mcp/search.js +48 -0
- package/dist/mcp/search.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +135 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool.d.ts +21 -0
- package/dist/mcp/tool.js +36 -0
- package/dist/mcp/tool.js.map +1 -0
- package/dist/metro/index.js +10 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/index.d.ts +50 -13
- package/dist/navigation/index.js +183 -148
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/option-warnings.js +9 -1
- package/dist/navigation/option-warnings.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +207 -9
- package/dist/navigation/sidebar.js +294 -10
- 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.d.ts +22 -2
- package/dist/runner/index.js +163 -13
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/assemble.d.ts +29 -0
- package/dist/sea/assemble.js +129 -0
- package/dist/sea/assemble.js.map +1 -0
- package/dist/sea/bundle.d.ts +61 -0
- package/dist/sea/bundle.js +202 -0
- package/dist/sea/bundle.js.map +1 -0
- package/dist/sea/gtkx-config-module.d.ts +18 -0
- package/dist/sea/gtkx-config-module.js +67 -0
- package/dist/sea/gtkx-config-module.js.map +1 -0
- package/dist/sea/native-shim.d.ts +40 -0
- package/dist/sea/native-shim.js +121 -0
- package/dist/sea/native-shim.js.map +1 -0
- package/dist/sea/react-anchor-plugin.d.ts +2 -0
- package/dist/sea/react-anchor-plugin.js +30 -0
- package/dist/sea/react-anchor-plugin.js.map +1 -0
- 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 +3 -0
- package/dist/style/index.js +3 -0
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.d.ts +5 -2
- package/dist/style/split-style.js +33 -5
- 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/transform.d.ts +22 -0
- package/dist/style/transform.js +137 -0
- package/dist/style/transform.js.map +1 -0
- package/dist/style/visual-css.js +40 -3
- package/dist/style/visual-css.js.map +1 -1
- package/dist/svg/geometry.d.ts +19 -0
- package/dist/svg/geometry.js +80 -0
- package/dist/svg/geometry.js.map +1 -0
- package/dist/svg/gradient-geometry.d.ts +15 -0
- package/dist/svg/gradient-geometry.js +15 -0
- package/dist/svg/gradient-geometry.js.map +1 -0
- package/dist/svg/transform.d.ts +22 -0
- package/dist/svg/transform.js +74 -0
- package/dist/svg/transform.js.map +1 -0
- package/dist/svg/view-box.d.ts +34 -0
- package/dist/svg/view-box.js +108 -0
- package/dist/svg/view-box.js.map +1 -0
- package/dist/svg-compat/index.d.ts +1 -0
- package/dist/svg-compat/index.js +16 -0
- package/dist/svg-compat/index.js.map +1 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +14 -0
- package/dist/testing/index.js.map +1 -0
- 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.d.ts +7 -3
- package/dist/vite/index.js +16 -4
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +52 -0
- package/dist/vitest/index.js +58 -0
- package/dist/vitest/index.js.map +1 -0
- package/dist/vitest/setup.d.ts +4 -0
- package/dist/vitest/setup.js +22 -0
- package/dist/vitest/setup.js.map +1 -0
- package/package.json +52 -13
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-registry.js","sourceRoot":"","sources":["../../src/components/app-registry.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, type ComponentType } from \"react\"\nimport {\n AdwApplicationWindow,\n createRoot,\n Gtk,\n GtkApplication,\n GtkApplicationWindow,\n quit,\n} from \"../gtkx/bridge/index\"\nimport { Root } from \"./root\"\n\ntype ComponentProvider = () => ComponentType<Record<string, unknown>>\n\nconst registry = new Map<string, ComponentProvider>()\n\n// The chrome the running app was started with — read by the navigation\n// dev-mode hint (a HeaderBar page under \"system\" chrome renders a doubled\n// titlebar). null until runApplication ran (test harnesses never set it).\nlet activeChrome: \"system\" | \"content\" | null = null\nexport const getActiveChrome = (): \"system\" | \"content\" | null => activeChrome\n\nexport type RunApplicationParams = {\n initialProps?: Record<string, unknown>\n title?: string\n width?: number\n height?: number\n // Window chrome. \"system\" (default): a GtkApplicationWindow with its own\n // titlebar. \"content\": an AdwApplicationWindow with NO window titlebar —\n // the app's content provides HeaderBars (navigation apps: the page\n // HeaderBar becomes the titlebar, with the window controls in it).\n chrome?: \"system\" | \"content\"\n}\n\n// RN parity: system \"reduce animations\" hints never auto-stop RN animations\n// (ActivityIndicator keeps spinning; honoring reduce-motion is an explicit\n// opt-in via AccessibilityInfo). Our Animated runs on GLib timers and already\n// ignores the hint, so GTK-internal animations (GtkSpinner, switch slides)\n// must behave the same — otherwise e.g. GNOME under software rendering\n// reports enable-animations=false through the settings portal and spinners\n// freeze while Animated keeps moving.\nlet animationsForced = false\nconst forceEnableAnimations = (): void => {\n if (animationsForced) {\n return\n }\n const settings = Gtk.Settings.getDefault()\n if (!settings) {\n return\n }\n animationsForced = true\n // Application-set GtkSettings values outrank the desktop backend, so a\n // later portal update cannot flip this back.\n settings.gtkEnableAnimations = true\n}\n\n// chrome \"content\": the app component IS the window content (a navigation\n// container whose pages host their own NestedRoots) — no window-level Yoga\n// root in between: the layout root would not allocate foreign GTK children.\nconst ContentChrome = ({\n App,\n initialProps,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n}) => {\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n return <App {...initialProps} />\n}\n\nconst WindowContent = ({\n App,\n initialProps,\n initialWidth,\n initialHeight,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n initialWidth: number\n initialHeight: number\n}) => {\n // Settings need an initialized display, and writing them mid-render is\n // unsafe — apply after the window mounts.\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n\n // The Root is the window's direct child: RnGtkxLayout reports a zero\n // minimum (the window shrinks freely — no ratchet) and adopts the actual\n // content-area allocation as the layout viewport. No scrollable wrapper\n // means a window can never scroll its own root — RN semantics, scrolling\n // stays opt-in via <ScrollView>.\n return (\n <Root\n width={initialWidth}\n height={initialHeight}\n followAllocation\n >\n <App {...initialProps} />\n </Root>\n )\n}\n\nexport const AppRegistry = {\n registerComponent(appKey: string, provider: ComponentProvider): string {\n registry.set(appKey, provider)\n return appKey\n },\n\n getAppKeys(): string[] {\n return [...registry.keys()]\n },\n\n runApplication(appKey: string, params: RunApplicationParams = {}): void {\n const provider = registry.get(appKey)\n if (!provider) {\n throw new Error(\n `AppRegistry: no component registered for \"${appKey}\". ` +\n `Registered: ${[...registry.keys()].join(\", \") || \"(none)\"}`,\n )\n }\n const App = provider()\n const width = params.width ?? 800\n const height = params.height ?? 600\n\n const contentChrome = params.chrome === \"content\"\n activeChrome = contentChrome ? \"content\" : \"system\"\n const Window = contentChrome ? AdwApplicationWindow : GtkApplicationWindow\n const AppWindow = () => (\n <Window\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n >\n {contentChrome ? (\n <ContentChrome\n App={App}\n initialProps={params.initialProps ?? {}}\n />\n ) : (\n <WindowContent\n App={App}\n initialProps={params.initialProps ?? {}}\n initialWidth={width}\n initialHeight={height}\n />\n )}\n </Window>\n )\n\n createRoot().render(\n <GtkApplication>\n <AppWindow />\n </GtkApplication>,\n )\n },\n}\n"]}
|
|
1
|
+
{"version":3,"file":"app-registry.js","sourceRoot":"","sources":["../../src/components/app-registry.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, type ComponentType, type ReactNode } from \"react\"\nimport {\n AdwApplicationWindow,\n createRoot,\n Gtk,\n GtkApplication,\n GtkApplicationWindow,\n quit,\n} from \"../gtkx/bridge/index\"\nimport { Root } from \"./root\"\n\ntype ComponentProvider = () => ComponentType<Record<string, unknown>>\n\nconst registry = new Map<string, ComponentProvider>()\n\n// The chrome the running app was started with — read by the navigation\n// dev-mode hint (a HeaderBar page under \"system\" chrome renders a doubled\n// titlebar). null until runApplication ran (test harnesses never set it).\nlet activeChrome: \"system\" | \"content\" | null = null\nexport const getActiveChrome = (): \"system\" | \"content\" | null => activeChrome\n\n// One accelerator binding on the GApplication — the shape GtkApplication's\n// own `actionAccels` prop takes, restated locally so this file needs\n// nothing from @gtkx/react/internal (the \"internal\" subpath is where the\n// real type lives, but nothing else in this package reaches for it).\nexport type ActionAccel = {\n detailedActionName: string\n accels: string[]\n}\n\nexport type RunApplicationParams = {\n initialProps?: Record<string, unknown>\n title?: string\n width?: number\n height?: number\n // Window chrome. \"system\" (default): a GtkApplicationWindow with its own\n // titlebar. \"content\": an AdwApplicationWindow with NO window titlebar —\n // the app's content provides HeaderBars (navigation apps: the page\n // HeaderBar becomes the titlebar, with the window controls in it).\n chrome?: \"system\" | \"content\"\n /**\n * GSimpleAction elements registered on the GApplication itself\n * (\"app.<name>\" — reachable from anywhere, including a Gio.Notification's\n * action buttons, which can only ever target an app-level action).\n *\n * @deprecated Render `<ApplicationActions>` (react-native-gtkx/gtk) inside\n * the app instead. Options render as SIBLINGS of the app tree, so no\n * context of the app's can reach them and the set is fixed for the\n * process's lifetime. Still supported; see docs/api.md.\n */\n applicationActions?: ReactNode\n // Keyboard accelerators bound to actions, at the application level —\n // GtkApplication's own actionAccels prop, unchanged. NOT deprecated with\n // the three ReactNode options below: this is a flat name→keys table with\n // no children and nothing to read from context, and it is deliberately\n // process-wide (an accelerator for an action that is not currently\n // registered simply does nothing). A shortcut that should come and go\n // with a screen is a GtkShortcutController in <WindowControllers>.\n actionAccels?: ActionAccel[]\n /**\n * GSimpleAction elements registered on the window (\"win.<name>\" — what a\n * HeaderBar button's actionName or a GMenu item usually targets).\n *\n * @deprecated Render `<WindowActions>` (react-native-gtkx/gtk) inside the\n * app instead — it can read the app's context and registers/unregisters\n * with the component that declares it. Still supported; see docs/api.md.\n */\n windowActions?: ReactNode\n /**\n * Event controllers attached to the window itself — a\n * GtkShortcutController scoped to the whole window is the common case.\n *\n * @deprecated Render `<WindowControllers>` (react-native-gtkx/gtk) inside\n * the app instead, for the same reasons as `windowActions`. Still\n * supported; see docs/api.md.\n */\n windowControllers?: ReactNode\n // AdwBreakpoint elements, evaluated against the window's own allocated\n // size. Only meaningful under chrome: \"content\" (AdwApplicationWindow) —\n // GtkApplicationWindow has no such concept, so this is ignored (with a\n // dev warning) under the default \"system\" chrome.\n breakpoints?: ReactNode\n}\n\n// RN parity: system \"reduce animations\" hints never auto-stop RN animations\n// (ActivityIndicator keeps spinning; honoring reduce-motion is an explicit\n// opt-in via AccessibilityInfo). Our Animated runs on GLib timers and already\n// ignores the hint, so GTK-internal animations (GtkSpinner, switch slides)\n// must behave the same — otherwise e.g. GNOME under software rendering\n// reports enable-animations=false through the settings portal and spinners\n// freeze while Animated keeps moving.\nlet animationsForced = false\nconst forceEnableAnimations = (): void => {\n if (animationsForced) {\n return\n }\n const settings = Gtk.Settings.getDefault()\n if (!settings) {\n return\n }\n animationsForced = true\n // Application-set GtkSettings values outrank the desktop backend, so a\n // later portal update cannot flip this back.\n settings.gtkEnableAnimations = true\n}\n\n// chrome \"content\": the app component IS the window content (a navigation\n// container whose pages host their own NestedRoots) — no window-level Yoga\n// root in between: the layout root would not allocate foreign GTK children.\nconst ContentChrome = ({\n App,\n initialProps,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n}) => {\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n return <App {...initialProps} />\n}\n\nconst WindowContent = ({\n App,\n initialProps,\n initialWidth,\n initialHeight,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n initialWidth: number\n initialHeight: number\n}) => {\n // Settings need an initialized display, and writing them mid-render is\n // unsafe — apply after the window mounts.\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n\n // The Root is the window's direct child: RnGtkxLayout reports a zero\n // minimum (the window shrinks freely — no ratchet) and adopts the actual\n // content-area allocation as the layout viewport. No scrollable wrapper\n // means a window can never scroll its own root — RN semantics, scrolling\n // stays opt-in via <ScrollView>.\n return (\n <Root\n width={initialWidth}\n height={initialHeight}\n followAllocation\n >\n <App {...initialProps} />\n </Root>\n )\n}\n\nexport const AppRegistry = {\n registerComponent(appKey: string, provider: ComponentProvider): string {\n registry.set(appKey, provider)\n return appKey\n },\n\n getAppKeys(): string[] {\n return [...registry.keys()]\n },\n\n runApplication(appKey: string, params: RunApplicationParams = {}): void {\n const provider = registry.get(appKey)\n if (!provider) {\n throw new Error(\n `AppRegistry: no component registered for \"${appKey}\". ` +\n `Registered: ${[...registry.keys()].join(\", \") || \"(none)\"}`,\n )\n }\n const App = provider()\n const width = params.width ?? 800\n const height = params.height ?? 600\n\n const contentChrome = params.chrome === \"content\"\n activeChrome = contentChrome ? \"content\" : \"system\"\n\n if (\n process.env.NODE_ENV !== \"production\" &&\n params.breakpoints &&\n !contentChrome\n ) {\n console.warn(\n '[react-native-gtkx] breakpoints was passed to runApplication without chrome: \"content\" — GtkApplicationWindow (the \"system\" chrome default) has no breakpoints concept, so it is ignored.',\n )\n }\n\n const content = contentChrome ? (\n <ContentChrome\n App={App}\n initialProps={params.initialProps ?? {}}\n />\n ) : (\n <WindowContent\n App={App}\n initialProps={params.initialProps ?? {}}\n initialWidth={width}\n initialHeight={height}\n />\n )\n\n const AppWindow = () =>\n contentChrome ? (\n <AdwApplicationWindow\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n actions={params.windowActions}\n controllers={params.windowControllers}\n breakpoints={params.breakpoints}\n >\n {content}\n </AdwApplicationWindow>\n ) : (\n <GtkApplicationWindow\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n actions={params.windowActions}\n controllers={params.windowControllers}\n >\n {content}\n </GtkApplicationWindow>\n )\n\n createRoot().render(\n <GtkApplication\n actions={params.applicationActions}\n actionAccels={params.actionAccels}\n >\n <AppWindow />\n </GtkApplication>,\n )\n },\n}\n"]}
|
|
@@ -7,4 +7,11 @@ export type HostNode = {
|
|
|
7
7
|
widgetRef: RefObject<Gtk.Box | null>;
|
|
8
8
|
};
|
|
9
9
|
export declare const HostNodeContext: React.Context<HostNode | null>;
|
|
10
|
+
export type SlotLocation = {
|
|
11
|
+
/** The widget this content was handed to, e.g. "AdwBottomSheet". */
|
|
12
|
+
widget: string;
|
|
13
|
+
/** The slot property, e.g. "sheet" — or null for an ordinary child. */
|
|
14
|
+
slot: string | null;
|
|
15
|
+
};
|
|
16
|
+
export declare const SlotContext: React.Context<SlotLocation | null>;
|
|
10
17
|
export declare const useHostNode: () => HostNode;
|
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
import { createContext, useContext } from "react";
|
|
2
2
|
export const HostNodeContext = createContext(null);
|
|
3
|
+
export const SlotContext = createContext(null);
|
|
4
|
+
const slotError = ({ widget, slot }) => {
|
|
5
|
+
// The two shapes differ only in how you write the wrapper, so the message
|
|
6
|
+
// shows the caller their own syntax rather than a generic one.
|
|
7
|
+
const where = slot
|
|
8
|
+
? `${widget}'s \`${slot}\` slot`
|
|
9
|
+
: `${widget}, as its child`;
|
|
10
|
+
const wrap = (root) => slot
|
|
11
|
+
? ` ${slot}={<${root}>…</${root}>}`
|
|
12
|
+
: ` <${widget}>…</${widget}> → <${widget}><${root}>…</${root}></${widget}>`;
|
|
13
|
+
return (`react-native-gtkx: React Native content was put in ${where} without a layout root.\n` +
|
|
14
|
+
`A GTK widget hands out a rectangle, and React Native needs a root to lay out inside it. Wrap the content:\n` +
|
|
15
|
+
`${wrap("SlotContent")} — fill the area (a page body, a pane, a content area)\n` +
|
|
16
|
+
`${wrap("IntrinsicContent")} — size the area to the content (a bar, a header slot, a row)\n` +
|
|
17
|
+
// The subpath is named without quoting it as a specifier: the metro-preset
|
|
18
|
+
// test scans src for bare imports with a regex, and an import-shaped
|
|
19
|
+
// sentence in a string literal reads to it exactly like a real one.
|
|
20
|
+
`Both are exported by react-native-gtkx/common. GTK widgets need neither — they are what a widget takes natively.`);
|
|
21
|
+
};
|
|
3
22
|
export const useHostNode = () => {
|
|
4
23
|
const host = useContext(HostNodeContext);
|
|
24
|
+
// One extra context read per layout child. Deliberate: it is what turns the
|
|
25
|
+
// single most confusing failure on this platform — content silently laid
|
|
26
|
+
// out against the wrong rectangle — into a sentence naming where it landed.
|
|
27
|
+
const slot = useContext(SlotContext);
|
|
5
28
|
if (host === null) {
|
|
6
|
-
throw new Error(
|
|
29
|
+
throw new Error(slot
|
|
30
|
+
? slotError(slot)
|
|
31
|
+
: "react-native-gtkx components must be rendered inside AppRegistry.runApplication() or a <Root>");
|
|
7
32
|
}
|
|
8
33
|
return host;
|
|
9
34
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-node.js","sourceRoot":"","sources":["../../src/components/host-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAkB,MAAM,OAAO,CAAA;AAajE,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAkB,IAAI,CAAC,CAAA;AAEnE,MAAM,CAAC,MAAM,WAAW,GAAG,GAAa,EAAE;IACxC,MAAM,IAAI,GAAG,UAAU,CAAC,eAAe,CAAC,CAAA;IACxC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,+FAA+F,
|
|
1
|
+
{"version":3,"file":"host-node.js","sourceRoot":"","sources":["../../src/components/host-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAkB,MAAM,OAAO,CAAA;AAajE,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAkB,IAAI,CAAC,CAAA;AAenE,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAsB,IAAI,CAAC,CAAA;AAEnE,MAAM,SAAS,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAgB,EAAU,EAAE;IAC3D,0EAA0E;IAC1E,+DAA+D;IAC/D,MAAM,KAAK,GAAG,IAAI;QAChB,CAAC,CAAC,GAAG,MAAM,QAAQ,IAAI,SAAS;QAChC,CAAC,CAAC,GAAG,MAAM,gBAAgB,CAAA;IAC7B,MAAM,IAAI,GAAG,CAAC,IAAY,EAAU,EAAE,CACpC,IAAI;QACF,CAAC,CAAC,KAAK,IAAI,MAAM,IAAI,OAAO,IAAI,IAAI;QACpC,CAAC,CAAC,MAAM,MAAM,OAAO,MAAM,QAAQ,MAAM,KAAK,IAAI,OAAO,IAAI,MAAM,MAAM,GAAG,CAAA;IAChF,OAAO,CACL,sDAAsD,KAAK,2BAA2B;QACtF,6GAA6G;QAC7G,GAAG,IAAI,CAAC,aAAa,CAAC,0DAA0D;QAChF,GAAG,IAAI,CAAC,kBAAkB,CAAC,iEAAiE;QAC5F,2EAA2E;QAC3E,qEAAqE;QACrE,oEAAoE;QACpE,kHAAkH,CACnH,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,GAAa,EAAE;IACxC,MAAM,IAAI,GAAG,UAAU,CAAC,eAAe,CAAC,CAAA;IACxC,4EAA4E;IAC5E,yEAAyE;IACzE,4EAA4E;IAC5E,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACpC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,IAAI;YACF,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC;YACjB,CAAC,CAAC,+FAA+F,CACpG,CAAA;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA","sourcesContent":["import { createContext, useContext, type RefObject } from \"react\"\nimport type { LayoutEngine, LayoutNode } from \"../layout/index\"\nimport type { Gtk } from \"../gtkx/bridge/index\"\n\n// One per mounted container (Root or View): children register their layout\n// nodes here and commit rects into the store read by the parent's\n// RnGtkxLayout allocate().\nexport type HostNode = {\n engine: LayoutEngine\n node: LayoutNode\n widgetRef: RefObject<Gtk.Box | null>\n}\n\nexport const HostNodeContext = createContext<HostNode | null>(null)\n\n// Which widget content area the current subtree was handed to, when it was\n// handed to one. Set by wrapReactNative for every element-valued prop it\n// forwards AND for the widget's children (see ../common/widget), and read\n// ONLY to build the error below — the boundary clears the layout root, so\n// React Native content that lands inside a widget without bringing its own\n// root has to be told where it is, and what to wrap it in.\nexport type SlotLocation = {\n /** The widget this content was handed to, e.g. \"AdwBottomSheet\". */\n widget: string\n /** The slot property, e.g. \"sheet\" — or null for an ordinary child. */\n slot: string | null\n}\n\nexport const SlotContext = createContext<SlotLocation | null>(null)\n\nconst slotError = ({ widget, slot }: SlotLocation): string => {\n // The two shapes differ only in how you write the wrapper, so the message\n // shows the caller their own syntax rather than a generic one.\n const where = slot\n ? `${widget}'s \\`${slot}\\` slot`\n : `${widget}, as its child`\n const wrap = (root: string): string =>\n slot\n ? ` ${slot}={<${root}>…</${root}>}`\n : ` <${widget}>…</${widget}> → <${widget}><${root}>…</${root}></${widget}>`\n return (\n `react-native-gtkx: React Native content was put in ${where} without a layout root.\\n` +\n `A GTK widget hands out a rectangle, and React Native needs a root to lay out inside it. Wrap the content:\\n` +\n `${wrap(\"SlotContent\")} — fill the area (a page body, a pane, a content area)\\n` +\n `${wrap(\"IntrinsicContent\")} — size the area to the content (a bar, a header slot, a row)\\n` +\n // The subpath is named without quoting it as a specifier: the metro-preset\n // test scans src for bare imports with a regex, and an import-shaped\n // sentence in a string literal reads to it exactly like a real one.\n `Both are exported by react-native-gtkx/common. GTK widgets need neither — they are what a widget takes natively.`\n )\n}\n\nexport const useHostNode = (): HostNode => {\n const host = useContext(HostNodeContext)\n // One extra context read per layout child. Deliberate: it is what turns the\n // single most confusing failure on this platform — content silently laid\n // out against the wrong rectangle — into a sentence naming where it landed.\n const slot = useContext(SlotContext)\n if (host === null) {\n throw new Error(\n slot\n ? slotError(slot)\n : \"react-native-gtkx components must be rendered inside AppRegistry.runApplication() or a <Root>\",\n )\n }\n return host\n}\n"]}
|
|
@@ -4,11 +4,15 @@ export { AppRegistry, type RunApplicationParams } from "./app-registry";
|
|
|
4
4
|
export { FlatList, SectionList, type FlatListProps, type ListRenderItemInfo, type SectionListData, type SectionListProps, type ViewabilityConfig, type ViewToken, } from "./flat-list";
|
|
5
5
|
export { Image, type ImageProps, type ImageSource } from "./image";
|
|
6
6
|
export { Modal, type ModalProps } from "./modal";
|
|
7
|
-
export { Pressable, TouchableOpacity, type PressableProps, type PressableStateCallbackType, type PressEvent, type TouchableOpacityProps, } from "./pressable";
|
|
7
|
+
export { Pressable, TouchableOpacity, type NativeTouch, type PressableProps, type PressableStateCallbackType, type PressEvent, type TouchableOpacityProps, } from "./pressable";
|
|
8
8
|
export { IntrinsicRoot, NestedRoot, Root, type IntrinsicRootProps, type NestedRootProps, type RootProps, } from "./root";
|
|
9
9
|
export { ScrollView, type ScrollEvent, type ScrollViewHandle, type ScrollViewProps, } from "./scroll-view";
|
|
10
10
|
export { Switch, type SwitchProps } from "./switch";
|
|
11
11
|
export { Text, type TextProps } from "./text";
|
|
12
12
|
export { TextInput, type TextInputProps } from "./text-input";
|
|
13
|
-
export { SafeAreaView, StatusBar, View, type ViewProps } from "./view";
|
|
13
|
+
export { SafeAreaView, StatusBar, View, type ViewHandle, type ViewProps, } from "./view";
|
|
14
|
+
export type { MeasureHandle, MeasureInWindowOnSuccessCallback, MeasureLayoutOnSuccessCallback, MeasureOnSuccessCallback, } from "./measure";
|
|
14
15
|
export { type LayoutEvent } from "./use-layout-child";
|
|
16
|
+
export { default as PanResponder } from "../vendor/react-native/pan-responder";
|
|
17
|
+
export type { PanResponderCallbacks, PanResponderGestureState, PanResponderInstance, } from "../vendor/react-native/pan-responder";
|
|
18
|
+
export type { GestureResponderEvent, ResponderProps, TouchHistory, TouchRecord, } from "../responder/types";
|
package/dist/components/index.js
CHANGED
|
@@ -10,5 +10,6 @@ export { ScrollView, } from "./scroll-view";
|
|
|
10
10
|
export { Switch } from "./switch";
|
|
11
11
|
export { Text } from "./text";
|
|
12
12
|
export { TextInput } from "./text-input";
|
|
13
|
-
export { SafeAreaView, StatusBar, View } from "./view";
|
|
13
|
+
export { SafeAreaView, StatusBar, View, } from "./view";
|
|
14
|
+
export { default as PanResponder } from "../vendor/react-native/pan-responder";
|
|
14
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,QAAQ,EACR,MAAM,GAGP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,WAAW,EAA6B,MAAM,gBAAgB,CAAA;AACvE,OAAO,EACL,QAAQ,EACR,WAAW,GAOZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,EAAqC,MAAM,SAAS,CAAA;AAClE,OAAO,EAAE,KAAK,EAAmB,MAAM,SAAS,CAAA;AAChD,OAAO,EACL,SAAS,EACT,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,QAAQ,EACR,MAAM,GAGP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,WAAW,EAA6B,MAAM,gBAAgB,CAAA;AACvE,OAAO,EACL,QAAQ,EACR,WAAW,GAOZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,EAAqC,MAAM,SAAS,CAAA;AAClE,OAAO,EAAE,KAAK,EAAmB,MAAM,SAAS,CAAA;AAChD,OAAO,EACL,SAAS,EACT,gBAAgB,GAMjB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,aAAa,EACb,UAAU,EACV,IAAI,GAIL,MAAM,QAAQ,CAAA;AACf,OAAO,EACL,UAAU,GAIX,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAuB,MAAM,cAAc,CAAA;AAC7D,OAAO,EACL,YAAY,EACZ,SAAS,EACT,IAAI,GAGL,MAAM,QAAQ,CAAA;AAQf,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sCAAsC,CAAA","sourcesContent":["export {\n ActivityIndicator,\n type ActivityIndicatorProps,\n} from \"./activity-indicator\"\nexport {\n Animated,\n Easing,\n type AnimatedViewProps,\n type AnimatedViewStyle,\n} from \"./animated\"\nexport { AppRegistry, type RunApplicationParams } from \"./app-registry\"\nexport {\n FlatList,\n SectionList,\n type FlatListProps,\n type ListRenderItemInfo,\n type SectionListData,\n type SectionListProps,\n type ViewabilityConfig,\n type ViewToken,\n} from \"./flat-list\"\nexport { Image, type ImageProps, type ImageSource } from \"./image\"\nexport { Modal, type ModalProps } from \"./modal\"\nexport {\n Pressable,\n TouchableOpacity,\n type NativeTouch,\n type PressableProps,\n type PressableStateCallbackType,\n type PressEvent,\n type TouchableOpacityProps,\n} from \"./pressable\"\nexport {\n IntrinsicRoot,\n NestedRoot,\n Root,\n type IntrinsicRootProps,\n type NestedRootProps,\n type RootProps,\n} from \"./root\"\nexport {\n ScrollView,\n type ScrollEvent,\n type ScrollViewHandle,\n type ScrollViewProps,\n} from \"./scroll-view\"\nexport { Switch, type SwitchProps } from \"./switch\"\nexport { Text, type TextProps } from \"./text\"\nexport { TextInput, type TextInputProps } from \"./text-input\"\nexport {\n SafeAreaView,\n StatusBar,\n View,\n type ViewHandle,\n type ViewProps,\n} from \"./view\"\nexport type {\n MeasureHandle,\n MeasureInWindowOnSuccessCallback,\n MeasureLayoutOnSuccessCallback,\n MeasureOnSuccessCallback,\n} from \"./measure\"\nexport { type LayoutEvent } from \"./use-layout-child\"\nexport { default as PanResponder } from \"../vendor/react-native/pan-responder\"\nexport type {\n PanResponderCallbacks,\n PanResponderGestureState,\n PanResponderInstance,\n} from \"../vendor/react-native/pan-responder\"\nexport type {\n GestureResponderEvent,\n ResponderProps,\n TouchHistory,\n TouchRecord,\n} from \"../responder/types\"\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RefObject } from "react";
|
|
2
|
+
import type { LayoutNodeApi } from "../contracts";
|
|
3
|
+
import { type Gtk } from "../gtkx/bridge/index";
|
|
4
|
+
export type MeasureOnSuccessCallback = (x: number, y: number, width: number, height: number, pageX: number, pageY: number) => void;
|
|
5
|
+
export type MeasureInWindowOnSuccessCallback = (x: number, y: number, width: number, height: number) => void;
|
|
6
|
+
export type MeasureLayoutOnSuccessCallback = (left: number, top: number, width: number, height: number) => void;
|
|
7
|
+
/** The measurement half of a component ref, matching RN's method set. */
|
|
8
|
+
export type MeasureHandle = {
|
|
9
|
+
measure(callback: MeasureOnSuccessCallback): void;
|
|
10
|
+
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void;
|
|
11
|
+
measureLayout(relativeTo: MeasureHandle, onSuccess: MeasureLayoutOnSuccessCallback, onFail?: () => void): void;
|
|
12
|
+
};
|
|
13
|
+
export declare const createMeasureHandle: (widgetRef: RefObject<Gtk.Widget | null>, node: LayoutNodeApi) => MeasureHandle;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { computePointIn, computePointInWindow, } from "../gtkx/bridge/index";
|
|
2
|
+
// measureLayout needs the OTHER view's widget, and the public handle type
|
|
3
|
+
// deliberately does not carry one — a portable API should not hand out a
|
|
4
|
+
// Gtk.Widget. The lookup lives here instead, keyed by the handle identity.
|
|
5
|
+
const widgetOf = new WeakMap();
|
|
6
|
+
export const createMeasureHandle = (widgetRef, node) => {
|
|
7
|
+
const handle = {
|
|
8
|
+
measure(callback) {
|
|
9
|
+
const widget = widgetRef.current;
|
|
10
|
+
const rect = node.getRect();
|
|
11
|
+
if (!widget || !rect) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const page = computePointInWindow(widget, 0, 0);
|
|
15
|
+
if (!page) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
callback(rect.x, rect.y, rect.width, rect.height, page.x, page.y);
|
|
19
|
+
},
|
|
20
|
+
measureInWindow(callback) {
|
|
21
|
+
const widget = widgetRef.current;
|
|
22
|
+
const rect = node.getRect();
|
|
23
|
+
if (!widget || !rect) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const page = computePointInWindow(widget, 0, 0);
|
|
27
|
+
if (!page) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
callback(page.x, page.y, rect.width, rect.height);
|
|
31
|
+
},
|
|
32
|
+
measureLayout(relativeTo, onSuccess, onFail) {
|
|
33
|
+
const widget = widgetRef.current;
|
|
34
|
+
const other = widgetOf.get(relativeTo)?.() ?? null;
|
|
35
|
+
const rect = node.getRect();
|
|
36
|
+
if (!widget || !other || !rect) {
|
|
37
|
+
onFail?.();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const origin = computePointIn(widget, other, 0, 0);
|
|
41
|
+
if (!origin) {
|
|
42
|
+
onFail?.();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
onSuccess(origin.x, origin.y, rect.width, rect.height);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
widgetOf.set(handle, () => widgetRef.current);
|
|
49
|
+
return handle;
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=measure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"measure.js","sourceRoot":"","sources":["../../src/components/measure.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,cAAc,EACd,oBAAoB,GAErB,MAAM,sBAAsB,CAAA;AAoC7B,0EAA0E;AAC1E,yEAAyE;AACzE,2EAA2E;AAC3E,MAAM,QAAQ,GAAG,IAAI,OAAO,EAA0C,CAAA;AAEtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,SAAuC,EACvC,IAAmB,EACJ,EAAE;IACjB,MAAM,MAAM,GAAkB;QAC5B,OAAO,CAAC,QAAQ;YACd,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAA;YAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;YAC3B,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBACrB,OAAM;YACR,CAAC;YACD,MAAM,IAAI,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAM;YACR,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACnE,CAAC;QAED,eAAe,CAAC,QAAQ;YACtB,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAA;YAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;YAC3B,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBACrB,OAAM;YACR,CAAC;YACD,MAAM,IAAI,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAM;YACR,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QACnD,CAAC;QAED,aAAa,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM;YACzC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAA;YAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,IAAI,CAAA;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;YAC3B,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC/B,MAAM,EAAE,EAAE,CAAA;gBACV,OAAM;YACR,CAAC;YACD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YAClD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,EAAE,EAAE,CAAA;gBACV,OAAM;YACR,CAAC;YACD,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QACxD,CAAC;KACF,CAAA;IAED,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IAC7C,OAAO,MAAM,CAAA;AACf,CAAC,CAAA","sourcesContent":["// RN's imperative geometry API (measure / measureInWindow / measureLayout),\n// shared by every component that exposes a ref.\n//\n// The size and the parent-relative position come from the Yoga rect the\n// component's layout node already holds — that is the layout truth, and it\n// is what RN reports (a rotated view still measures its own box, not its\n// axis-aligned bounding box). Only the translation into another coordinate\n// space goes through GTK, where compute_point walks the transform chain and\n// therefore already accounts for scroll offsets.\n//\n// RN's contract on failure is to not call back rather than to invent a\n// number: a view whose layout has not been committed yet (before the first\n// onLayout) or that is not in a window has nothing true to report.\nimport type { RefObject } from \"react\"\nimport type { LayoutNodeApi } from \"../contracts\"\nimport {\n computePointIn,\n computePointInWindow,\n type Gtk,\n} from \"../gtkx/bridge/index\"\n\nexport type MeasureOnSuccessCallback = (\n x: number,\n y: number,\n width: number,\n height: number,\n pageX: number,\n pageY: number,\n) => void\n\nexport type MeasureInWindowOnSuccessCallback = (\n x: number,\n y: number,\n width: number,\n height: number,\n) => void\n\nexport type MeasureLayoutOnSuccessCallback = (\n left: number,\n top: number,\n width: number,\n height: number,\n) => void\n\n/** The measurement half of a component ref, matching RN's method set. */\nexport type MeasureHandle = {\n measure(callback: MeasureOnSuccessCallback): void\n measureInWindow(callback: MeasureInWindowOnSuccessCallback): void\n measureLayout(\n relativeTo: MeasureHandle,\n onSuccess: MeasureLayoutOnSuccessCallback,\n onFail?: () => void,\n ): void\n}\n\n// measureLayout needs the OTHER view's widget, and the public handle type\n// deliberately does not carry one — a portable API should not hand out a\n// Gtk.Widget. The lookup lives here instead, keyed by the handle identity.\nconst widgetOf = new WeakMap<MeasureHandle, () => Gtk.Widget | null>()\n\nexport const createMeasureHandle = (\n widgetRef: RefObject<Gtk.Widget | null>,\n node: LayoutNodeApi,\n): MeasureHandle => {\n const handle: MeasureHandle = {\n measure(callback) {\n const widget = widgetRef.current\n const rect = node.getRect()\n if (!widget || !rect) {\n return\n }\n const page = computePointInWindow(widget, 0, 0)\n if (!page) {\n return\n }\n callback(rect.x, rect.y, rect.width, rect.height, page.x, page.y)\n },\n\n measureInWindow(callback) {\n const widget = widgetRef.current\n const rect = node.getRect()\n if (!widget || !rect) {\n return\n }\n const page = computePointInWindow(widget, 0, 0)\n if (!page) {\n return\n }\n callback(page.x, page.y, rect.width, rect.height)\n },\n\n measureLayout(relativeTo, onSuccess, onFail) {\n const widget = widgetRef.current\n const other = widgetOf.get(relativeTo)?.() ?? null\n const rect = node.getRect()\n if (!widget || !other || !rect) {\n onFail?.()\n return\n }\n const origin = computePointIn(widget, other, 0, 0)\n if (!origin) {\n onFail?.()\n return\n }\n onSuccess(origin.x, origin.y, rect.width, rect.height)\n },\n }\n\n widgetOf.set(handle, () => widgetRef.current)\n return handle\n}\n"]}
|
|
@@ -9,4 +9,4 @@ export type ModalProps = {
|
|
|
9
9
|
height?: number;
|
|
10
10
|
children?: ReactNode;
|
|
11
11
|
};
|
|
12
|
-
export declare const Modal: ({ visible, onRequestClose, title, width, height, children, }: ModalProps) =>
|
|
12
|
+
export declare const Modal: ({ visible, onRequestClose, title, width, height, children, }: ModalProps) => React.ReactPortal | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Gtk } from "../gtkx/bridge/index";
|
|
2
|
+
/** One touch point, in RN's shape. */
|
|
3
|
+
export type NativeTouch = {
|
|
4
|
+
identifier: number;
|
|
5
|
+
target: number | null;
|
|
6
|
+
locationX: number;
|
|
7
|
+
locationY: number;
|
|
8
|
+
pageX: number;
|
|
9
|
+
pageY: number;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
force: number;
|
|
12
|
+
};
|
|
13
|
+
export type PressEvent = {
|
|
14
|
+
nativeEvent: NativeTouch & {
|
|
15
|
+
touches: readonly NativeTouch[];
|
|
16
|
+
changedTouches: readonly NativeTouch[];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export declare const touchTimestamp: () => number;
|
|
20
|
+
/** A stable numeric id for a widget, standing in for RN's node handle. */
|
|
21
|
+
export declare const targetIdOf: (widget: object | null) => number | null;
|
|
22
|
+
/**
|
|
23
|
+
* Builds a touch from coordinates in the target widget's own space — which
|
|
24
|
+
* is what every GTK gesture reports.
|
|
25
|
+
*/
|
|
26
|
+
export declare const createTouch: (widget: Gtk.Widget | null, locationX: number, locationY: number, identifier?: number) => NativeTouch;
|
|
27
|
+
/** Wraps a single touch as a complete RN press event. */
|
|
28
|
+
export declare const createPressEvent: (touch: NativeTouch) => PressEvent;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { computePointInWindow } from "../gtkx/bridge/index";
|
|
2
|
+
// performance.now() is monotonic and sub-millisecond — see the note above.
|
|
3
|
+
export const touchTimestamp = () => performance.now();
|
|
4
|
+
let nextTargetId = 1;
|
|
5
|
+
const targetIds = new WeakMap();
|
|
6
|
+
/** A stable numeric id for a widget, standing in for RN's node handle. */
|
|
7
|
+
export const targetIdOf = (widget) => {
|
|
8
|
+
if (!widget) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const existing = targetIds.get(widget);
|
|
12
|
+
if (existing !== undefined) {
|
|
13
|
+
return existing;
|
|
14
|
+
}
|
|
15
|
+
const id = nextTargetId;
|
|
16
|
+
nextTargetId += 1;
|
|
17
|
+
targetIds.set(widget, id);
|
|
18
|
+
return id;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Builds a touch from coordinates in the target widget's own space — which
|
|
22
|
+
* is what every GTK gesture reports.
|
|
23
|
+
*/
|
|
24
|
+
export const createTouch = (widget, locationX, locationY, identifier = 0) => {
|
|
25
|
+
const page = widget
|
|
26
|
+
? computePointInWindow(widget, locationX, locationY)
|
|
27
|
+
: null;
|
|
28
|
+
return {
|
|
29
|
+
identifier,
|
|
30
|
+
target: targetIdOf(widget),
|
|
31
|
+
locationX,
|
|
32
|
+
locationY,
|
|
33
|
+
// A widget outside a window has no page space; falling back to the
|
|
34
|
+
// local point keeps the fields numeric without pretending to a
|
|
35
|
+
// translation that does not exist.
|
|
36
|
+
pageX: page?.x ?? locationX,
|
|
37
|
+
pageY: page?.y ?? locationY,
|
|
38
|
+
timestamp: touchTimestamp(),
|
|
39
|
+
force: 0,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/** Wraps a single touch as a complete RN press event. */
|
|
43
|
+
export const createPressEvent = (touch) => ({
|
|
44
|
+
nativeEvent: { ...touch, touches: [touch], changedTouches: [touch] },
|
|
45
|
+
});
|
|
46
|
+
//# sourceMappingURL=press-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"press-event.js","sourceRoot":"","sources":["../../src/components/press-event.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AA+B3D,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,GAAW,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAA;AAE7D,IAAI,YAAY,GAAG,CAAC,CAAA;AACpB,MAAM,SAAS,GAAG,IAAI,OAAO,EAAkB,CAAA;AAE/C,0EAA0E;AAC1E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,MAAqB,EAAiB,EAAE;IACjE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACtC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,MAAM,EAAE,GAAG,YAAY,CAAA;IACvB,YAAY,IAAI,CAAC,CAAA;IACjB,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACzB,OAAO,EAAE,CAAA;AACX,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,MAAyB,EACzB,SAAiB,EACjB,SAAiB,EACjB,UAAU,GAAG,CAAC,EACD,EAAE;IACf,MAAM,IAAI,GAAG,MAAM;QACjB,CAAC,CAAC,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC;QACpD,CAAC,CAAC,IAAI,CAAA;IACR,OAAO;QACL,UAAU;QACV,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC;QAC1B,SAAS;QACT,SAAS;QACT,mEAAmE;QACnE,+DAA+D;QAC/D,mCAAmC;QACnC,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,SAAS;QAC3B,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,SAAS;QAC3B,SAAS,EAAE,cAAc,EAAE;QAC3B,KAAK,EAAE,CAAC;KACT,CAAA;AACH,CAAC,CAAA;AAED,yDAAyD;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAkB,EAAc,EAAE,CAAC,CAAC;IACnE,WAAW,EAAE,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC,KAAK,CAAC,EAAE;CACrE,CAAC,CAAA","sourcesContent":["// RN's touch payload. One shape, shared by Pressable today and by the touch\n// and responder props when they land — in RN a single native touch stream\n// feeds both, and splitting it here would guarantee the two drift.\n//\n// Two details that look cosmetic and are not:\n//\n// - `timestamp` must be monotonic and finer than a millisecond tick.\n// PanResponder and TouchHistoryMath compute velocity by differencing it,\n// so a coarse clock makes consecutive frames compare equal and silently\n// reports ZERO movement. That is the standing diagnosis for\n// react-native-windows' New-Architecture PanResponder bug (#14119, open\n// since 2024-11): Date.now() granularity, not a logic error.\n// - `pageX/pageY` and `locationX/locationY` are different spaces (window vs\n// target-relative) and code in the wild reads both. Reporting one twice\n// is the kind of bug that only shows up once something is nested.\nimport type { Gtk } from \"../gtkx/bridge/index\"\nimport { computePointInWindow } from \"../gtkx/bridge/index\"\n\n/** One touch point, in RN's shape. */\nexport type NativeTouch = {\n // A desktop pointer is a single fabricated touch: RN's own event plugin\n // has always had a mouse path, and react-native-web documents converting\n // mouse events into one emulated touch. Real multitouch will hand out the\n // GdkEventSequence's own identity here.\n identifier: number\n // Stable per-widget id; RN's is a React node handle, which we have no\n // equivalent of. Null before the widget exists.\n target: number | null\n // Relative to the target view.\n locationX: number\n locationY: number\n // Relative to the window (\"page\" in RN's vocabulary).\n pageX: number\n pageY: number\n timestamp: number\n // No pressure-sensitive input plumbed yet; RN reports 0 for devices\n // without it, which is exactly what a mouse is.\n force: number\n}\n\nexport type PressEvent = {\n nativeEvent: NativeTouch & {\n touches: readonly NativeTouch[]\n changedTouches: readonly NativeTouch[]\n }\n}\n\n// performance.now() is monotonic and sub-millisecond — see the note above.\nexport const touchTimestamp = (): number => performance.now()\n\nlet nextTargetId = 1\nconst targetIds = new WeakMap<object, number>()\n\n/** A stable numeric id for a widget, standing in for RN's node handle. */\nexport const targetIdOf = (widget: object | null): number | null => {\n if (!widget) {\n return null\n }\n const existing = targetIds.get(widget)\n if (existing !== undefined) {\n return existing\n }\n const id = nextTargetId\n nextTargetId += 1\n targetIds.set(widget, id)\n return id\n}\n\n/**\n * Builds a touch from coordinates in the target widget's own space — which\n * is what every GTK gesture reports.\n */\nexport const createTouch = (\n widget: Gtk.Widget | null,\n locationX: number,\n locationY: number,\n identifier = 0,\n): NativeTouch => {\n const page = widget\n ? computePointInWindow(widget, locationX, locationY)\n : null\n return {\n identifier,\n target: targetIdOf(widget),\n locationX,\n locationY,\n // A widget outside a window has no page space; falling back to the\n // local point keeps the fields numeric without pretending to a\n // translation that does not exist.\n pageX: page?.x ?? locationX,\n pageY: page?.y ?? locationY,\n timestamp: touchTimestamp(),\n force: 0,\n }\n}\n\n/** Wraps a single touch as a complete RN press event. */\nexport const createPressEvent = (touch: NativeTouch): PressEvent => ({\n nativeEvent: { ...touch, touches: [touch], changedTouches: [touch] },\n})\n"]}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { StyleProp } from "../contracts";
|
|
3
|
+
import { type PressEvent } from "./press-event";
|
|
3
4
|
import { type LayoutEvent } from "./use-layout-child";
|
|
4
5
|
export type PressableStateCallbackType = {
|
|
5
6
|
pressed: boolean;
|
|
6
7
|
hovered: boolean;
|
|
7
8
|
};
|
|
8
|
-
export type PressEvent
|
|
9
|
-
nativeEvent: {
|
|
10
|
-
x: number;
|
|
11
|
-
y: number;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
9
|
+
export type { NativeTouch, PressEvent } from "./press-event";
|
|
14
10
|
export type PressableProps = {
|
|
15
11
|
style?: StyleProp | ((state: PressableStateCallbackType) => StyleProp);
|
|
16
12
|
children?: ReactNode | ((state: PressableStateCallbackType) => ReactNode);
|
|
@@ -1,21 +1,46 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { useLayoutEffect, useReducer, useRef, useState, } from "react";
|
|
3
|
+
import { splitStyle, StyleSheet } from "../style/index";
|
|
4
|
+
import { defaultCssRegistry } from "../style/registry.gtkx";
|
|
3
5
|
import { Gtk, GtkBox } from "../gtkx/bridge/index";
|
|
6
|
+
import { perfAddTime, perfCount, perfEnabled, perfNow } from "../perf";
|
|
4
7
|
import { HostNodeContext } from "./host-node";
|
|
8
|
+
import { createPressEvent, createTouch } from "./press-event";
|
|
5
9
|
import { useLayoutChild, useRnContainer, } from "./use-layout-child";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
// Coordinates arrive from GtkGestureClick in the widget's own space, which
|
|
11
|
+
// is exactly RN's locationX/locationY; createTouch translates them into the
|
|
12
|
+
// window for pageX/pageY.
|
|
13
|
+
const pressEvent = (widget, x, y) => createPressEvent(createTouch(widget, x, y));
|
|
9
14
|
// A View with a click gesture and hover tracking. State-dependent style and
|
|
10
15
|
// children follow the RN Pressable function-prop contract.
|
|
11
16
|
export const Pressable = ({ style, children, onPress, onPressIn, onPressOut, onLongPress, onHoverIn, onHoverOut, disabled = false, delayLongPress = 500, onLayout, testID, }) => {
|
|
12
17
|
const widgetRef = useRef(null);
|
|
13
18
|
const [pressed, setPressed] = useState(false);
|
|
14
|
-
|
|
19
|
+
// Hover is not React state: a boundary crossing should cost what it costs
|
|
20
|
+
// a native GtkListBox row to swap a CSS class, not a setState + render +
|
|
21
|
+
// Yoga + allocate cycle (measured: roughly 500x the latency of a native
|
|
22
|
+
// row at rest, see docs/research/scroll-performance.md, task 007).
|
|
23
|
+
// `hoveredRef` is the single source of truth, read directly at render
|
|
24
|
+
// time — safe here because this component already mutates
|
|
25
|
+
// `handlersRef.current` in the render body below, so render was never
|
|
26
|
+
// React-DOM-pure to begin with. `forceRender` is used only for the two
|
|
27
|
+
// cases that genuinely need a real render: `children` reads `hovered`,
|
|
28
|
+
// or hovering would itself change layout (not just paint).
|
|
29
|
+
const hoveredRef = useRef(false);
|
|
30
|
+
const [, forceRender] = useReducer((n) => n + 1, 0);
|
|
15
31
|
const longPressTimer = useRef(null);
|
|
16
32
|
const longPressFired = useRef(false);
|
|
17
|
-
|
|
18
|
-
//
|
|
33
|
+
// TEMPORARY perf instrumentation (task 007, see
|
|
34
|
+
// docs/research/scroll-performance.md): timestamp the native enter/leave
|
|
35
|
+
// signal so hover's real signal-to-applied latency can be reported —
|
|
36
|
+
// synchronously for the fast path below, after the resulting commit for
|
|
37
|
+
// the slow one. perf.ts hooks are no-ops unless GTKX_PERF=1.
|
|
38
|
+
const hoverSignalAt = useRef(null);
|
|
39
|
+
const state = {
|
|
40
|
+
pressed,
|
|
41
|
+
hovered: hoveredRef.current,
|
|
42
|
+
};
|
|
43
|
+
// Gestures are attached imperatively even though rc.3 has a declarative
|
|
19
44
|
// `controllers` slot: the controllers are wired exactly once per widget and
|
|
20
45
|
// the handlers read the latest props through a ref, so a re-render never
|
|
21
46
|
// detaches and re-adds a gesture mid-press.
|
|
@@ -36,11 +61,54 @@ export const Pressable = ({ style, children, onPress, onPressIn, onPressOut, onL
|
|
|
36
61
|
});
|
|
37
62
|
const resolvedStyle = typeof style === "function" ? style(state) : style;
|
|
38
63
|
const resolvedChildren = typeof children === "function" ? children(state) : children;
|
|
39
|
-
const { host, node, cssClass } = useLayoutChild(widgetRef, {
|
|
64
|
+
const { host, node, cssClass, flat } = useLayoutChild(widgetRef, {
|
|
40
65
|
style: resolvedStyle,
|
|
41
66
|
onLayout,
|
|
42
67
|
});
|
|
43
68
|
useRnContainer(widgetRef, node);
|
|
69
|
+
// Hover fast path: when `children` does not read state, hovering can
|
|
70
|
+
// never change what is rendered — only `style` might. Precompute the CSS
|
|
71
|
+
// class for the OTHER hover value so the native enter/leave handlers
|
|
72
|
+
// below can swap classes directly on the widget, with no React involved
|
|
73
|
+
// at all. If hovering would also change LAYOUT (rare — every hover style
|
|
74
|
+
// in this codebase's own examples is background/border) that swap is
|
|
75
|
+
// unsafe, since a real reflow is required; fall back to the ordinary
|
|
76
|
+
// render in that case, exactly as before this change.
|
|
77
|
+
let hoverOnClass = null;
|
|
78
|
+
let hoverOffClass = null;
|
|
79
|
+
let canSwapHoverClass = typeof children !== "function";
|
|
80
|
+
if (canSwapHoverClass && typeof style === "function") {
|
|
81
|
+
const otherState = {
|
|
82
|
+
pressed,
|
|
83
|
+
hovered: !hoveredRef.current,
|
|
84
|
+
};
|
|
85
|
+
const otherFlat = StyleSheet.flatten(style(otherState));
|
|
86
|
+
const otherSplit = splitStyle(otherFlat);
|
|
87
|
+
const currentLayout = splitStyle(flat).layout;
|
|
88
|
+
if (JSON.stringify(currentLayout) !== JSON.stringify(otherSplit.layout)) {
|
|
89
|
+
// Hovering resizes or repositions this widget — a CSS class alone
|
|
90
|
+
// cannot do that, it needs Yoga. Fall back to a real render.
|
|
91
|
+
canSwapHoverClass = false;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const otherClass = defaultCssRegistry.getClassName(otherSplit.visual);
|
|
95
|
+
hoverOnClass = hoveredRef.current ? cssClass : otherClass;
|
|
96
|
+
hoverOffClass = hoveredRef.current ? otherClass : cssClass;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// TEMPORARY perf instrumentation (task 007): runs after every commit, so
|
|
100
|
+
// it always lands right after whatever render the SLOW hover path
|
|
101
|
+
// triggered. A no-op when no hover signal is pending (the common case:
|
|
102
|
+
// renders triggered by anything else, or the fast path above, which
|
|
103
|
+
// measures itself synchronously) or when GTKX_PERF is unset.
|
|
104
|
+
useLayoutEffect(() => {
|
|
105
|
+
if (!perfEnabled || hoverSignalAt.current === null) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
perfAddTime("pressable.hoverApply", perfNow() - hoverSignalAt.current);
|
|
109
|
+
perfCount("pressable.hoverFullCycle");
|
|
110
|
+
hoverSignalAt.current = null;
|
|
111
|
+
});
|
|
44
112
|
const clearLongPress = () => {
|
|
45
113
|
if (longPressTimer.current !== null) {
|
|
46
114
|
clearTimeout(longPressTimer.current);
|
|
@@ -53,12 +121,15 @@ export const Pressable = ({ style, children, onPress, onPressIn, onPressOut, onL
|
|
|
53
121
|
}
|
|
54
122
|
longPressFired.current = false;
|
|
55
123
|
setPressed(true);
|
|
56
|
-
|
|
124
|
+
const widget = widgetRef.current;
|
|
125
|
+
onPressIn?.(pressEvent(widget, x, y));
|
|
57
126
|
if (onLongPress) {
|
|
58
127
|
clearLongPress();
|
|
59
128
|
longPressTimer.current = setTimeout(() => {
|
|
60
129
|
longPressFired.current = true;
|
|
61
|
-
|
|
130
|
+
// Timestamped when it FIRES, not when the press started — the delay
|
|
131
|
+
// is the whole point of the event.
|
|
132
|
+
onLongPress(pressEvent(widgetRef.current, x, y));
|
|
62
133
|
}, delayLongPress);
|
|
63
134
|
}
|
|
64
135
|
};
|
|
@@ -68,27 +139,55 @@ export const Pressable = ({ style, children, onPress, onPressIn, onPressOut, onL
|
|
|
68
139
|
}
|
|
69
140
|
clearLongPress();
|
|
70
141
|
setPressed(false);
|
|
71
|
-
|
|
142
|
+
const widget = widgetRef.current;
|
|
143
|
+
onPressOut?.(pressEvent(widget, x, y));
|
|
72
144
|
if (!longPressFired.current) {
|
|
73
|
-
onPress?.(pressEvent(x, y));
|
|
145
|
+
onPress?.(pressEvent(widget, x, y));
|
|
74
146
|
}
|
|
75
147
|
};
|
|
76
148
|
const handleCancel = () => {
|
|
77
149
|
clearLongPress();
|
|
78
150
|
setPressed(false);
|
|
79
151
|
};
|
|
152
|
+
// Applies a hover transition: fires the RN callback, then either swaps
|
|
153
|
+
// the precomputed CSS class directly on the widget (fast path) or forces
|
|
154
|
+
// the ordinary render (slow path) — see the `canSwapHoverClass` block
|
|
155
|
+
// above for which one applies this render.
|
|
156
|
+
const applyHover = (next, fire) => {
|
|
157
|
+
const t0 = perfEnabled ? perfNow() : 0;
|
|
158
|
+
hoveredRef.current = next;
|
|
159
|
+
fire?.();
|
|
160
|
+
if (!canSwapHoverClass) {
|
|
161
|
+
if (perfEnabled) {
|
|
162
|
+
hoverSignalAt.current = t0;
|
|
163
|
+
perfCount("pressable.hoverEvent");
|
|
164
|
+
}
|
|
165
|
+
forceRender();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const widget = widgetRef.current;
|
|
169
|
+
const applyClass = next ? hoverOnClass : hoverOffClass;
|
|
170
|
+
const removeClass = next ? hoverOffClass : hoverOnClass;
|
|
171
|
+
if (widget && hoverOnClass !== hoverOffClass) {
|
|
172
|
+
if (removeClass) {
|
|
173
|
+
widget.removeCssClass(removeClass);
|
|
174
|
+
}
|
|
175
|
+
if (applyClass) {
|
|
176
|
+
widget.addCssClass(applyClass);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (perfEnabled) {
|
|
180
|
+
perfAddTime("pressable.hoverApply", perfNow() - t0);
|
|
181
|
+
perfCount("pressable.hoverEvent");
|
|
182
|
+
perfCount("pressable.hoverFast");
|
|
183
|
+
}
|
|
184
|
+
};
|
|
80
185
|
handlersRef.current = {
|
|
81
186
|
handlePressed,
|
|
82
187
|
handleReleased,
|
|
83
188
|
handleCancel,
|
|
84
|
-
handleEnter: () =>
|
|
85
|
-
|
|
86
|
-
onHoverIn?.();
|
|
87
|
-
},
|
|
88
|
-
handleLeave: () => {
|
|
89
|
-
setHovered(false);
|
|
90
|
-
onHoverOut?.();
|
|
91
|
-
},
|
|
189
|
+
handleEnter: () => applyHover(true, onHoverIn),
|
|
190
|
+
handleLeave: () => applyHover(false, onHoverOut),
|
|
92
191
|
};
|
|
93
192
|
useLayoutEffect(() => {
|
|
94
193
|
const widget = widgetRef.current;
|