basalt-core 0.0.0-rc5

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 (325) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli/basalt.d.ts +3 -0
  3. package/dist/cli/basalt.d.ts.map +1 -0
  4. package/dist/cli/basalt.js +47 -0
  5. package/dist/cli/basalt.js.map +1 -0
  6. package/dist/cli/bundle-cli.d.ts +12 -0
  7. package/dist/cli/bundle-cli.d.ts.map +1 -0
  8. package/dist/cli/bundle-cli.js +124 -0
  9. package/dist/cli/bundle-cli.js.map +1 -0
  10. package/dist/cli/bundleWithAssets.d.ts +79 -0
  11. package/dist/cli/bundleWithAssets.d.ts.map +1 -0
  12. package/dist/cli/bundleWithAssets.js +198 -0
  13. package/dist/cli/bundleWithAssets.js.map +1 -0
  14. package/dist/cli/desktop.d.ts +339 -0
  15. package/dist/cli/desktop.d.ts.map +1 -0
  16. package/dist/cli/desktop.js +1029 -0
  17. package/dist/cli/desktop.js.map +1 -0
  18. package/dist/cli/doctor.d.ts +31 -0
  19. package/dist/cli/doctor.d.ts.map +1 -0
  20. package/dist/cli/doctor.js +283 -0
  21. package/dist/cli/doctor.js.map +1 -0
  22. package/dist/cli/init.d.ts +77 -0
  23. package/dist/cli/init.d.ts.map +1 -0
  24. package/dist/cli/init.js +442 -0
  25. package/dist/cli/init.js.map +1 -0
  26. package/dist/cli/metro.d.ts +49 -0
  27. package/dist/cli/metro.d.ts.map +1 -0
  28. package/dist/cli/metro.js +148 -0
  29. package/dist/cli/metro.js.map +1 -0
  30. package/dist/cli/packageApp.d.ts +102 -0
  31. package/dist/cli/packageApp.d.ts.map +1 -0
  32. package/dist/cli/packageApp.js +319 -0
  33. package/dist/cli/packageApp.js.map +1 -0
  34. package/dist/cli/steps.d.ts +34 -0
  35. package/dist/cli/steps.d.ts.map +1 -0
  36. package/dist/cli/steps.js +53 -0
  37. package/dist/cli/steps.js.map +1 -0
  38. package/dist/metro-config.d.ts +168 -0
  39. package/dist/metro-config.d.ts.map +1 -0
  40. package/dist/metro-config.js +671 -0
  41. package/dist/metro-config.js.map +1 -0
  42. package/dist/react-native.config.d.ts +7 -0
  43. package/dist/react-native.config.d.ts.map +1 -0
  44. package/dist/react-native.config.js +80 -0
  45. package/dist/react-native.config.js.map +1 -0
  46. package/dist/src/DragSource.d.ts +52 -0
  47. package/dist/src/DragSource.d.ts.map +1 -0
  48. package/dist/src/DragSource.js +97 -0
  49. package/dist/src/DragSource.js.map +1 -0
  50. package/dist/src/DropTarget.d.ts +58 -0
  51. package/dist/src/DropTarget.d.ts.map +1 -0
  52. package/dist/src/DropTarget.js +131 -0
  53. package/dist/src/DropTarget.js.map +1 -0
  54. package/dist/src/KeyHandler.d.ts +57 -0
  55. package/dist/src/KeyHandler.d.ts.map +1 -0
  56. package/dist/src/KeyHandler.js +48 -0
  57. package/dist/src/KeyHandler.js.map +1 -0
  58. package/dist/src/Menu.d.ts +72 -0
  59. package/dist/src/Menu.d.ts.map +1 -0
  60. package/dist/src/Menu.js +179 -0
  61. package/dist/src/Menu.js.map +1 -0
  62. package/dist/src/TitleBar.d.ts +75 -0
  63. package/dist/src/TitleBar.d.ts.map +1 -0
  64. package/dist/src/TitleBar.js +236 -0
  65. package/dist/src/TitleBar.js.map +1 -0
  66. package/dist/src/Window.d.ts +87 -0
  67. package/dist/src/Window.d.ts.map +1 -0
  68. package/dist/src/Window.js +248 -0
  69. package/dist/src/Window.js.map +1 -0
  70. package/dist/src/closeRequest.d.ts +55 -0
  71. package/dist/src/closeRequest.d.ts.map +1 -0
  72. package/dist/src/closeRequest.js +134 -0
  73. package/dist/src/closeRequest.js.map +1 -0
  74. package/dist/src/index.d.ts +45 -0
  75. package/dist/src/index.d.ts.map +1 -0
  76. package/dist/src/index.js +92 -0
  77. package/dist/src/index.js.map +1 -0
  78. package/dist/src/overrides/Alert.d.ts +69 -0
  79. package/dist/src/overrides/Alert.d.ts.map +1 -0
  80. package/dist/src/overrides/Alert.js +112 -0
  81. package/dist/src/overrides/Alert.js.map +1 -0
  82. package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
  83. package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
  84. package/dist/src/overrides/BaseViewConfig.js +72 -0
  85. package/dist/src/overrides/BaseViewConfig.js.map +1 -0
  86. package/dist/src/overrides/Platform.linux.d.ts +10 -0
  87. package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
  88. package/dist/src/overrides/Platform.linux.js +15 -0
  89. package/dist/src/overrides/Platform.linux.js.map +1 -0
  90. package/dist/src/overrides/Platform.macos.d.ts +10 -0
  91. package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
  92. package/dist/src/overrides/Platform.macos.js +15 -0
  93. package/dist/src/overrides/Platform.macos.js.map +1 -0
  94. package/dist/src/overrides/Platform.windows.d.ts +10 -0
  95. package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
  96. package/dist/src/overrides/Platform.windows.js +15 -0
  97. package/dist/src/overrides/Platform.windows.js.map +1 -0
  98. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
  99. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
  100. package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
  101. package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
  102. package/dist/src/overrides/RefreshControl.d.ts +52 -0
  103. package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
  104. package/dist/src/overrides/RefreshControl.js +92 -0
  105. package/dist/src/overrides/RefreshControl.js.map +1 -0
  106. package/dist/src/overrides/ScrollView.d.ts +39 -0
  107. package/dist/src/overrides/ScrollView.d.ts.map +1 -0
  108. package/dist/src/overrides/ScrollView.js +59 -0
  109. package/dist/src/overrides/ScrollView.js.map +1 -0
  110. package/dist/src/overrides/Share.d.ts +96 -0
  111. package/dist/src/overrides/Share.d.ts.map +1 -0
  112. package/dist/src/overrides/Share.js +97 -0
  113. package/dist/src/overrides/Share.js.map +1 -0
  114. package/dist/src/overrides/TextInput.d.ts +81 -0
  115. package/dist/src/overrides/TextInput.d.ts.map +1 -0
  116. package/dist/src/overrides/TextInput.js +213 -0
  117. package/dist/src/overrides/TextInput.js.map +1 -0
  118. package/dist/src/overrides/View.d.ts +36 -0
  119. package/dist/src/overrides/View.d.ts.map +1 -0
  120. package/dist/src/overrides/View.js +99 -0
  121. package/dist/src/overrides/View.js.map +1 -0
  122. package/dist/src/overrides/createPlatform.d.ts +32 -0
  123. package/dist/src/overrides/createPlatform.d.ts.map +1 -0
  124. package/dist/src/overrides/createPlatform.js +84 -0
  125. package/dist/src/overrides/createPlatform.js.map +1 -0
  126. package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
  127. package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
  128. package/dist/src/overrides/setUpDeveloperTools.js +84 -0
  129. package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
  130. package/dist/src/overrides/setUpXHR.d.ts +46 -0
  131. package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
  132. package/dist/src/overrides/setUpXHR.js +236 -0
  133. package/dist/src/overrides/setUpXHR.js.map +1 -0
  134. package/dist/src/quitRequest.d.ts +55 -0
  135. package/dist/src/quitRequest.d.ts.map +1 -0
  136. package/dist/src/quitRequest.js +121 -0
  137. package/dist/src/quitRequest.js.map +1 -0
  138. package/dist/src/resourcePath.d.ts +18 -0
  139. package/dist/src/resourcePath.d.ts.map +1 -0
  140. package/dist/src/resourcePath.js +31 -0
  141. package/dist/src/resourcePath.js.map +1 -0
  142. package/dist/src/responseBody.d.ts +76 -0
  143. package/dist/src/responseBody.d.ts.map +1 -0
  144. package/dist/src/responseBody.js +94 -0
  145. package/dist/src/responseBody.js.map +1 -0
  146. package/dist/src/responseHeaders.d.ts +40 -0
  147. package/dist/src/responseHeaders.d.ts.map +1 -0
  148. package/dist/src/responseHeaders.js +75 -0
  149. package/dist/src/responseHeaders.js.map +1 -0
  150. package/dist/src/titleBarState.d.ts +43 -0
  151. package/dist/src/titleBarState.d.ts.map +1 -0
  152. package/dist/src/titleBarState.js +77 -0
  153. package/dist/src/titleBarState.js.map +1 -0
  154. package/dist/src/useContextMenu.d.ts +113 -0
  155. package/dist/src/useContextMenu.d.ts.map +1 -0
  156. package/dist/src/useContextMenu.js +109 -0
  157. package/dist/src/useContextMenu.js.map +1 -0
  158. package/dist/src/useDialog.d.ts +98 -0
  159. package/dist/src/useDialog.d.ts.map +1 -0
  160. package/dist/src/useDialog.js +112 -0
  161. package/dist/src/useDialog.js.map +1 -0
  162. package/dist/src/useDisplays.d.ts +70 -0
  163. package/dist/src/useDisplays.d.ts.map +1 -0
  164. package/dist/src/useDisplays.js +118 -0
  165. package/dist/src/useDisplays.js.map +1 -0
  166. package/dist/src/useHandledKeys.d.ts +38 -0
  167. package/dist/src/useHandledKeys.d.ts.map +1 -0
  168. package/dist/src/useHandledKeys.js +70 -0
  169. package/dist/src/useHandledKeys.js.map +1 -0
  170. package/dist/src/useWindow.d.ts +140 -0
  171. package/dist/src/useWindow.d.ts.map +1 -0
  172. package/dist/src/useWindow.js +233 -0
  173. package/dist/src/useWindow.js.map +1 -0
  174. package/dist/tsconfig.tsbuildinfo +1 -0
  175. package/native/CMakeLists.txt +355 -0
  176. package/native/bootstrap.sh +613 -0
  177. package/native/cmake/CompilerFlags.cmake +49 -0
  178. package/native/cmake/Expo.cmake +125 -0
  179. package/native/cmake/ReactNativeCore.cmake +612 -0
  180. package/native/cmake/Reanimated.cmake +108 -0
  181. package/native/cmake/Skia.cmake +379 -0
  182. package/native/cmake/ThirdParty.cmake +290 -0
  183. package/native/cmake/Worklets.cmake +67 -0
  184. package/native/cmake/codegen/CMakeLists.txt +32 -0
  185. package/native/core/AppIdentity.cpp +85 -0
  186. package/native/core/AppIdentity.h +59 -0
  187. package/native/core/AppPaths.cpp +96 -0
  188. package/native/core/AppPaths.h +53 -0
  189. package/native/core/AppearanceModule.cpp +60 -0
  190. package/native/core/AppearanceModule.h +40 -0
  191. package/native/core/Backface.h +59 -0
  192. package/native/core/BlobModule.cpp +283 -0
  193. package/native/core/BlobModule.h +76 -0
  194. package/native/core/BlobRegistry.cpp +75 -0
  195. package/native/core/BlobRegistry.h +44 -0
  196. package/native/core/ColorScheme.cpp +105 -0
  197. package/native/core/ColorScheme.h +59 -0
  198. package/native/core/ComponentRegistry.h +30 -0
  199. package/native/core/ControlMetrics.h +37 -0
  200. package/native/core/CoreModules.cpp +592 -0
  201. package/native/core/CoreModules.h +308 -0
  202. package/native/core/DebuggingOverlay.cpp +106 -0
  203. package/native/core/DebuggingOverlay.h +65 -0
  204. package/native/core/DesktopControls.cpp +232 -0
  205. package/native/core/DesktopControls.h +170 -0
  206. package/native/core/DevBundle.cpp +108 -0
  207. package/native/core/DevBundle.h +69 -0
  208. package/native/core/DevMenu.cpp +99 -0
  209. package/native/core/DevMenu.h +75 -0
  210. package/native/core/DialogModule.cpp +150 -0
  211. package/native/core/DialogModule.h +51 -0
  212. package/native/core/DragAndDrop.cpp +45 -0
  213. package/native/core/DragAndDrop.h +225 -0
  214. package/native/core/ExpoImageComponent.cpp +79 -0
  215. package/native/core/ExpoImageComponent.h +80 -0
  216. package/native/core/ExpoModules.cpp +415 -0
  217. package/native/core/ExpoModules.h +115 -0
  218. package/native/core/ExpoRuntime.cpp +366 -0
  219. package/native/core/ExpoRuntime.h +42 -0
  220. package/native/core/FocusRing.h +36 -0
  221. package/native/core/FontRegistry.h +51 -0
  222. package/native/core/GestureHandlerModule.cpp +136 -0
  223. package/native/core/GestureHandlerModule.h +71 -0
  224. package/native/core/Gestures.cpp +533 -0
  225. package/native/core/Gestures.h +145 -0
  226. package/native/core/HoverTracker.cpp +69 -0
  227. package/native/core/HoverTracker.h +109 -0
  228. package/native/core/HttpClient.cpp +282 -0
  229. package/native/core/ImageBytes.cpp +205 -0
  230. package/native/core/ImageBytes.h +29 -0
  231. package/native/core/ImageCache.cpp +51 -0
  232. package/native/core/ImageCache.h +89 -0
  233. package/native/core/JsRuntimeAccess.cpp +70 -0
  234. package/native/core/JsRuntimeAccess.h +60 -0
  235. package/native/core/JsiPromise.h +51 -0
  236. package/native/core/KeyEvents.cpp +116 -0
  237. package/native/core/KeyEvents.h +144 -0
  238. package/native/core/LogBoxSurface.cpp +46 -0
  239. package/native/core/LogBoxSurface.h +68 -0
  240. package/native/core/MenuModel.h +117 -0
  241. package/native/core/MenuModule.cpp +209 -0
  242. package/native/core/MenuModule.h +66 -0
  243. package/native/core/MountingWalk.h +524 -0
  244. package/native/core/PackageModules.cpp.in +25 -0
  245. package/native/core/PackageModules.h +50 -0
  246. package/native/core/PlatformConstantsModule.cpp +63 -0
  247. package/native/core/PlatformConstantsModule.h +64 -0
  248. package/native/core/PlatformServices.h +213 -0
  249. package/native/core/PointerButtons.h +63 -0
  250. package/native/core/PullToRefresh.cpp +57 -0
  251. package/native/core/PullToRefresh.h +73 -0
  252. package/native/core/ReanimatedCompat.h +47 -0
  253. package/native/core/ReanimatedModule.cpp +203 -0
  254. package/native/core/ReanimatedModule.h +55 -0
  255. package/native/core/ScrollAnimation.cpp +59 -0
  256. package/native/core/ScrollAnimation.h +70 -0
  257. package/native/core/ScrollBounds.cpp +26 -0
  258. package/native/core/ScrollBounds.h +68 -0
  259. package/native/core/ScrollIndicator.cpp +50 -0
  260. package/native/core/ScrollIndicator.h +69 -0
  261. package/native/core/ScrollMomentum.cpp +71 -0
  262. package/native/core/ScrollMomentum.h +73 -0
  263. package/native/core/ScrollSnap.cpp +123 -0
  264. package/native/core/ScrollSnap.h +80 -0
  265. package/native/core/ShareFallback.cpp +101 -0
  266. package/native/core/ShareFallback.h +45 -0
  267. package/native/core/SkiaPictureViewComponent.cpp +11 -0
  268. package/native/core/SkiaPictureViewComponent.h +47 -0
  269. package/native/core/SourceCodeModule.cpp +55 -0
  270. package/native/core/SourceCodeModule.h +53 -0
  271. package/native/core/StatusBarModule.cpp +23 -0
  272. package/native/core/StatusBarModule.h +42 -0
  273. package/native/core/TestDialog.cpp +148 -0
  274. package/native/core/TestDialog.h +96 -0
  275. package/native/core/TestKeys.cpp +78 -0
  276. package/native/core/TestKeys.h +50 -0
  277. package/native/core/TestQuitFile.cpp +33 -0
  278. package/native/core/TestQuitFile.h +60 -0
  279. package/native/core/TestSettle.h +192 -0
  280. package/native/core/TitleBarRegions.h +31 -0
  281. package/native/core/TurboModuleProxy.cpp +51 -0
  282. package/native/core/TurboModuleProxy.h +46 -0
  283. package/native/core/UIManagerAccess.cpp +43 -0
  284. package/native/core/UIManagerAccess.h +43 -0
  285. package/native/core/WindowBoundsCache.cpp +319 -0
  286. package/native/core/WindowControl.h +235 -0
  287. package/native/core/WindowHost.h +158 -0
  288. package/native/core/WindowMethods.cpp +167 -0
  289. package/native/core/WindowMethods.h +84 -0
  290. package/native/core/WindowsModule.cpp +448 -0
  291. package/native/core/WindowsModule.h +133 -0
  292. package/native/core/WorkletsModule.cpp +199 -0
  293. package/native/core/WorkletsModule.h +57 -0
  294. package/native/core/portability_probe.cpp +210 -0
  295. package/native/tests/EventRecorder.h +124 -0
  296. package/native/tests/TestHarness.cpp +109 -0
  297. package/native/tests/TestHarness.h +89 -0
  298. package/native/tests/TreeDump.h +49 -0
  299. package/native/tests/test_app_identity.cpp +114 -0
  300. package/native/tests/test_app_paths.cpp +48 -0
  301. package/native/tests/test_appearance.cpp +107 -0
  302. package/native/tests/test_backface.cpp +79 -0
  303. package/native/tests/test_blobs.cpp +157 -0
  304. package/native/tests/test_controls.cpp +185 -0
  305. package/native/tests/test_devbundle.cpp +103 -0
  306. package/native/tests/test_drag_and_drop.cpp +143 -0
  307. package/native/tests/test_gestures.cpp +331 -0
  308. package/native/tests/test_hover.cpp +92 -0
  309. package/native/tests/test_image_cache.cpp +109 -0
  310. package/native/tests/test_js_runtime_access.cpp +137 -0
  311. package/native/tests/test_key_events.cpp +235 -0
  312. package/native/tests/test_momentum.cpp +138 -0
  313. package/native/tests/test_quit_file.cpp +104 -0
  314. package/native/tests/test_scroll_animation.cpp +120 -0
  315. package/native/tests/test_scroll_bounds.cpp +66 -0
  316. package/native/tests/test_scroll_indicator.cpp +149 -0
  317. package/native/tests/test_scroll_snap.cpp +112 -0
  318. package/native/tests/test_settle.cpp +115 -0
  319. package/native/tests/test_share.cpp +75 -0
  320. package/native/tests/test_sourcecode.cpp +62 -0
  321. package/native/tests/test_window_close.cpp +119 -0
  322. package/native/tests/test_window_limits.cpp +123 -0
  323. package/package.json +43 -0
  324. package/react-native.config.js +86 -0
  325. package/supported-versions.json +66 -0
@@ -0,0 +1,235 @@
1
+ // The window's geometry: how big it is, where it is, and whether it fills the
2
+ // screen.
3
+ //
4
+ // React Native has no API for any of this, for the same reason it has no file
5
+ // dialog: on a phone there is one window, it is the screen, and nothing an app
6
+ // can say would change it. On a desktop it is the first thing an app wants
7
+ // after it has drawn something.
8
+ //
9
+ // A seam rather than three modules. The title bar next door is per-host because
10
+ // what it does differs per host -- GTK installs a header bar to have something
11
+ // to style, Windows draws its own caption buttons -- but a window's size is the
12
+ // same idea everywhere and only the call differs. So the JavaScript boundary is
13
+ // written once, in core/WindowMethods.h, and this is the four lines under it
14
+ // each platform answers.
15
+ //
16
+ // One window, which is the honest limit. Every function here means "the
17
+ // window", because the host makes exactly one; multiple windows are their own
18
+ // piece of work and are in docs/BACKLOG.md. When they arrive these grow a
19
+ // window argument and the module grows a handle, and nothing above changes
20
+ // shape.
21
+
22
+ #pragma once
23
+
24
+ #include <functional>
25
+ #include <vector>
26
+
27
+ namespace basalt {
28
+
29
+ struct WindowBounds {
30
+ // In logical pixels, in the desktop's coordinates -- which is what an app can
31
+ // pass straight back to `setWindowPosition`. Not physical pixels: an app
32
+ // reasoning about its own layout is reasoning in the same units React Native
33
+ // lays out in.
34
+ double x{0.0};
35
+ double y{0.0};
36
+ double width{0.0};
37
+ double height{0.0};
38
+
39
+ // Distinct states, not one enum: a window can be maximised *and* full screen
40
+ // on every one of these desktops, and an app that restores a remembered
41
+ // layout needs to know which it was.
42
+ bool fullScreen{false};
43
+ bool maximized{false};
44
+ };
45
+
46
+ // Where the window is now.
47
+ //
48
+ // **Called on the UI thread only.** Every one of these touches a GtkWindow, an
49
+ // NSWindow or an HWND, and none of those may be touched from the JavaScript
50
+ // thread -- which is where a TurboModule method arrives. `core/WindowMethods.h`
51
+ // is what marshals, so a platform implementation may assume it is already in
52
+ // the right place. Getting this wrong is quiet rather than loud: the AppKit
53
+ // resize simply did not happen, with no warning anywhere.
54
+ WindowBounds windowBounds();
55
+
56
+ void setWindowSize(double width, double height);
57
+ void setWindowPosition(double x, double y);
58
+
59
+ // Centred on the display the window is already on, which is not always the
60
+ // primary one.
61
+ void centerWindow();
62
+
63
+ void setWindowFullScreen(bool fullScreen);
64
+
65
+ // Called by the host whenever the window is resized, moved, or changes state.
66
+ //
67
+ // The module installs one of these and turns it into a device event, so that
68
+ // `useWindow()` can hand an app live bounds rather than a snapshot it has to
69
+ // remember to refresh. Replacing a listener drops the previous one; passing
70
+ // nothing removes it.
71
+ void setWindowBoundsListener(std::function<void(const WindowBounds &)> listener);
72
+
73
+ // The host's half of that: called from wherever it already learns the window
74
+ // moved or resized, on the UI thread. Also updates the cache below, so it is
75
+ // worth calling once when the window is first shown even if nothing is
76
+ // listening yet.
77
+ void notifyWindowBoundsChanged();
78
+
79
+ // How big the window is allowed to be, in logical pixels.
80
+ //
81
+ // Zero means "no limit" in either direction, which is the default and is also
82
+ // how an app clears one. Not part of `WindowBounds`: bounds are what the window
83
+ // *is* and these are what it may become, and an app restoring a remembered
84
+ // layout wants the first without being told the second.
85
+ struct WindowSizeLimits {
86
+ double minWidth{0.0};
87
+ double minHeight{0.0};
88
+ double maxWidth{0.0};
89
+ double maxHeight{0.0};
90
+ };
91
+
92
+ // Set the limits and apply them. Portable: the storing is identical everywhere
93
+ // and only `applyWindowSizeLimits` differs.
94
+ void setWindowMinimumSize(double width, double height);
95
+ void setWindowMaximumSize(double width, double height);
96
+
97
+ // What is in force, readable from any thread. Windows answers WM_GETMINMAXINFO
98
+ // from this, which is a message rather than a call and so cannot be given them.
99
+ WindowSizeLimits windowSizeLimits();
100
+
101
+ // Clamp a requested size to the limits this platform says it enforces.
102
+ //
103
+ // Portable, and applied before every `setWindowSize`, because the toolkits do
104
+ // not agree on whether a programmatic resize is subject to the same limits a
105
+ // dragged corner is. AppKit's `setFrame:` clamps down to the maximum and not up
106
+ // to the minimum, which is a resize that lands outside a limit an app set and
107
+ // the window manager would have refused -- found by the test for exactly that.
108
+ //
109
+ // Filtered by `windowCapabilities()` rather than applied unconditionally, so
110
+ // that what an app is told and what it gets stay the same thing: a desktop that
111
+ // reports `maximumSize: false` does not quietly enforce one here.
112
+ void constrainToWindowSizeLimits(double &width, double &height);
113
+
114
+ // Tell the window manager about the limits above. Called on the UI thread, with
115
+ // the new values already stored.
116
+ void applyWindowSizeLimits();
117
+
118
+ // Whether the person may resize the window at all.
119
+ void setWindowResizable(bool resizable);
120
+
121
+ // Whether it floats above other applications' windows. Not "above everything":
122
+ // no desktop lets an ordinary application outrank a screen lock or a system
123
+ // alert, and none of these ask it to.
124
+ void setWindowAlwaysOnTop(bool alwaysOnTop);
125
+
126
+ // Which of the above this desktop actually does.
127
+ //
128
+ // Answered rather than documented, because two of these are not gaps and will
129
+ // not be filled. GTK4 removed `gtk_window_set_geometry_hints` and
130
+ // `gtk_window_set_keep_above`, and Wayland has no protocol for either: a client
131
+ // there does not know where it is, does not place itself, and does not outrank
132
+ // its neighbours. An app that asks can put its "always on top" switch away on
133
+ // the platform that has no such thing, which is better than a switch that lies.
134
+ struct WindowCapabilities {
135
+ bool position{false};
136
+ bool minimumSize{false};
137
+ bool maximumSize{false};
138
+ bool resizable{false};
139
+ bool alwaysOnTop{false};
140
+ };
141
+
142
+ WindowCapabilities windowCapabilities();
143
+
144
+ // The last bounds the host reported, readable from any thread.
145
+ //
146
+ // `windowBounds()` cannot be: it asks the toolkit, and a TurboModule method
147
+ // runs on the JavaScript thread. So `getBounds()` answers from here, which is
148
+ // exact rather than approximate -- the host reports every change -- and needs
149
+ // no hop, which matters because it is read during render.
150
+ //
151
+ // All zeroes before the host has reported anything, which is what an app
152
+ // reading bounds during its first render sees.
153
+ WindowBounds lastKnownWindowBounds();
154
+
155
+ // --- The displays the desktop has -------------------------------------------
156
+ //
157
+ // React Native's `Dimensions` reports the window, which is the right answer to
158
+ // a different question: an app placing a window, or deciding how much it can
159
+ // show, is asking about the screen it is on and the ones beside it.
160
+ //
161
+ // The lookups already existed -- `NSScreen`, `GdkMonitor` and
162
+ // `MonitorFromWindow` are how `center()` and full screen work -- and were
163
+ // simply not passed on.
164
+
165
+ struct DisplayInfo {
166
+ // In the same logical pixels and the same desktop coordinates WindowBounds
167
+ // uses, so a bound and a display can be compared without converting either.
168
+ // Top-left origin on every host, which costs AppKit a flip: its own
169
+ // coordinates grow upwards from the primary screen.
170
+ double x{0.0};
171
+ double y{0.0};
172
+ double width{0.0};
173
+ double height{0.0};
174
+
175
+ // What is left after the desktop's own furniture -- a menu bar, a taskbar, a
176
+ // dock, a panel. Where a window should go; `bounds` is where the glass is.
177
+ // Equal to bounds on a desktop that reserves nothing.
178
+ double workX{0.0};
179
+ double workY{0.0};
180
+ double workWidth{0.0};
181
+ double workHeight{0.0};
182
+
183
+ // Physical pixels per logical pixel: 2 on a Retina display, 1 on an
184
+ // ordinary one, and fractional under Wayland or a Windows display set to
185
+ // 150%. Never zero -- a host that cannot answer reports 1, because a caller
186
+ // dividing by this should not have to check.
187
+ double scaleFactor{1.0};
188
+
189
+ // Exactly one display is primary, and it is the one the desktop's
190
+ // coordinates are anchored to.
191
+ bool primary{false};
192
+ };
193
+
194
+ // Every display, primary first.
195
+ //
196
+ // **Called on the UI thread only**, like everything above it: each host asks
197
+ // its toolkit.
198
+ std::vector<DisplayInfo> displays();
199
+
200
+ // The last list the host reported, readable from any thread.
201
+ //
202
+ // The same arrangement `lastKnownWindowBounds` has, and for the same reason:
203
+ // `displays()` asks the toolkit and a TurboModule method runs on the
204
+ // JavaScript thread. Exact rather than approximate, because the host reports
205
+ // every change -- and empty before the host has reported anything, which is
206
+ // what an app asking during its first render sees.
207
+ std::vector<DisplayInfo> lastKnownDisplays();
208
+
209
+ // Where the pointer is, in the same coordinates the bounds above use.
210
+ //
211
+ // A display question rather than a window one: it may be over a different
212
+ // display than the app's window, or over no window at all. All zeroes on a
213
+ // host that cannot answer -- Wayland does not tell a client where the pointer
214
+ // is unless it is over one of its own surfaces.
215
+ struct PointerPosition {
216
+ double x{0.0};
217
+ double y{0.0};
218
+ // False where the desktop will not say, so that an app can tell "at the
219
+ // origin" from "not answered".
220
+ bool known{false};
221
+ };
222
+
223
+ PointerPosition pointerPosition();
224
+
225
+ // A monitor plugged in, unplugged, or rearranged.
226
+ //
227
+ // No argument: an app that cares re-reads the list, which is the only way to
228
+ // be right about several changes arriving together. Same shape as the window
229
+ // bounds listener above, and set by the module for the same reason.
230
+ void setDisplaysListener(std::function<void()> listener);
231
+
232
+ // The host's half: called on the UI thread when the arrangement changes.
233
+ void notifyDisplaysChanged();
234
+
235
+ } // namespace basalt
@@ -0,0 +1,158 @@
1
+ // Opening a second window.
2
+ //
3
+ // Fabric has supported more than one surface all along -- `startSurface` takes
4
+ // a surface id, the mounting managers keep a root per id, and the error
5
+ // inspector has been a second surface since it was written. What has been
6
+ // missing is a second *window* to put one in: every host made exactly one, at
7
+ // startup, and everything in it reached for `the` window and `the` root.
8
+ //
9
+ // So this is the seam for the part that is genuinely per-toolkit -- making a
10
+ // window and wiring a surface into it -- and `core/WindowsModule.h` is the
11
+ // JavaScript boundary above it. The implementation lives in each host's
12
+ // `main_*.cpp` rather than beside the other platform services, because that is
13
+ // where a window is made and where the ReactHost that starts a surface lives.
14
+ //
15
+ // ## A window is a surface
16
+ //
17
+ // One window, one surface, one React root. That is Fabric's grain rather than a
18
+ // simplification: a surface is what has a size, a layout context and a root
19
+ // shadow node, and two windows sharing one would be two windows sharing a
20
+ // layout. It is also why the id a window is identified by *is* its surface id
21
+ // -- there is no second numbering to keep in step.
22
+ //
23
+ // The cost is that a second window is a second React tree: it does not share
24
+ // context, state or a render pass with the first. `<Window>` in JavaScript
25
+ // papers over enough of that to be useful and says where it cannot; see
26
+ // src/Window.js.
27
+
28
+ #pragma once
29
+
30
+ #include <folly/dynamic.h>
31
+
32
+ #include <functional>
33
+ #include <react/renderer/core/ReactPrimitives.h>
34
+
35
+ #include <string>
36
+ #include <vector>
37
+
38
+ namespace basalt {
39
+
40
+ struct NewWindowOptions {
41
+ // The component to run, as registered with `AppRegistry.registerComponent`.
42
+ // A window with no component is a window with nothing in it, which is not
43
+ // something to open by accident: an empty one is refused.
44
+ std::string component;
45
+ std::string title;
46
+ double width{900.0};
47
+ double height{700.0};
48
+ // `initialProps`, which is how the second React root is told which window it
49
+ // is. See src/Window.js.
50
+ folly::dynamic props{folly::dynamic::object()};
51
+ };
52
+
53
+ // Opens a window and starts `component` in it. Returns the new surface id,
54
+ // which is also the window's, or 0 if it could not.
55
+ //
56
+ // Called on the UI thread.
57
+ facebook::react::SurfaceId openHostWindow(const NewWindowOptions &options);
58
+
59
+ // Closes a window opened by `openHostWindow` and stops its surface. Ignores the
60
+ // main window: an app closing the window it is running in should say so through
61
+ // `close()` on the window itself, which is a different thing from destroying a
62
+ // surface out from under a React tree.
63
+ //
64
+ // Called on the UI thread.
65
+ void closeHostWindow(facebook::react::SurfaceId surfaceId);
66
+
67
+ // Ends the application: `[NSApp terminate:]`, `g_application_quit`,
68
+ // `PostQuitMessage`. What `quit()` reaches, and what a quit handler calls
69
+ // when its answer is "go ahead" -- an app that intercepted the quit refused
70
+ // the one the system asked for, so somebody has to ask again.
71
+ //
72
+ // Called on the UI thread.
73
+ void quitHost();
74
+
75
+ // Every window this host has open, main window first. For the tree dump, and
76
+ // for an app asking what it has.
77
+ std::vector<facebook::react::SurfaceId> hostWindows();
78
+
79
+ // A window was closed by the person rather than by the app -- its own close
80
+ // button, or the window manager.
81
+ //
82
+ // This is not a nicety. Without it the host is left holding a record whose
83
+ // window is gone, which is a dangling pointer rather than a stale flag, and the
84
+ // `<Window>` that opened it goes on believing it is open: the app's state says
85
+ // one thing and the screen says another, and the next render tries to close a
86
+ // window that has already closed itself.
87
+ //
88
+ // So the host calls `hostWindowClosed` on the way out, and the module turns it
89
+ // into a device event that `<Window>` unmounts on.
90
+ void setHostWindowClosedListener(std::function<void(facebook::react::SurfaceId)> listener);
91
+
92
+ // The host's half: called on the UI thread, before the record is dropped.
93
+ void hostWindowClosed(facebook::react::SurfaceId surfaceId);
94
+
95
+ // ## Refusing to close
96
+ //
97
+ // The above is a window that *has* closed. This is a window somebody is trying
98
+ // to close, which is a different question with a different answer: an app with
99
+ // unsaved work wants to ask before it goes, and by the time `hostWindowClosed`
100
+ // runs there is nothing left to ask about.
101
+ //
102
+ // Electron spells this `event.preventDefault()` in a `close` handler, and can,
103
+ // because its handler runs in the main process and the answer is available the
104
+ // moment the event is raised. Here the answer lives in JavaScript, on another
105
+ // thread, and the window manager wants a synchronous yes or no -- so the
106
+ // decision has to be made *before* the attempt rather than during it.
107
+ //
108
+ // Hence a flag rather than a callback. An app that registers a close handler
109
+ // marks its window intercepted; the host then refuses every close the window
110
+ // manager asks for and reports the attempt instead, and the app closes the
111
+ // window itself when it is ready. A window with no handler is not marked and
112
+ // closes exactly as it did before -- which matters, because that is every
113
+ // window, and a close path that waits on JavaScript to say "go ahead" would be
114
+ // a window that does not close when the runtime is busy.
115
+ void setHostWindowCloseIntercepted(facebook::react::SurfaceId surfaceId, bool intercepted);
116
+
117
+ // Whether this window refuses the window manager. Called on the UI thread from
118
+ // inside a close handler, which is why it may not hop to ask anybody.
119
+ bool hostWindowCloseIntercepted(facebook::react::SurfaceId surfaceId);
120
+
121
+ // Somebody tried to close a window that is intercepted, and it did not close.
122
+ // The module turns this into a device event; the app decides what to do and
123
+ // calls `close()` if that is "go ahead".
124
+ void setHostWindowCloseRequestListener(std::function<void(facebook::react::SurfaceId)> listener);
125
+
126
+ // The host's half: called on the UI thread, instead of closing.
127
+ void hostWindowCloseRequested(facebook::react::SurfaceId surfaceId);
128
+
129
+ // ## Refusing to quit
130
+ //
131
+ // The same question about the application rather than about one window, and
132
+ // not answerable by the above: an app can guard every window it owns and
133
+ // still lose the person's work to Cmd-Q. macOS routes quitting through
134
+ // `applicationShouldTerminate:` and asks no window whether it minds; the
135
+ // Windows and GNOME session-end signals do the same.
136
+ //
137
+ // A flag rather than a callback, for exactly the reason the close case is:
138
+ // the system wants a synchronous yes or no and the answer lives in
139
+ // JavaScript on another thread. An app that registers a quit handler marks
140
+ // the application intercepted; the host then refuses the quit and reports
141
+ // the attempt, and the app quits itself when it is ready.
142
+ //
143
+ // One flag rather than one per window, because quitting is one event no
144
+ // matter how many windows are open -- and because the last window closing is
145
+ // already a different thing that already works.
146
+ void setHostQuitIntercepted(bool intercepted);
147
+
148
+ // Whether the application refuses to quit. Called on the UI thread from
149
+ // inside a terminate handler, which is why it may not hop to ask anybody.
150
+ bool hostQuitIntercepted();
151
+
152
+ // Somebody tried to quit an intercepted application, and it did not quit.
153
+ void setHostQuitRequestListener(std::function<void()> listener);
154
+
155
+ // The host's half: called on the UI thread, instead of quitting.
156
+ void hostQuitRequested();
157
+
158
+ } // namespace basalt
@@ -0,0 +1,167 @@
1
+ #include "WindowMethods.h"
2
+
3
+ #include "PlatformServices.h"
4
+ #include "WindowControl.h"
5
+
6
+ #include <mutex>
7
+
8
+ namespace basalt {
9
+
10
+ namespace {
11
+
12
+ using facebook::jsi::Object;
13
+ using facebook::jsi::Runtime;
14
+ using facebook::jsi::Value;
15
+ using facebook::react::TurboModule;
16
+
17
+ bool twoNumbers(const Value *args, size_t count) {
18
+ return count >= 2 && args[0].isNumber() && args[1].isNumber();
19
+ }
20
+
21
+ // Everything that changes a window happens on the UI thread.
22
+ //
23
+ // A TurboModule method runs on the JavaScript thread, and none of the three
24
+ // toolkits may be touched from it. This was found the loud way and reported the
25
+ // quiet way: `setSize` from JavaScript did nothing at all on macOS, with no
26
+ // warning and no crash.
27
+ void onUiThread(std::function<void()> work) {
28
+ postToUiThread(std::move(work));
29
+ }
30
+
31
+ } // namespace
32
+
33
+ Value windowSetSize(Runtime & /*runtime*/,
34
+ TurboModule & /*module*/,
35
+ const Value *args,
36
+ size_t count) {
37
+ if (twoNumbers(args, count)) {
38
+ const double first = args[0].asNumber();
39
+ const double second = args[1].asNumber();
40
+ onUiThread([first, second] {
41
+ // Clamped here rather than left to the toolkit, which is the only way the
42
+ // three agree. See core/WindowControl.h.
43
+ double width = first;
44
+ double height = second;
45
+ constrainToWindowSizeLimits(width, height);
46
+ setWindowSize(width, height);
47
+ });
48
+ }
49
+ return Value::undefined();
50
+ }
51
+
52
+ Value windowSetPosition(Runtime & /*runtime*/,
53
+ TurboModule & /*module*/,
54
+ const Value *args,
55
+ size_t count) {
56
+ if (twoNumbers(args, count)) {
57
+ const double first = args[0].asNumber();
58
+ const double second = args[1].asNumber();
59
+ onUiThread([first, second] { setWindowPosition(first, second); });
60
+ }
61
+ return Value::undefined();
62
+ }
63
+
64
+ Value windowCenter(Runtime & /*runtime*/,
65
+ TurboModule & /*module*/,
66
+ const Value * /*args*/,
67
+ size_t /*count*/) {
68
+ onUiThread([] { centerWindow(); });
69
+ return Value::undefined();
70
+ }
71
+
72
+ Value windowSetFullScreen(Runtime & /*runtime*/,
73
+ TurboModule & /*module*/,
74
+ const Value *args,
75
+ size_t count) {
76
+ // Anything but a boolean is ignored rather than coerced: `setFullScreen()`
77
+ // with no argument reads as "make it full screen" to a JavaScript truthiness
78
+ // check and as a mistake to everyone else.
79
+ if (count >= 1 && args[0].isBool()) {
80
+ const bool fullScreen = args[0].asBool();
81
+ onUiThread([fullScreen] { setWindowFullScreen(fullScreen); });
82
+ }
83
+ return Value::undefined();
84
+ }
85
+
86
+ Value windowSetMinimumSize(Runtime & /*runtime*/,
87
+ TurboModule & /*module*/,
88
+ const Value *args,
89
+ size_t count) {
90
+ if (twoNumbers(args, count)) {
91
+ const double width = args[0].asNumber();
92
+ const double height = args[1].asNumber();
93
+ // The store is portable and thread-safe; what needs the UI thread is the
94
+ // applying, which is why the hop wraps the whole call rather than half of
95
+ // it. See core/WindowControl.h.
96
+ onUiThread([width, height] { setWindowMinimumSize(width, height); });
97
+ }
98
+ return Value::undefined();
99
+ }
100
+
101
+ Value windowSetMaximumSize(Runtime & /*runtime*/,
102
+ TurboModule & /*module*/,
103
+ const Value *args,
104
+ size_t count) {
105
+ if (twoNumbers(args, count)) {
106
+ const double width = args[0].asNumber();
107
+ const double height = args[1].asNumber();
108
+ onUiThread([width, height] { setWindowMaximumSize(width, height); });
109
+ }
110
+ return Value::undefined();
111
+ }
112
+
113
+ Value windowSetResizable(Runtime & /*runtime*/,
114
+ TurboModule & /*module*/,
115
+ const Value *args,
116
+ size_t count) {
117
+ if (count >= 1 && args[0].isBool()) {
118
+ const bool resizable = args[0].asBool();
119
+ onUiThread([resizable] { setWindowResizable(resizable); });
120
+ }
121
+ return Value::undefined();
122
+ }
123
+
124
+ Value windowSetAlwaysOnTop(Runtime & /*runtime*/,
125
+ TurboModule & /*module*/,
126
+ const Value *args,
127
+ size_t count) {
128
+ if (count >= 1 && args[0].isBool()) {
129
+ const bool onTop = args[0].asBool();
130
+ onUiThread([onTop] { setWindowAlwaysOnTop(onTop); });
131
+ }
132
+ return Value::undefined();
133
+ }
134
+
135
+ Value windowGetCapabilities(Runtime &runtime,
136
+ TurboModule & /*module*/,
137
+ const Value * /*args*/,
138
+ size_t /*count*/) {
139
+ const WindowCapabilities capabilities = windowCapabilities();
140
+ Object result(runtime);
141
+ result.setProperty(runtime, "position", capabilities.position);
142
+ result.setProperty(runtime, "minimumSize", capabilities.minimumSize);
143
+ result.setProperty(runtime, "maximumSize", capabilities.maximumSize);
144
+ result.setProperty(runtime, "resizable", capabilities.resizable);
145
+ result.setProperty(runtime, "alwaysOnTop", capabilities.alwaysOnTop);
146
+ return Value(runtime, result);
147
+ }
148
+
149
+ Value windowGetBounds(Runtime &runtime,
150
+ TurboModule & /*module*/,
151
+ const Value * /*args*/,
152
+ size_t /*count*/) {
153
+ // From the cache rather than from the toolkit: this runs on the JavaScript
154
+ // thread, and it is read during render, so it can neither ask AppKit nor wait
155
+ // for a hop. The host reports every change, so the cache is exact.
156
+ const WindowBounds bounds = lastKnownWindowBounds();
157
+ Object object(runtime);
158
+ object.setProperty(runtime, "x", bounds.x);
159
+ object.setProperty(runtime, "y", bounds.y);
160
+ object.setProperty(runtime, "width", bounds.width);
161
+ object.setProperty(runtime, "height", bounds.height);
162
+ object.setProperty(runtime, "fullScreen", bounds.fullScreen);
163
+ object.setProperty(runtime, "maximized", bounds.maximized);
164
+ return Value(runtime, object);
165
+ }
166
+
167
+ } // namespace basalt
@@ -0,0 +1,84 @@
1
+ // The JavaScript boundary for a window's geometry, written once.
2
+ //
3
+ // Each host's `BasaltWindow` module registers these beside its own title-bar
4
+ // methods. They are free functions with the signature a TurboModule method has,
5
+ // so a host installs one with a line:
6
+ //
7
+ // methodMap_["setSize"] = MethodMetadata{2, basalt::windowSetSize};
8
+ //
9
+ // Everything they do is in core/WindowControl.h, which is the part that differs
10
+ // per platform. Nothing here is per-platform at all, which is why it is not
11
+ // written three times -- the reading of arguments and the shape of the answer
12
+ // are exactly the things three copies would drift on.
13
+
14
+ #pragma once
15
+
16
+ #include <ReactCommon/TurboModule.h>
17
+
18
+ namespace basalt {
19
+
20
+ // `setSize(width, height)`. Ignores a call with anything but two numbers, which
21
+ // is what a JavaScript caller passing undefined looks like.
22
+ facebook::jsi::Value windowSetSize(facebook::jsi::Runtime &runtime,
23
+ facebook::react::TurboModule &module,
24
+ const facebook::jsi::Value *args,
25
+ size_t count);
26
+
27
+ // `setPosition(x, y)`, in the desktop's coordinates.
28
+ facebook::jsi::Value windowSetPosition(facebook::jsi::Runtime &runtime,
29
+ facebook::react::TurboModule &module,
30
+ const facebook::jsi::Value *args,
31
+ size_t count);
32
+
33
+ // `center()`, on whichever display the window is already on.
34
+ facebook::jsi::Value windowCenter(facebook::jsi::Runtime &runtime,
35
+ facebook::react::TurboModule &module,
36
+ const facebook::jsi::Value *args,
37
+ size_t count);
38
+
39
+ // `setFullScreen(boolean)`.
40
+ facebook::jsi::Value windowSetFullScreen(facebook::jsi::Runtime &runtime,
41
+ facebook::react::TurboModule &module,
42
+ const facebook::jsi::Value *args,
43
+ size_t count);
44
+
45
+ // `setMinimumSize(width, height)` and `setMaximumSize(width, height)`. Zero in
46
+ // either direction clears that limit.
47
+ facebook::jsi::Value windowSetMinimumSize(facebook::jsi::Runtime &runtime,
48
+ facebook::react::TurboModule &module,
49
+ const facebook::jsi::Value *args,
50
+ size_t count);
51
+ facebook::jsi::Value windowSetMaximumSize(facebook::jsi::Runtime &runtime,
52
+ facebook::react::TurboModule &module,
53
+ const facebook::jsi::Value *args,
54
+ size_t count);
55
+
56
+ // `setResizable(boolean)` and `setAlwaysOnTop(boolean)`.
57
+ facebook::jsi::Value windowSetResizable(facebook::jsi::Runtime &runtime,
58
+ facebook::react::TurboModule &module,
59
+ const facebook::jsi::Value *args,
60
+ size_t count);
61
+ facebook::jsi::Value windowSetAlwaysOnTop(facebook::jsi::Runtime &runtime,
62
+ facebook::react::TurboModule &module,
63
+ const facebook::jsi::Value *args,
64
+ size_t count);
65
+
66
+ // `getCapabilities()` -> which of the above this desktop does. Read during
67
+ // render, so it answers without a thread hop -- it is five booleans a platform
68
+ // knows at compile time, not a question for the window manager.
69
+ facebook::jsi::Value windowGetCapabilities(facebook::jsi::Runtime &runtime,
70
+ facebook::react::TurboModule &module,
71
+ const facebook::jsi::Value *args,
72
+ size_t count);
73
+
74
+ // `getBounds()` -> `{x, y, width, height, fullScreen, maximized}`.
75
+ facebook::jsi::Value windowGetBounds(facebook::jsi::Runtime &runtime,
76
+ facebook::react::TurboModule &module,
77
+ const facebook::jsi::Value *args,
78
+ size_t count);
79
+
80
+ // The device event `useWindow()` listens for. Named here so the two sides
81
+ // cannot drift.
82
+ inline constexpr const char *kWindowBoundsEvent = "basaltWindowBoundsChanged";
83
+
84
+ } // namespace basalt