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.
Files changed (257) hide show
  1. package/README.md +22 -9
  2. package/dist/adw/index.d.ts +3 -0
  3. package/dist/adw/index.js +41 -0
  4. package/dist/adw/index.js.map +1 -0
  5. package/dist/adw/widgets.generated.d.ts +63 -0
  6. package/dist/adw/widgets.generated.js +125 -0
  7. package/dist/adw/widgets.generated.js.map +1 -0
  8. package/dist/animated/create-animated.d.ts +2 -0
  9. package/dist/animated/create-animated.js +2 -0
  10. package/dist/animated/create-animated.js.map +1 -1
  11. package/dist/animated/index.d.ts +2 -0
  12. package/dist/animated/index.js +1 -0
  13. package/dist/animated/index.js.map +1 -1
  14. package/dist/animated/value-xy.d.ts +41 -0
  15. package/dist/animated/value-xy.js +90 -0
  16. package/dist/animated/value-xy.js.map +1 -0
  17. package/dist/animated/value.d.ts +9 -0
  18. package/dist/animated/value.js +30 -2
  19. package/dist/animated/value.js.map +1 -1
  20. package/dist/common/content.d.ts +69 -0
  21. package/dist/common/content.js +84 -0
  22. package/dist/common/content.js.map +1 -0
  23. package/dist/common/icon.d.ts +23 -0
  24. package/dist/common/icon.js +14 -0
  25. package/dist/common/icon.js.map +1 -0
  26. package/dist/common/index.d.ts +5 -0
  27. package/dist/common/index.js +21 -0
  28. package/dist/common/index.js.map +1 -0
  29. package/dist/common/list.d.ts +82 -0
  30. package/dist/common/list.js +166 -0
  31. package/dist/common/list.js.map +1 -0
  32. package/dist/common/navigation-stack.d.ts +84 -0
  33. package/dist/common/navigation-stack.js +313 -0
  34. package/dist/common/navigation-stack.js.map +1 -0
  35. package/dist/common/widget.d.ts +62 -0
  36. package/dist/common/widget.js +209 -0
  37. package/dist/common/widget.js.map +1 -0
  38. package/dist/components/animated.d.ts +7 -4
  39. package/dist/components/animated.js +88 -67
  40. package/dist/components/animated.js.map +1 -1
  41. package/dist/components/app-registry.d.ts +36 -1
  42. package/dist/components/app-registry.js +8 -3
  43. package/dist/components/app-registry.js.map +1 -1
  44. package/dist/components/host-node.d.ts +7 -0
  45. package/dist/components/host-node.js +26 -1
  46. package/dist/components/host-node.js.map +1 -1
  47. package/dist/components/index.d.ts +6 -2
  48. package/dist/components/index.js +2 -1
  49. package/dist/components/index.js.map +1 -1
  50. package/dist/components/measure.d.ts +13 -0
  51. package/dist/components/measure.js +51 -0
  52. package/dist/components/measure.js.map +1 -0
  53. package/dist/components/modal.d.ts +1 -1
  54. package/dist/components/press-event.d.ts +28 -0
  55. package/dist/components/press-event.js +46 -0
  56. package/dist/components/press-event.js.map +1 -0
  57. package/dist/components/pressable.d.ts +2 -6
  58. package/dist/components/pressable.js +119 -20
  59. package/dist/components/pressable.js.map +1 -1
  60. package/dist/components/rect-store.d.ts +11 -1
  61. package/dist/components/rect-store.js +27 -3
  62. package/dist/components/rect-store.js.map +1 -1
  63. package/dist/components/scroll-view.d.ts +4 -0
  64. package/dist/components/scroll-view.js +8 -2
  65. package/dist/components/scroll-view.js.map +1 -1
  66. package/dist/components/svg/animated-support.d.ts +19 -0
  67. package/dist/components/svg/animated-support.js +64 -0
  68. package/dist/components/svg/animated-support.js.map +1 -0
  69. package/dist/components/svg/context.d.ts +8 -0
  70. package/dist/components/svg/context.js +15 -0
  71. package/dist/components/svg/context.js.map +1 -0
  72. package/dist/components/svg/gradients.d.ts +31 -0
  73. package/dist/components/svg/gradients.js +93 -0
  74. package/dist/components/svg/gradients.js.map +1 -0
  75. package/dist/components/svg/group.d.ts +8 -0
  76. package/dist/components/svg/group.js +30 -0
  77. package/dist/components/svg/group.js.map +1 -0
  78. package/dist/components/svg/index.d.ts +6 -0
  79. package/dist/components/svg/index.js +5 -0
  80. package/dist/components/svg/index.js.map +1 -0
  81. package/dist/components/svg/node.d.ts +7 -0
  82. package/dist/components/svg/node.js +7 -0
  83. package/dist/components/svg/node.js.map +1 -0
  84. package/dist/components/svg/paint.d.ts +24 -0
  85. package/dist/components/svg/paint.js +57 -0
  86. package/dist/components/svg/paint.js.map +1 -0
  87. package/dist/components/svg/shapes.d.ts +47 -0
  88. package/dist/components/svg/shapes.js +51 -0
  89. package/dist/components/svg/shapes.js.map +1 -0
  90. package/dist/components/svg/svg.d.ts +14 -0
  91. package/dist/components/svg/svg.js +48 -0
  92. package/dist/components/svg/svg.js.map +1 -0
  93. package/dist/components/svg/use-shape-node.d.ts +12 -0
  94. package/dist/components/svg/use-shape-node.js +52 -0
  95. package/dist/components/svg/use-shape-node.js.map +1 -0
  96. package/dist/components/text-input.js +1 -1
  97. package/dist/components/text-input.js.map +1 -1
  98. package/dist/components/text.js +36 -3
  99. package/dist/components/text.js.map +1 -1
  100. package/dist/components/use-layout-child.js +50 -4
  101. package/dist/components/use-layout-child.js.map +1 -1
  102. package/dist/components/view.d.ts +9 -4
  103. package/dist/components/view.js +8 -2
  104. package/dist/components/view.js.map +1 -1
  105. package/dist/contracts.d.ts +31 -4
  106. package/dist/contracts.js.map +1 -1
  107. package/dist/gtk/index.d.ts +7 -0
  108. package/dist/gtk/index.js +46 -0
  109. package/dist/gtk/index.js.map +1 -0
  110. package/dist/gtk/widgets.generated.d.ts +103 -0
  111. package/dist/gtk/widgets.generated.js +205 -0
  112. package/dist/gtk/widgets.generated.js.map +1 -0
  113. package/dist/gtk/window-actions.d.ts +77 -0
  114. package/dist/gtk/window-actions.js +193 -0
  115. package/dist/gtk/window-actions.js.map +1 -0
  116. package/dist/gtkx/bridge/geometry.d.ts +15 -0
  117. package/dist/gtkx/bridge/geometry.js +38 -0
  118. package/dist/gtkx/bridge/geometry.js.map +1 -0
  119. package/dist/gtkx/bridge/index.d.ts +11 -4
  120. package/dist/gtkx/bridge/index.js +47 -6
  121. package/dist/gtkx/bridge/index.js.map +1 -1
  122. package/dist/gtkx/bridge/layout-manager.d.ts +2 -1
  123. package/dist/gtkx/bridge/layout-manager.js +31 -9
  124. package/dist/gtkx/bridge/layout-manager.js.map +1 -1
  125. package/dist/gtkx/bridge/slot-portal.d.ts +12 -0
  126. package/dist/gtkx/bridge/slot-portal.js +17 -0
  127. package/dist/gtkx/bridge/slot-portal.js.map +1 -0
  128. package/dist/gtkx/bridge/svg-node.d.ts +79 -0
  129. package/dist/gtkx/bridge/svg-node.js +323 -0
  130. package/dist/gtkx/bridge/svg-node.js.map +1 -0
  131. package/dist/gtkx/bridge/use-signal.js +19 -9
  132. package/dist/gtkx/bridge/use-signal.js.map +1 -1
  133. package/dist/gtkx/bridge/widgets.generated.d.ts +2 -0
  134. package/dist/gtkx/bridge/widgets.generated.js +14 -0
  135. package/dist/gtkx/bridge/widgets.generated.js.map +1 -0
  136. package/dist/index.d.ts +2 -2
  137. package/dist/index.js +1 -1
  138. package/dist/index.js.map +1 -1
  139. package/dist/mcp/bin.d.ts +2 -0
  140. package/dist/mcp/bin.js +17 -0
  141. package/dist/mcp/bin.js.map +1 -0
  142. package/dist/mcp/data/generated.d.ts +1304 -0
  143. package/dist/mcp/data/generated.js +1179 -0
  144. package/dist/mcp/data/generated.js.map +1 -0
  145. package/dist/mcp/index.d.ts +5 -0
  146. package/dist/mcp/index.js +9 -0
  147. package/dist/mcp/index.js.map +1 -0
  148. package/dist/mcp/list.d.ts +4 -0
  149. package/dist/mcp/list.js +56 -0
  150. package/dist/mcp/list.js.map +1 -0
  151. package/dist/mcp/resolve.d.ts +55 -0
  152. package/dist/mcp/resolve.js +151 -0
  153. package/dist/mcp/resolve.js.map +1 -0
  154. package/dist/mcp/search.d.ts +13 -0
  155. package/dist/mcp/search.js +48 -0
  156. package/dist/mcp/search.js.map +1 -0
  157. package/dist/mcp/server.d.ts +7 -0
  158. package/dist/mcp/server.js +135 -0
  159. package/dist/mcp/server.js.map +1 -0
  160. package/dist/mcp/tool.d.ts +21 -0
  161. package/dist/mcp/tool.js +36 -0
  162. package/dist/mcp/tool.js.map +1 -0
  163. package/dist/metro/index.js +10 -0
  164. package/dist/metro/index.js.map +1 -1
  165. package/dist/navigation/index.d.ts +50 -13
  166. package/dist/navigation/index.js +183 -148
  167. package/dist/navigation/index.js.map +1 -1
  168. package/dist/navigation/option-warnings.js +9 -1
  169. package/dist/navigation/option-warnings.js.map +1 -1
  170. package/dist/navigation/sidebar.d.ts +207 -9
  171. package/dist/navigation/sidebar.js +294 -10
  172. package/dist/navigation/sidebar.js.map +1 -1
  173. package/dist/responder/system.d.ts +27 -0
  174. package/dist/responder/system.js +184 -0
  175. package/dist/responder/system.js.map +1 -0
  176. package/dist/responder/touch-history.d.ts +10 -0
  177. package/dist/responder/touch-history.js +78 -0
  178. package/dist/responder/touch-history.js.map +1 -0
  179. package/dist/responder/types.d.ts +63 -0
  180. package/dist/responder/types.js +29 -0
  181. package/dist/responder/types.js.map +1 -0
  182. package/dist/responder/use-responder.d.ts +12 -0
  183. package/dist/responder/use-responder.js +98 -0
  184. package/dist/responder/use-responder.js.map +1 -0
  185. package/dist/runner/host-dev.js +3 -1
  186. package/dist/runner/host-dev.js.map +1 -1
  187. package/dist/runner/host.js +5 -1
  188. package/dist/runner/host.js.map +1 -1
  189. package/dist/runner/index.d.ts +22 -2
  190. package/dist/runner/index.js +163 -13
  191. package/dist/runner/index.js.map +1 -1
  192. package/dist/sea/assemble.d.ts +29 -0
  193. package/dist/sea/assemble.js +129 -0
  194. package/dist/sea/assemble.js.map +1 -0
  195. package/dist/sea/bundle.d.ts +61 -0
  196. package/dist/sea/bundle.js +202 -0
  197. package/dist/sea/bundle.js.map +1 -0
  198. package/dist/sea/gtkx-config-module.d.ts +18 -0
  199. package/dist/sea/gtkx-config-module.js +67 -0
  200. package/dist/sea/gtkx-config-module.js.map +1 -0
  201. package/dist/sea/native-shim.d.ts +40 -0
  202. package/dist/sea/native-shim.js +121 -0
  203. package/dist/sea/native-shim.js.map +1 -0
  204. package/dist/sea/react-anchor-plugin.d.ts +2 -0
  205. package/dist/sea/react-anchor-plugin.js +30 -0
  206. package/dist/sea/react-anchor-plugin.js.map +1 -0
  207. package/dist/style/box-shadow.d.ts +7 -0
  208. package/dist/style/box-shadow.js +197 -0
  209. package/dist/style/box-shadow.js.map +1 -0
  210. package/dist/style/index.d.ts +3 -0
  211. package/dist/style/index.js +3 -0
  212. package/dist/style/index.js.map +1 -1
  213. package/dist/style/split-style.d.ts +5 -2
  214. package/dist/style/split-style.js +33 -5
  215. package/dist/style/split-style.js.map +1 -1
  216. package/dist/style/text-decoration.d.ts +15 -0
  217. package/dist/style/text-decoration.js +26 -0
  218. package/dist/style/text-decoration.js.map +1 -0
  219. package/dist/style/transform.d.ts +22 -0
  220. package/dist/style/transform.js +137 -0
  221. package/dist/style/transform.js.map +1 -0
  222. package/dist/style/visual-css.js +40 -3
  223. package/dist/style/visual-css.js.map +1 -1
  224. package/dist/svg/geometry.d.ts +19 -0
  225. package/dist/svg/geometry.js +80 -0
  226. package/dist/svg/geometry.js.map +1 -0
  227. package/dist/svg/gradient-geometry.d.ts +15 -0
  228. package/dist/svg/gradient-geometry.js +15 -0
  229. package/dist/svg/gradient-geometry.js.map +1 -0
  230. package/dist/svg/transform.d.ts +22 -0
  231. package/dist/svg/transform.js +74 -0
  232. package/dist/svg/transform.js.map +1 -0
  233. package/dist/svg/view-box.d.ts +34 -0
  234. package/dist/svg/view-box.js +108 -0
  235. package/dist/svg/view-box.js.map +1 -0
  236. package/dist/svg-compat/index.d.ts +1 -0
  237. package/dist/svg-compat/index.js +16 -0
  238. package/dist/svg-compat/index.js.map +1 -0
  239. package/dist/testing/index.d.ts +14 -0
  240. package/dist/testing/index.js +14 -0
  241. package/dist/testing/index.js.map +1 -0
  242. package/dist/vendor/react-native/pan-responder.d.ts +102 -0
  243. package/dist/vendor/react-native/pan-responder.js +196 -0
  244. package/dist/vendor/react-native/pan-responder.js.map +1 -0
  245. package/dist/vendor/react-native/touch-history-math.d.ts +18 -0
  246. package/dist/vendor/react-native/touch-history-math.js +102 -0
  247. package/dist/vendor/react-native/touch-history-math.js.map +1 -0
  248. package/dist/vite/index.d.ts +7 -3
  249. package/dist/vite/index.js +16 -4
  250. package/dist/vite/index.js.map +1 -1
  251. package/dist/vitest/index.d.ts +52 -0
  252. package/dist/vitest/index.js +58 -0
  253. package/dist/vitest/index.js.map +1 -0
  254. package/dist/vitest/setup.d.ts +4 -0
  255. package/dist/vitest/setup.js +22 -0
  256. package/dist/vitest/setup.js.map +1 -0
  257. 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("react-native-gtkx components must be rendered inside AppRegistry.runApplication() or a <Root>");
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,CAChG,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\nexport const useHostNode = (): HostNode => {\n const host = useContext(HostNodeContext)\n if (host === null) {\n throw new Error(\n \"react-native-gtkx components must be rendered inside AppRegistry.runApplication() or a <Root>\",\n )\n }\n return host\n}\n"]}
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";
@@ -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,GAKjB,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,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAkB,MAAM,QAAQ,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 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 { SafeAreaView, StatusBar, View, type ViewProps } from \"./view\"\nexport { type LayoutEvent } from \"./use-layout-child\"\n"]}
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) => ReactNode;
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
- const pressEvent = (x, y) => ({
7
- nativeEvent: { x, y },
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
- const [hovered, setHovered] = useState(false);
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
- const state = { pressed, hovered };
18
- // Gestures are attached imperatively even though rc.2 has a declarative
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
- onPressIn?.(pressEvent(x, y));
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
- onLongPress(pressEvent(x, y));
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
- onPressOut?.(pressEvent(x, y));
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
- setHovered(true);
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;