basalt-win32 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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/cli/runWindows.js +35 -0
  3. package/cli/shared.js +75 -0
  4. package/native/CMakeLists.txt +398 -0
  5. package/native/tests/test_win32_accessibility.cpp +271 -0
  6. package/native/tests/test_win32_clipboard.cpp +121 -0
  7. package/native/tests/test_win32_focus.cpp +195 -0
  8. package/native/tests/test_win32_hittest.cpp +410 -0
  9. package/native/tests/test_win32_image.cpp +239 -0
  10. package/native/tests/test_win32_imageloader.cpp +208 -0
  11. package/native/tests/test_win32_input.cpp +361 -0
  12. package/native/tests/test_win32_key_events.cpp +84 -0
  13. package/native/tests/test_win32_mounting.cpp +410 -0
  14. package/native/tests/test_win32_paint.cpp +505 -0
  15. package/native/tests/test_win32_scrollview.cpp +385 -0
  16. package/native/tests/test_win32_text.cpp +258 -0
  17. package/native/tests/test_win32_textinput.cpp +609 -0
  18. package/native/tests/test_win32_titlebar.cpp +111 -0
  19. package/native/tests/test_win32_titlebar_actions.cpp +62 -0
  20. package/native/tests/test_win32_view.cpp +222 -0
  21. package/native/win32/ColorSchemeWin32.cpp +56 -0
  22. package/native/win32/ComponentRegistryWin32.cpp +124 -0
  23. package/native/win32/DirectWriteLayout.cpp +116 -0
  24. package/native/win32/DirectWriteLayout.h +36 -0
  25. package/native/win32/DirectWriteLayoutManager.cpp +124 -0
  26. package/native/win32/FontRegistryDirectWrite.cpp +117 -0
  27. package/native/win32/PlatformServicesWin32.cpp +643 -0
  28. package/native/win32/RnWin32Accessible.cpp +258 -0
  29. package/native/win32/RnWin32Accessible.h +91 -0
  30. package/native/win32/RnWin32Image.cpp +265 -0
  31. package/native/win32/RnWin32Image.h +93 -0
  32. package/native/win32/RnWin32TextLayout.cpp +310 -0
  33. package/native/win32/RnWin32TextLayout.h +160 -0
  34. package/native/win32/RnWin32View.cpp +1281 -0
  35. package/native/win32/RnWin32View.h +506 -0
  36. package/native/win32/Win32AnimationChoreographer.cpp +76 -0
  37. package/native/win32/Win32AnimationChoreographer.h +61 -0
  38. package/native/win32/Win32Clip.h +173 -0
  39. package/native/win32/Win32DropTarget.cpp +567 -0
  40. package/native/win32/Win32DropTarget.h +42 -0
  41. package/native/win32/Win32Focus.cpp +262 -0
  42. package/native/win32/Win32Focus.h +109 -0
  43. package/native/win32/Win32ImageLoader.cpp +167 -0
  44. package/native/win32/Win32ImageLoader.h +104 -0
  45. package/native/win32/Win32KeyEvents.cpp +126 -0
  46. package/native/win32/Win32KeyEvents.h +20 -0
  47. package/native/win32/Win32MenuBar.cpp +352 -0
  48. package/native/win32/Win32MenuBar.h +16 -0
  49. package/native/win32/Win32MountingManager.cpp +727 -0
  50. package/native/win32/Win32MountingManager.h +203 -0
  51. package/native/win32/Win32Packaging.cpp +161 -0
  52. package/native/win32/Win32Packaging.h +56 -0
  53. package/native/win32/Win32RunLoopObserver.cpp +48 -0
  54. package/native/win32/Win32RunLoopObserver.h +42 -0
  55. package/native/win32/Win32ScrollView.cpp +515 -0
  56. package/native/win32/Win32ScrollView.h +208 -0
  57. package/native/win32/Win32SkiaContext.cpp +221 -0
  58. package/native/win32/Win32SkiaContext.h +53 -0
  59. package/native/win32/Win32SkiaModule.cpp +80 -0
  60. package/native/win32/Win32SkiaModule.h +50 -0
  61. package/native/win32/Win32SkiaPeer.cpp +358 -0
  62. package/native/win32/Win32SkiaPeer.h +66 -0
  63. package/native/win32/Win32SkiaUnicode.cpp +42 -0
  64. package/native/win32/Win32Snapshot.cpp +201 -0
  65. package/native/win32/Win32Snapshot.h +66 -0
  66. package/native/win32/Win32Strings.h +54 -0
  67. package/native/win32/Win32TextInput.cpp +811 -0
  68. package/native/win32/Win32TextInput.h +232 -0
  69. package/native/win32/Win32TitleBar.cpp +562 -0
  70. package/native/win32/Win32TitleBar.h +146 -0
  71. package/native/win32/Win32TitleBarLayout.cpp +46 -0
  72. package/native/win32/Win32TitleBarLayout.h +58 -0
  73. package/native/win32/Win32TouchDispatcher.cpp +443 -0
  74. package/native/win32/Win32TouchDispatcher.h +153 -0
  75. package/native/win32/Win32UiThread.h +27 -0
  76. package/native/win32/Win32WindowControl.cpp +329 -0
  77. package/native/win32/Win32WindowModule.cpp +209 -0
  78. package/native/win32/Win32WindowModule.h +72 -0
  79. package/native/win32/demo_layout_win32.cpp +232 -0
  80. package/native/win32/main_win32.cpp +2481 -0
  81. package/native/win32/mount_harness_win32.cpp +316 -0
  82. package/package.json +26 -0
  83. package/react-native.config.js +22 -0
@@ -0,0 +1,316 @@
1
+ // Drives real ShadowViewMutations through the real Win32MountingManager into
2
+ // real views, with no JS runtime, no Hermes and no Metro.
3
+ //
4
+ // The mirror of gtk/mount_harness_gtk.cpp and appkit/mount_harness_appkit.mm,
5
+ // with the same two transactions and the same boxes, which is the point: all
6
+ // three platforms share their mutation walk, and the cheapest way to notice
7
+ // that sharing has broken is to put the three pictures side by side.
8
+ //
9
+ // This is the checkpoint between "the mounting manager compiles" and "React
10
+ // Native runs". A mistake in the Create/Insert/Update/Remove/Delete handling
11
+ // shows up here as a picture rather than as something to be found by review.
12
+ //
13
+ // With BASALT_SNAPSHOT_DIR set it renders both transactions to PNGs and exits.
14
+ // Without it, it opens a window and applies the second transaction after two
15
+ // seconds, the way the other two harnesses do.
16
+
17
+ #include "Win32MountingManager.h"
18
+ #include "Win32Snapshot.h"
19
+
20
+ #include <react/renderer/components/view/ViewProps.h>
21
+ #include <react/renderer/core/LayoutMetrics.h>
22
+ #include <react/renderer/graphics/Color.h>
23
+ #include <react/renderer/mounting/MountingTransaction.h>
24
+ #include <react/renderer/mounting/ShadowViewMutation.h>
25
+
26
+ #include <windows.h>
27
+
28
+ #include <d2d1.h>
29
+ #include <wrl/client.h>
30
+
31
+ #include <cstdio>
32
+ #include <cstdlib>
33
+ #include <memory>
34
+ #include <string>
35
+ #include <utility>
36
+
37
+ using Microsoft::WRL::ComPtr;
38
+ using basalt::Win32MountingManager;
39
+ using basalt::win32::RnWin32View;
40
+ using facebook::react::ColorComponents;
41
+ using facebook::react::LayoutMetrics;
42
+ using facebook::react::MountingTransaction;
43
+ using facebook::react::ShadowView;
44
+ using facebook::react::ShadowViewMutation;
45
+ using facebook::react::ShadowViewMutationList;
46
+ using facebook::react::SurfaceId;
47
+ using facebook::react::Tag;
48
+ using facebook::react::TransactionTelemetry;
49
+ using facebook::react::ViewProps;
50
+
51
+ namespace {
52
+
53
+ constexpr SurfaceId kSurfaceId = 1;
54
+
55
+ ShadowView makeShadowView(Tag tag,
56
+ float x,
57
+ float y,
58
+ float width,
59
+ float height,
60
+ float red,
61
+ float green,
62
+ float blue,
63
+ float opacity = 1.0f) {
64
+ auto props = std::make_shared<ViewProps>();
65
+ props->backgroundColor = facebook::react::colorFromComponents(
66
+ ColorComponents{.red = red, .green = green, .blue = blue, .alpha = 1.0f});
67
+ props->opacity = opacity;
68
+
69
+ LayoutMetrics layoutMetrics;
70
+ layoutMetrics.frame = {.origin = {.x = x, .y = y},
71
+ .size = {.width = width, .height = height}};
72
+
73
+ ShadowView shadowView;
74
+ shadowView.componentName = "View";
75
+ shadowView.surfaceId = kSurfaceId;
76
+ shadowView.tag = tag;
77
+ shadowView.props = props;
78
+ shadowView.layoutMetrics = layoutMetrics;
79
+ return shadowView;
80
+ }
81
+
82
+ MountingTransaction makeTransaction(MountingTransaction::Number number,
83
+ ShadowViewMutationList &&mutations) {
84
+ return MountingTransaction(kSurfaceId, number, std::move(mutations), TransactionTelemetry{});
85
+ }
86
+
87
+ // First transaction: create four views and place them, one of them nested.
88
+ ShadowViewMutationList firstTransaction() {
89
+ ShadowViewMutationList mutations;
90
+
91
+ const ShadowView blue = makeShadowView(2, 32, 32, 240, 160, 0.30f, 0.55f, 0.95f);
92
+ const ShadowView orange = makeShadowView(3, 296, 32, 240, 160, 0.95f, 0.45f, 0.35f);
93
+ const ShadowView green = makeShadowView(4, 32, 224, 504, 140, 0.35f, 0.80f, 0.55f);
94
+ const ShadowView nested = makeShadowView(5, 24, 24, 120, 90, 1.0f, 1.0f, 1.0f, 0.85f);
95
+
96
+ // Fabric emits every Create before the Inserts that place them.
97
+ mutations.push_back(ShadowViewMutation::CreateMutation(blue));
98
+ mutations.push_back(ShadowViewMutation::CreateMutation(orange));
99
+ mutations.push_back(ShadowViewMutation::CreateMutation(green));
100
+ mutations.push_back(ShadowViewMutation::CreateMutation(nested));
101
+
102
+ mutations.push_back(ShadowViewMutation::InsertMutation(kSurfaceId, blue, 0));
103
+ mutations.push_back(ShadowViewMutation::InsertMutation(kSurfaceId, orange, 1));
104
+ mutations.push_back(ShadowViewMutation::InsertMutation(kSurfaceId, green, 2));
105
+ mutations.push_back(ShadowViewMutation::InsertMutation(blue.tag, nested, 0));
106
+
107
+ return mutations;
108
+ }
109
+
110
+ // Second transaction: recolour one view, and remove another entirely. Fabric
111
+ // always emits Remove before Delete, and this reproduces that ordering.
112
+ ShadowViewMutationList secondTransaction() {
113
+ ShadowViewMutationList mutations;
114
+
115
+ // Update: same tag, new props. Recolour blue -> purple and shrink.
116
+ const ShadowView oldBlue = makeShadowView(2, 32, 32, 240, 160, 0.30f, 0.55f, 0.95f);
117
+ const ShadowView newBlue = makeShadowView(2, 32, 32, 240, 100, 0.60f, 0.35f, 0.90f);
118
+ mutations.push_back(ShadowViewMutation::UpdateMutation(oldBlue, newBlue, kSurfaceId));
119
+
120
+ // Remove then Delete, the order Fabric guarantees.
121
+ const ShadowView orange = makeShadowView(3, 296, 32, 240, 160, 0.95f, 0.45f, 0.35f);
122
+ mutations.push_back(ShadowViewMutation::RemoveMutation(kSurfaceId, orange, 1));
123
+ mutations.push_back(ShadowViewMutation::DeleteMutation(orange));
124
+
125
+ return mutations;
126
+ }
127
+
128
+ RnWin32View *makeRoot(Win32MountingManager &manager) {
129
+ // The host owns the root: Fabric never emits a Create for it, because the
130
+ // root shadow node is the base of every diff.
131
+ RnWin32View *root = manager.createSurfaceRoot(kSurfaceId);
132
+ root->setFrame(0, 0, 640, 420);
133
+ root->setBackgroundColor(0.12f, 0.13f, 0.16f, 1.0f, true);
134
+ return root;
135
+ }
136
+
137
+ int runSnapshots(Win32MountingManager &manager, const std::string &directory) {
138
+ RnWin32View *root = makeRoot(manager);
139
+
140
+ // executeMount posts to the UI thread. With no host installed there is no
141
+ // message loop and postToUiThread runs the work inline, so nothing has to be
142
+ // drained here -- unlike the AppKit harness, which spins its run loop. Worth
143
+ // stating rather than looking like an omission.
144
+ manager.executeMount(kSurfaceId, makeTransaction(1, firstTransaction()));
145
+ if (!basalt::win32::writeSnapshot(*root, directory + "/mount-1.png")) {
146
+ std::fputs("could not write mount-1.png\n", stderr);
147
+ return 1;
148
+ }
149
+ std::fputs(root->describeTree().c_str(), stdout);
150
+
151
+ manager.executeMount(kSurfaceId, makeTransaction(2, secondTransaction()));
152
+ if (!basalt::win32::writeSnapshot(*root, directory + "/mount-2.png")) {
153
+ std::fputs("could not write mount-2.png\n", stderr);
154
+ return 1;
155
+ }
156
+ std::fputs("---\n", stdout);
157
+ std::fputs(root->describeTree().c_str(), stdout);
158
+
159
+ manager.destroySurfaceRoot(kSurfaceId);
160
+ return 0;
161
+ }
162
+
163
+ // --- the windowed mode -------------------------------------------------------
164
+
165
+ struct HarnessWindow {
166
+ Win32MountingManager *manager = nullptr;
167
+ RnWin32View *root = nullptr;
168
+ ComPtr<ID2D1Factory> factory;
169
+ ComPtr<ID2D1HwndRenderTarget> target;
170
+ };
171
+
172
+ constexpr UINT_PTR kSecondTransactionTimer = 1;
173
+
174
+ bool ensureTarget(HarnessWindow *harness, HWND hwnd) {
175
+ if (harness->target) {
176
+ return true;
177
+ }
178
+ RECT client{};
179
+ GetClientRect(hwnd, &client);
180
+ const D2D1_SIZE_U size = D2D1::SizeU(static_cast<UINT32>(client.right - client.left),
181
+ static_cast<UINT32>(client.bottom - client.top));
182
+ if (size.width == 0 || size.height == 0) {
183
+ return false;
184
+ }
185
+ return SUCCEEDED(harness->factory->CreateHwndRenderTarget(
186
+ D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwnd, size),
187
+ &harness->target));
188
+ }
189
+
190
+ LRESULT CALLBACK harnessProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
191
+ auto *harness = reinterpret_cast<HarnessWindow *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
192
+
193
+ switch (message) {
194
+ case WM_CREATE: {
195
+ auto *create = reinterpret_cast<CREATESTRUCT *>(lparam);
196
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(create->lpCreateParams));
197
+ // Two seconds, then the second transaction -- the same beat the other two
198
+ // harnesses use, so the three can be watched side by side.
199
+ SetTimer(hwnd, kSecondTransactionTimer, 2000, nullptr);
200
+ return 0;
201
+ }
202
+
203
+ case WM_TIMER:
204
+ if (wparam == kSecondTransactionTimer && harness != nullptr) {
205
+ KillTimer(hwnd, kSecondTransactionTimer);
206
+ harness->manager->executeMount(kSurfaceId, makeTransaction(2, secondTransaction()));
207
+ std::fputs("---\n", stdout);
208
+ std::fputs(harness->root->describeTree().c_str(), stdout);
209
+ InvalidateRect(hwnd, nullptr, FALSE);
210
+ }
211
+ return 0;
212
+
213
+ case WM_SIZE:
214
+ if (harness != nullptr && harness->target) {
215
+ harness->target->Resize(D2D1::SizeU(LOWORD(lparam), HIWORD(lparam)));
216
+ }
217
+ return 0;
218
+
219
+ case WM_PAINT: {
220
+ PAINTSTRUCT paint{};
221
+ BeginPaint(hwnd, &paint);
222
+ if (harness != nullptr && ensureTarget(harness, hwnd)) {
223
+ harness->target->BeginDraw();
224
+ harness->target->Clear(D2D1::ColorF(D2D1::ColorF::Black));
225
+ harness->target->SetTransform(D2D1::Matrix3x2F::Identity());
226
+ harness->root->paint(harness->target.Get());
227
+ if (harness->target->EndDraw() == D2DERR_RECREATE_TARGET) {
228
+ harness->target.Reset();
229
+ }
230
+ }
231
+ EndPaint(hwnd, &paint);
232
+ return 0;
233
+ }
234
+
235
+ case WM_DESTROY:
236
+ PostQuitMessage(0);
237
+ return 0;
238
+
239
+ default:
240
+ break;
241
+ }
242
+ return DefWindowProc(hwnd, message, wparam, lparam);
243
+ }
244
+
245
+ int runWindow(Win32MountingManager &manager) {
246
+ HarnessWindow harness;
247
+ harness.manager = &manager;
248
+ harness.root = makeRoot(manager);
249
+ if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
250
+ harness.factory.GetAddressOf()))) {
251
+ std::fputs("could not create a Direct2D factory\n", stderr);
252
+ return 1;
253
+ }
254
+
255
+ const HINSTANCE instance = GetModuleHandle(nullptr);
256
+ WNDCLASSEX windowClass{};
257
+ windowClass.cbSize = sizeof(windowClass);
258
+ windowClass.style = CS_HREDRAW | CS_VREDRAW;
259
+ windowClass.lpfnWndProc = harnessProc;
260
+ windowClass.hInstance = instance;
261
+ windowClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
262
+ windowClass.lpszClassName = L"BasaltMountHarness";
263
+ if (RegisterClassEx(&windowClass) == 0) {
264
+ std::fputs("could not register the window class\n", stderr);
265
+ return 1;
266
+ }
267
+
268
+ RECT wanted{0, 0, 640, 420};
269
+ AdjustWindowRect(&wanted, WS_OVERLAPPEDWINDOW, FALSE);
270
+
271
+ const HWND hwnd = CreateWindowEx(0,
272
+ windowClass.lpszClassName,
273
+ L"basalt-core \x2014 Windows mount harness",
274
+ WS_OVERLAPPEDWINDOW,
275
+ CW_USEDEFAULT,
276
+ CW_USEDEFAULT,
277
+ wanted.right - wanted.left,
278
+ wanted.bottom - wanted.top,
279
+ nullptr,
280
+ nullptr,
281
+ instance,
282
+ &harness);
283
+ if (hwnd == nullptr) {
284
+ std::fputs("could not create the window\n", stderr);
285
+ return 1;
286
+ }
287
+
288
+ manager.executeMount(kSurfaceId, makeTransaction(1, firstTransaction()));
289
+ std::fputs(harness.root->describeTree().c_str(), stdout);
290
+
291
+ ShowWindow(hwnd, SW_SHOWNORMAL);
292
+ UpdateWindow(hwnd);
293
+
294
+ MSG message{};
295
+ while (GetMessage(&message, nullptr, 0, 0) > 0) {
296
+ TranslateMessage(&message);
297
+ DispatchMessage(&message);
298
+ }
299
+ return static_cast<int>(message.wParam);
300
+ }
301
+
302
+ } // namespace
303
+
304
+ int main(int argc, char **argv) {
305
+ (void)argc;
306
+ (void)argv;
307
+
308
+ // Constructed on this thread: MountingWalk records it and asserts every
309
+ // transaction arrives on the same one.
310
+ Win32MountingManager manager;
311
+
312
+ if (const char *directory = std::getenv("BASALT_SNAPSHOT_DIR")) {
313
+ return runSnapshots(manager, directory);
314
+ }
315
+ return runWindow(manager);
316
+ }
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "basalt-win32",
3
+ "version": "0.0.0-rc5",
4
+ "description": "The Windows view layer for basalt-core: a Win32 and Direct2D view layer, a Fabric mounting manager, and a host. Everything behind them is in basalt-core.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/vincentvella/basalt-core.git",
9
+ "directory": "packages/basalt-win32"
10
+ },
11
+ "exports": {
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "cli",
16
+ "native",
17
+ "react-native.config.js"
18
+ ],
19
+ "peerDependencies": {
20
+ "react": "*",
21
+ "react-native": "*"
22
+ },
23
+ "dependencies": {
24
+ "basalt-core": "0.0.0-rc5"
25
+ }
26
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * What React Native's CLI reads out of this package.
3
+ *
4
+ * Just the `run-windows` command. The platform *declarations* -- which make
5
+ * `--platform` accept this one at all -- are in basalt-core, because
6
+ * they are the same on every desktop and an app targeting only one of them
7
+ * still needs them.
8
+ *
9
+ * The CLI reads a react-native.config.js out of every dependency and merges
10
+ * them, so an app with more than one of these packages installed gets the
11
+ * platforms from the shared one and a command from each of the others, without
12
+ * any of them knowing about the rest.
13
+ *
14
+ * @format
15
+ */
16
+
17
+ // @ts-check
18
+ 'use strict';
19
+
20
+ module.exports = {
21
+ commands: [require('./cli/runWindows')],
22
+ };