basalt-win32 0.0.0-stage → 0.2.0

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 (90) 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 +429 -0
  5. package/native/tests/test_win32_accessibility.cpp +585 -0
  6. package/native/tests/test_win32_animated_image.cpp +351 -0
  7. package/native/tests/test_win32_clipboard.cpp +121 -0
  8. package/native/tests/test_win32_focus.cpp +195 -0
  9. package/native/tests/test_win32_hittest.cpp +750 -0
  10. package/native/tests/test_win32_image.cpp +370 -0
  11. package/native/tests/test_win32_imageloader.cpp +208 -0
  12. package/native/tests/test_win32_input.cpp +511 -0
  13. package/native/tests/test_win32_key_events.cpp +84 -0
  14. package/native/tests/test_win32_mounting.cpp +757 -0
  15. package/native/tests/test_win32_paint.cpp +1799 -0
  16. package/native/tests/test_win32_scrollview.cpp +647 -0
  17. package/native/tests/test_win32_selection.cpp +215 -0
  18. package/native/tests/test_win32_text.cpp +1456 -0
  19. package/native/tests/test_win32_textinput.cpp +1594 -0
  20. package/native/tests/test_win32_titlebar.cpp +111 -0
  21. package/native/tests/test_win32_titlebar_actions.cpp +62 -0
  22. package/native/tests/test_win32_view.cpp +222 -0
  23. package/native/win32/ColorSchemeWin32.cpp +56 -0
  24. package/native/win32/ComponentRegistryWin32.cpp +156 -0
  25. package/native/win32/DirectWriteLayout.cpp +485 -0
  26. package/native/win32/DirectWriteLayout.h +77 -0
  27. package/native/win32/DirectWriteLayoutManager.cpp +154 -0
  28. package/native/win32/FontRegistryDirectWrite.cpp +117 -0
  29. package/native/win32/PlatformServicesWin32.cpp +744 -0
  30. package/native/win32/RnWin32Accessible.cpp +343 -0
  31. package/native/win32/RnWin32Accessible.h +130 -0
  32. package/native/win32/RnWin32Image.cpp +588 -0
  33. package/native/win32/RnWin32Image.h +139 -0
  34. package/native/win32/RnWin32TextLayout.cpp +1379 -0
  35. package/native/win32/RnWin32TextLayout.h +390 -0
  36. package/native/win32/RnWin32View.cpp +2956 -0
  37. package/native/win32/RnWin32View.h +926 -0
  38. package/native/win32/Win32AnimationChoreographer.cpp +76 -0
  39. package/native/win32/Win32AnimationChoreographer.h +61 -0
  40. package/native/win32/Win32Clip.h +209 -0
  41. package/native/win32/Win32Cursors.cpp +309 -0
  42. package/native/win32/Win32Cursors.h +121 -0
  43. package/native/win32/Win32DropTarget.cpp +567 -0
  44. package/native/win32/Win32DropTarget.h +42 -0
  45. package/native/win32/Win32Focus.cpp +262 -0
  46. package/native/win32/Win32Focus.h +109 -0
  47. package/native/win32/Win32ImageLoader.cpp +199 -0
  48. package/native/win32/Win32ImageLoader.h +133 -0
  49. package/native/win32/Win32InputScopes.h +92 -0
  50. package/native/win32/Win32KeyEvents.cpp +126 -0
  51. package/native/win32/Win32KeyEvents.h +20 -0
  52. package/native/win32/Win32MenuBar.cpp +382 -0
  53. package/native/win32/Win32MenuBar.h +16 -0
  54. package/native/win32/Win32MountingManager.cpp +1282 -0
  55. package/native/win32/Win32MountingManager.h +243 -0
  56. package/native/win32/Win32Offscreen.h +53 -0
  57. package/native/win32/Win32Packaging.cpp +161 -0
  58. package/native/win32/Win32Packaging.h +56 -0
  59. package/native/win32/Win32RunLoopObserver.cpp +48 -0
  60. package/native/win32/Win32RunLoopObserver.h +42 -0
  61. package/native/win32/Win32ScrollView.cpp +718 -0
  62. package/native/win32/Win32ScrollView.h +256 -0
  63. package/native/win32/Win32SkiaContext.cpp +221 -0
  64. package/native/win32/Win32SkiaContext.h +53 -0
  65. package/native/win32/Win32SkiaModule.cpp +80 -0
  66. package/native/win32/Win32SkiaModule.h +50 -0
  67. package/native/win32/Win32SkiaPeer.cpp +358 -0
  68. package/native/win32/Win32SkiaPeer.h +66 -0
  69. package/native/win32/Win32SkiaUnicode.cpp +42 -0
  70. package/native/win32/Win32Snapshot.cpp +201 -0
  71. package/native/win32/Win32Snapshot.h +66 -0
  72. package/native/win32/Win32Strings.h +54 -0
  73. package/native/win32/Win32TextInput.cpp +1296 -0
  74. package/native/win32/Win32TextInput.h +341 -0
  75. package/native/win32/Win32TitleBar.cpp +562 -0
  76. package/native/win32/Win32TitleBar.h +146 -0
  77. package/native/win32/Win32TitleBarLayout.cpp +46 -0
  78. package/native/win32/Win32TitleBarLayout.h +58 -0
  79. package/native/win32/Win32TouchDispatcher.cpp +577 -0
  80. package/native/win32/Win32TouchDispatcher.h +201 -0
  81. package/native/win32/Win32UiThread.h +27 -0
  82. package/native/win32/Win32WindowControl.cpp +329 -0
  83. package/native/win32/Win32WindowModule.cpp +209 -0
  84. package/native/win32/Win32WindowModule.h +72 -0
  85. package/native/win32/demo_layout_win32.cpp +232 -0
  86. package/native/win32/main_win32.cpp +2722 -0
  87. package/native/win32/mount_harness_win32.cpp +316 -0
  88. package/package.json +25 -4
  89. package/react-native.config.js +22 -0
  90. package/README.md +0 -3
@@ -0,0 +1,382 @@
1
+ // The Windows half of core/MenuModel.h: a real menu bar, in the window.
2
+ //
3
+ // Windows is the middle case. A menu bar is conventional rather than required,
4
+ // it lives inside the window rather than in a system bar, and -- unlike macOS
5
+ // -- nothing about editing depends on it: an EDIT control implements Ctrl+C,
6
+ // Ctrl+V and Ctrl+Z itself, so the Edit menu is a convenience rather than the
7
+ // thing that makes copying work.
8
+ //
9
+ // Which is why the roles here are posted to the focused control rather than
10
+ // sent down a responder chain. Windows has no responder chain; what it has is
11
+ // `WM_COPY` and friends, which every standard edit control answers and which
12
+ // reach whichever one has focus.
13
+ //
14
+ // The host forwards `WM_COMMAND` here. A menu bar costs client area, so
15
+ // installing one resizes the surface -- which the host already handles, because
16
+ // the same thing happens when a window is resized for any other reason.
17
+
18
+ #include "Win32MenuBar.h"
19
+ #include "TextSelection.h"
20
+
21
+ #include "MenuModel.h"
22
+ #include "WindowControl.h"
23
+ #include "Win32Strings.h"
24
+
25
+ #include <windows.h>
26
+
27
+ #include <iterator>
28
+ #include <string>
29
+ #include <utility>
30
+ #include <vector>
31
+
32
+ namespace basalt {
33
+
34
+ namespace {
35
+
36
+ // Command ids. An app's own item carries its own id and a role needs one of
37
+ // these, so they are kept apart by range: an app's ids come from JavaScript and
38
+ // start at 1, and roles start high enough that the two cannot meet.
39
+ constexpr UINT kRoleBase = 0xE000;
40
+
41
+ struct Role {
42
+ const char *name;
43
+ UINT command;
44
+ };
45
+
46
+ constexpr Role kRoles[] = {
47
+ {"quit", kRoleBase + 1},
48
+ {"undo", kRoleBase + 2},
49
+ {"redo", kRoleBase + 3},
50
+ {"cut", kRoleBase + 4},
51
+ {"copy", kRoleBase + 5},
52
+ {"paste", kRoleBase + 6},
53
+ {"delete", kRoleBase + 7},
54
+ {"selectAll", kRoleBase + 8},
55
+ {"minimize", kRoleBase + 9},
56
+ {"zoom", kRoleBase + 10},
57
+ {"close", kRoleBase + 11},
58
+ {"togglefullscreen", kRoleBase + 12},
59
+ // "about" has no standard Windows action. An app that wants one shows its
60
+ // own window, which is an app's own item rather than a role.
61
+ };
62
+
63
+ UINT commandForRole(const std::string &role) {
64
+ for (const Role &known : kRoles) {
65
+ if (role == known.name) {
66
+ return known.command;
67
+ }
68
+ }
69
+ return 0;
70
+ }
71
+
72
+ // The platform's own word, plus the shortcut it answers to. Windows draws an
73
+ // accelerator as the text after a tab; nothing here registers one, because the
74
+ // EDIT control already answers these keys.
75
+ std::wstring labelForRole(const std::string &role, const std::string &label) {
76
+ if (!label.empty()) {
77
+ return win32::widen(label);
78
+ }
79
+ if (role == "quit") {
80
+ return L"Exit";
81
+ }
82
+ if (role == "undo") {
83
+ return L"Undo\tCtrl+Z";
84
+ }
85
+ if (role == "redo") {
86
+ return L"Redo\tCtrl+Y";
87
+ }
88
+ if (role == "cut") {
89
+ return L"Cut\tCtrl+X";
90
+ }
91
+ if (role == "copy") {
92
+ return L"Copy\tCtrl+C";
93
+ }
94
+ if (role == "paste") {
95
+ return L"Paste\tCtrl+V";
96
+ }
97
+ if (role == "delete") {
98
+ return L"Delete";
99
+ }
100
+ if (role == "selectAll") {
101
+ return L"Select All\tCtrl+A";
102
+ }
103
+ if (role == "minimize") {
104
+ return L"Minimize";
105
+ }
106
+ if (role == "zoom") {
107
+ return L"Maximize";
108
+ }
109
+ if (role == "close") {
110
+ return L"Close";
111
+ }
112
+ if (role == "togglefullscreen") {
113
+ return L"Full Screen";
114
+ }
115
+ return {};
116
+ }
117
+
118
+ std::function<void(int)> &handler() {
119
+ static std::function<void(int)> value;
120
+ return value;
121
+ }
122
+
123
+ HMENU &installed() {
124
+ static HMENU menu = nullptr;
125
+ return menu;
126
+ }
127
+
128
+ void addItems(HMENU menu, const std::vector<MenuItemModel> &items);
129
+
130
+ void addItem(HMENU menu, const MenuItemModel &item) {
131
+ if (item.separator) {
132
+ AppendMenuW(menu, MF_SEPARATOR, 0, nullptr);
133
+ return;
134
+ }
135
+
136
+ const UINT flags = item.enabled ? MF_ENABLED : MF_GRAYED;
137
+
138
+ if (!item.submenu.empty()) {
139
+ HMENU submenu = CreatePopupMenu();
140
+ if (submenu == nullptr) {
141
+ return;
142
+ }
143
+ addItems(submenu, item.submenu);
144
+ AppendMenuW(menu,
145
+ MF_POPUP | flags,
146
+ reinterpret_cast<UINT_PTR>(submenu),
147
+ win32::widen(item.label).c_str());
148
+ return;
149
+ }
150
+
151
+ if (!item.role.empty()) {
152
+ const UINT command = commandForRole(item.role);
153
+ const std::wstring label = labelForRole(item.role, item.label);
154
+ // An unknown role is dropped: a menu item that does nothing is worse than
155
+ // one that is not there.
156
+ if (command != 0 && !label.empty()) {
157
+ AppendMenuW(menu, MF_STRING | flags, command, label.c_str());
158
+ }
159
+ return;
160
+ }
161
+
162
+ if (item.id <= 0) {
163
+ return;
164
+ }
165
+ std::wstring label = win32::widen(item.label);
166
+ if (!item.accelerator.empty()) {
167
+ label += L"\t";
168
+ label += win32::widen(item.accelerator);
169
+ }
170
+ AppendMenuW(menu, MF_STRING | flags, static_cast<UINT_PTR>(item.id), label.c_str());
171
+ }
172
+
173
+ void addItems(HMENU menu, const std::vector<MenuItemModel> &items) {
174
+ for (const MenuItemModel &item : items) {
175
+ addItem(menu, item);
176
+ }
177
+ }
178
+
179
+ } // namespace
180
+
181
+ bool applicationMenuSupported() {
182
+ return true;
183
+ }
184
+
185
+ namespace {
186
+
187
+ void describeInto(std::string &out, HMENU menu, int depth) {
188
+ const int count = GetMenuItemCount(menu);
189
+ for (int i = 0; i < count; i++) {
190
+ wchar_t text[512] = {0};
191
+ MENUITEMINFOW info{};
192
+ info.cbSize = sizeof(info);
193
+ info.fMask = MIIM_STRING | MIIM_SUBMENU | MIIM_STATE | MIIM_FTYPE;
194
+ info.dwTypeData = text;
195
+ info.cch = static_cast<UINT>(std::size(text) - 1);
196
+ if (!GetMenuItemInfoW(menu, static_cast<UINT>(i), TRUE, &info)) {
197
+ continue;
198
+ }
199
+
200
+ out.append(static_cast<size_t>(depth) * 2, ' ');
201
+ if ((info.fType & MFT_SEPARATOR) != 0) {
202
+ out += "-\n";
203
+ continue;
204
+ }
205
+ // The accelerator is the text after a tab, which is how Windows stores one;
206
+ // it reads the same way the macOS dump's bracket does.
207
+ std::wstring label(text);
208
+ const size_t tab = label.find(L'\t');
209
+ if (tab != std::wstring::npos) {
210
+ out += win32::narrow(label.substr(0, tab));
211
+ out += " [";
212
+ out += win32::narrow(label.substr(tab + 1));
213
+ out += "]";
214
+ } else {
215
+ out += win32::narrow(label);
216
+ }
217
+ if ((info.fState & MFS_DISABLED) != 0) {
218
+ out += " (disabled)";
219
+ }
220
+ out += "\n";
221
+ if (info.hSubMenu != nullptr) {
222
+ describeInto(out, info.hSubMenu, depth + 1);
223
+ }
224
+ }
225
+ }
226
+
227
+ } // namespace
228
+
229
+ std::string describeApplicationMenu() {
230
+ std::string out;
231
+ if (installed() != nullptr) {
232
+ describeInto(out, installed(), 0);
233
+ }
234
+ return out;
235
+ }
236
+
237
+ void setApplicationMenu(const MenuModel &menu, std::function<void(int)> onChosen) {
238
+ HWND window = GetActiveWindow();
239
+ if (window == nullptr) {
240
+ window = GetForegroundWindow();
241
+ }
242
+ if (window == nullptr) {
243
+ return;
244
+ }
245
+
246
+ handler() = std::move(onChosen);
247
+
248
+ // An empty menu takes the bar away, which is what Windows' own default is:
249
+ // unlike macOS there is nothing an application needs a menu bar to do.
250
+ if (menu.empty()) {
251
+ SetMenu(window, nullptr);
252
+ if (installed() != nullptr) {
253
+ DestroyMenu(installed());
254
+ installed() = nullptr;
255
+ }
256
+ DrawMenuBar(window);
257
+ return;
258
+ }
259
+
260
+ HMENU bar = CreateMenu();
261
+ if (bar == nullptr) {
262
+ return;
263
+ }
264
+ for (const MenuItemModel &top : menu) {
265
+ // A bare command in a menu bar is not a thing any desktop has.
266
+ if (top.submenu.empty()) {
267
+ continue;
268
+ }
269
+ HMENU submenu = CreatePopupMenu();
270
+ if (submenu == nullptr) {
271
+ continue;
272
+ }
273
+ addItems(submenu, top.submenu);
274
+ AppendMenuW(bar,
275
+ MF_POPUP,
276
+ reinterpret_cast<UINT_PTR>(submenu),
277
+ win32::widen(top.label).c_str());
278
+ }
279
+
280
+ SetMenu(window, bar);
281
+ // Destroyed after the new one is in: a menu still attached to a window
282
+ // cannot be freed, and freeing the old one first would leave the window
283
+ // pointing at nothing for the length of a call.
284
+ if (installed() != nullptr) {
285
+ DestroyMenu(installed());
286
+ }
287
+ installed() = bar;
288
+ DrawMenuBar(window);
289
+ }
290
+
291
+ namespace win32 {
292
+
293
+ bool handleMenuCommand(unsigned int command) {
294
+ if (command < kRoleBase) {
295
+ // An app's own item. Zero is "not from a menu", which WM_COMMAND also uses
296
+ // for accelerators and controls.
297
+ if (command == 0 || !handler()) {
298
+ return false;
299
+ }
300
+ handler()(static_cast<int>(command));
301
+ return true;
302
+ }
303
+
304
+ HWND window = GetActiveWindow();
305
+ HWND focused = GetFocus();
306
+ switch (command) {
307
+ case kRoleBase + 1:
308
+ PostMessageW(window, WM_CLOSE, 0, 0);
309
+ return true;
310
+ // Posted to whatever has focus, which for these is a real EDIT control and
311
+ // answers every one of them itself. Windows has no responder chain; this is
312
+ // the equivalent.
313
+ case kRoleBase + 2:
314
+ SendMessageW(focused, WM_UNDO, 0, 0);
315
+ return true;
316
+ case kRoleBase + 3:
317
+ // EDIT has no redo. Reported as handled anyway: the alternative is the
318
+ // key travelling on to something that will do something else with it.
319
+ return true;
320
+ case kRoleBase + 4:
321
+ SendMessageW(focused, WM_CUT, 0, 0);
322
+ return true;
323
+ case kRoleBase + 5:
324
+ SendMessageW(focused, WM_COPY, 0, 0);
325
+ return true;
326
+ case kRoleBase + 6:
327
+ SendMessageW(focused, WM_PASTE, 0, 0);
328
+ return true;
329
+ case kRoleBase + 7:
330
+ SendMessageW(focused, WM_CLEAR, 0, 0);
331
+ return true;
332
+ case kRoleBase + 8:
333
+ SendMessageW(focused, EM_SETSEL, 0, -1);
334
+ return true;
335
+ case kRoleBase + 9:
336
+ ShowWindow(window, SW_MINIMIZE);
337
+ return true;
338
+ case kRoleBase + 10:
339
+ ShowWindow(window, IsZoomed(window) ? SW_RESTORE : SW_MAXIMIZE);
340
+ return true;
341
+ case kRoleBase + 11:
342
+ PostMessageW(window, WM_CLOSE, 0, 0);
343
+ return true;
344
+ case kRoleBase + 12:
345
+ setWindowFullScreen(!windowBounds().fullScreen);
346
+ return true;
347
+ default:
348
+ return false;
349
+ }
350
+ }
351
+
352
+ } // namespace win32
353
+
354
+ // --- Roles, for a popup menu too ----------------------------------------------
355
+ //
356
+ // `showMenu` in PlatformServicesWin32.cpp performs a role itself rather than
357
+ // letting a WM_COMMAND carry it, because it has a promise to settle with the
358
+ // index chosen. The work is the same work: `handleMenuCommand` above already
359
+ // posts WM_COPY and friends to whatever has focus, so this is a second caller
360
+ // for it rather than a second implementation.
361
+ bool menuRoleSupported(const std::string &role) {
362
+ return commandForRole(role) != 0;
363
+ }
364
+
365
+ void performMenuRole(const std::string &role) {
366
+ // A paragraph's selection first, when there is one: `Copy` over selected text
367
+ // means that text, and the focused control below is what the role meant when
368
+ // only a <TextInput> could hold a selection. At most one of the two exists --
369
+ // pressing in a field clears a paragraph's selection on the way past -- so
370
+ // the order settles a case that does not arise rather than choosing between
371
+ // two live selections. See core/TextSelection.h.
372
+ if (role == "copy" && copySelectedText()) {
373
+ return;
374
+ }
375
+
376
+ const UINT command = commandForRole(role);
377
+ if (command == 0) {
378
+ return;
379
+ }
380
+ win32::handleMenuCommand(command);
381
+ }
382
+ } // namespace basalt
@@ -0,0 +1,16 @@
1
+ // The host's half of the Windows menu bar: forwarding WM_COMMAND.
2
+ //
3
+ // A menu bar's items arrive as WM_COMMAND on the window that owns it, and the
4
+ // window procedure is the host's. This is the one line it has to pass on. See
5
+ // Win32MenuBar.cpp for what happens to it.
6
+
7
+ #pragma once
8
+
9
+ namespace basalt::win32 {
10
+
11
+ // Handles a WM_COMMAND from the application menu. Returns false when the
12
+ // command was not one of its own, so the host can go on treating it as whatever
13
+ // else a WM_COMMAND is -- a control's notification, in this host's case.
14
+ bool handleMenuCommand(unsigned int command);
15
+
16
+ } // namespace basalt::win32