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,111 @@
1
+ // The geometry of a hidden title bar: how big the caption is, which button a
2
+ // point is over, and whether a point drags the window.
3
+ //
4
+ // Pure functions of a view tree and a size, so no window, no DWM and no React
5
+ // Native. These are the questions a custom caption gets wrong in ways a person
6
+ // notices at once -- a close button a few pixels off, a label that will not
7
+ // drag, a button that drags instead of pressing -- and a test does not, unless
8
+ // it asks them.
9
+
10
+ #include "TestHarness.h"
11
+
12
+ #include "RnWin32View.h"
13
+ #include "Win32TitleBarLayout.h"
14
+
15
+ #include <memory>
16
+ #include <sstream>
17
+ #include <string>
18
+ #include <vector>
19
+
20
+ using basalt::win32::CaptionButton;
21
+ using basalt::win32::captionButtonAt;
22
+ using basalt::win32::captionMetricsForDpi;
23
+ using basalt::win32::isTitleBarDragRegionAt;
24
+ using basalt::win32::kTitleBarDragRegionId;
25
+ using basalt::win32::kTitleBarNoDragRegionId;
26
+ using basalt::win32::RnWin32View;
27
+
28
+ namespace {
29
+
30
+ class Tree {
31
+ public:
32
+ RnWin32View *box(int32_t tag, float x, float y, float w, float h, const char *nativeId = "") {
33
+ auto view = std::make_unique<RnWin32View>(tag);
34
+ view->setFrame(x, y, w, h);
35
+ view->setNativeId(nativeId);
36
+ RnWin32View *raw = view.get();
37
+ views_.push_back(std::move(view));
38
+ return raw;
39
+ }
40
+
41
+ private:
42
+ std::vector<std::unique_ptr<RnWin32View>> views_;
43
+ };
44
+
45
+ } // namespace
46
+
47
+ TEST(caption_metrics_are_windows_elevens_and_scale_with_dpi) {
48
+ const auto at100 = captionMetricsForDpi(96);
49
+ EXPECT_NEAR(at100.height, 32.0f, 0.01f);
50
+ EXPECT_NEAR(at100.buttonWidth, 46.0f, 0.01f);
51
+
52
+ const auto at150 = captionMetricsForDpi(144);
53
+ EXPECT_NEAR(at150.height, 48.0f, 0.01f);
54
+ EXPECT_NEAR(at150.buttonWidth, 69.0f, 0.01f);
55
+
56
+ // A DPI of zero is what a window that has not been shown can report; it is
57
+ // treated as 96 rather than collapsing the caption to nothing.
58
+ EXPECT_NEAR(captionMetricsForDpi(0).height, 32.0f, 0.01f);
59
+ }
60
+
61
+ TEST(caption_buttons_sit_flush_right_in_the_order_windows_puts_them) {
62
+ const auto metrics = captionMetricsForDpi(96);
63
+ const float width = 800;
64
+
65
+ EXPECT(captionButtonAt(799, 10, width, metrics) == CaptionButton::Close);
66
+ EXPECT(captionButtonAt(width - 46 - 1, 10, width, metrics) == CaptionButton::Maximize);
67
+ EXPECT(captionButtonAt(width - 92 - 1, 10, width, metrics) == CaptionButton::Minimize);
68
+ EXPECT(captionButtonAt(width - 138 - 1, 10, width, metrics) == CaptionButton::None);
69
+
70
+ // Below the caption, or outside the window, is no button at all.
71
+ EXPECT(captionButtonAt(799, 32, width, metrics) == CaptionButton::None);
72
+ EXPECT(captionButtonAt(-1, 10, width, metrics) == CaptionButton::None);
73
+ }
74
+
75
+ TEST(a_label_inside_a_drag_region_drags_the_window) {
76
+ Tree tree;
77
+ RnWin32View *root = tree.box(1, 0, 0, 800, 600);
78
+ RnWin32View *header = tree.box(2, 0, 0, 800, 32, kTitleBarDragRegionId);
79
+ root->insertChild(header, 0);
80
+ header->insertChild(tree.box(3, 10, 5, 100, 20), 0);
81
+
82
+ EXPECT(isTitleBarDragRegionAt(root, 20, 10));
83
+ EXPECT(isTitleBarDragRegionAt(root, 400, 10));
84
+ }
85
+
86
+ TEST(a_no_drag_region_inside_a_drag_region_stays_pressable) {
87
+ Tree tree;
88
+ RnWin32View *root = tree.box(1, 0, 0, 800, 600);
89
+ RnWin32View *header = tree.box(2, 0, 0, 800, 32, kTitleBarDragRegionId);
90
+ root->insertChild(header, 0);
91
+ RnWin32View *button = tree.box(3, 600, 0, 50, 32, kTitleBarNoDragRegionId);
92
+ header->insertChild(button, 0);
93
+
94
+ EXPECT(!isTitleBarDragRegionAt(root, 620, 10));
95
+
96
+ // And a drag region nested inside that button drags again: the nearest
97
+ // marker decides, not the outermost.
98
+ button->insertChild(tree.box(4, 5, 5, 10, 10, kTitleBarDragRegionId), 0);
99
+ EXPECT(isTitleBarDragRegionAt(root, 607, 7));
100
+ }
101
+
102
+ TEST(a_point_under_nothing_marked_is_ordinary_client_area) {
103
+ Tree tree;
104
+ RnWin32View *root = tree.box(1, 0, 0, 800, 600);
105
+ root->insertChild(tree.box(2, 0, 0, 800, 32, kTitleBarDragRegionId), 0);
106
+ root->insertChild(tree.box(3, 0, 32, 800, 568), 1);
107
+
108
+ EXPECT(!isTitleBarDragRegionAt(root, 400, 300));
109
+ // A miss altogether -- outside the root -- is not a drag either.
110
+ EXPECT(!isTitleBarDragRegionAt(root, 900, 10));
111
+ }
@@ -0,0 +1,62 @@
1
+ // The caption's four actions before there is a window.
2
+ //
3
+ // minimize, toggleMaximize, close and startWindowDrag are what an app-drawn
4
+ // header asks for by name. GtkWindowModule and AppKitWindowModule have
5
+ // registered them all along; this host registered none of them, so on Windows
6
+ // useWindow().minimize() and .close() were silent no-ops -- and useCloseRequest
7
+ // could refuse a close and then find that agreeing to it did nothing either.
8
+ //
9
+ // Now that they are registered, the question this asks is the one a test can
10
+ // answer without a desktop: what they do before the host has an HWND. An app
11
+ // can call close() from its first render, and the honest answer then is to do
12
+ // nothing, not to dereference a null window.
13
+ //
14
+ // What this deliberately cannot check is the other half -- that a real window
15
+ // actually minimises, or that startDrag hands Windows its move loop. Both need
16
+ // a window, a message queue and a person's mouse; they belong to the
17
+ // end-to-end suite, and saying so here is better than a test that looks like
18
+ // it covers them.
19
+ //
20
+ // Lives in the React-Native-gated source list rather than beside
21
+ // test_win32_titlebar.cpp, because Win32TitleBar is compiled into
22
+ // basalt_win32_mounting, which only exists when basalt_core does. Its
23
+ // neighbour tests pure geometry and needs no such thing.
24
+
25
+ #include "TestHarness.h"
26
+
27
+ #include "Win32TitleBar.h"
28
+
29
+ using basalt::TitleBarStyle;
30
+ using basalt::Win32TitleBar;
31
+
32
+ TEST(caption_actions_do_nothing_when_no_window_is_attached) {
33
+ Win32TitleBar bar;
34
+ const TitleBarStyle before = bar.style();
35
+
36
+ bar.minimize();
37
+ bar.toggleMaximize();
38
+ bar.close();
39
+ bar.startDrag();
40
+
41
+ // Reaching this line is most of the assertion: each call returned rather
42
+ // than posting to a null window. The style is the one piece of state they
43
+ // could have disturbed on the way, so it is worth saying it did not move.
44
+ EXPECT(bar.style() == before);
45
+ }
46
+
47
+ TEST(caption_actions_are_repeatable_without_a_window) {
48
+ Win32TitleBar bar;
49
+
50
+ // Twice, because the guard is a plain early return and an app that polls --
51
+ // a header re-rendering on every frame, say -- makes exactly this call
52
+ // pattern. Nothing here accumulates state, and this is what would notice if
53
+ // one of them started to.
54
+ for (int attempt = 0; attempt < 2; ++attempt) {
55
+ bar.minimize();
56
+ bar.toggleMaximize();
57
+ bar.close();
58
+ bar.startDrag();
59
+ }
60
+
61
+ EXPECT(bar.style() == TitleBarStyle::Native);
62
+ }
@@ -0,0 +1,222 @@
1
+ // Tests for the Windows view layer.
2
+ //
3
+ // RnWin32View has no React Native dependency and no Direct2D dependency until
4
+ // something paints, so these need no Fabric machinery, no window and no
5
+ // display. They share the toolkit-free harness the GTK and macOS suites use.
6
+ //
7
+ // The questions are deliberately the same ones tests/test_view.cpp asks of GTK
8
+ // and tests/test_appkit_view.mm asks of AppKit, in the same order: what is being
9
+ // checked is largely whether three platforms still agree, so a divergence
10
+ // should fail here rather than in an app.
11
+
12
+ #include "TestHarness.h"
13
+
14
+ #include "RnWin32View.h"
15
+
16
+ #include <memory>
17
+ #include <sstream>
18
+ #include <string>
19
+ #include <vector>
20
+
21
+ using basalt::win32::RnWin32View;
22
+
23
+ namespace {
24
+
25
+ // The tests own their views, as the mounting registry will. A view owns neither
26
+ // its children nor its parent; see RnWin32View.h.
27
+ class Tree {
28
+ public:
29
+ RnWin32View *box(int32_t tag, float x, float y, float w, float h) {
30
+ auto view = std::make_unique<RnWin32View>(tag);
31
+ view->setFrame(x, y, w, h);
32
+ RnWin32View *raw = view.get();
33
+ views_.push_back(std::move(view));
34
+ return raw;
35
+ }
36
+
37
+ // Releases one view, the way a Delete mutation would.
38
+ void destroy(RnWin32View *view) {
39
+ for (auto it = views_.begin(); it != views_.end(); ++it) {
40
+ if (it->get() == view) {
41
+ views_.erase(it);
42
+ return;
43
+ }
44
+ }
45
+ }
46
+
47
+ private:
48
+ std::vector<std::unique_ptr<RnWin32View>> views_;
49
+ };
50
+
51
+ int32_t tagOfChild(const RnWin32View *parent, size_t index) {
52
+ return parent->children()[index]->tag();
53
+ }
54
+
55
+ } // namespace
56
+
57
+ TEST(win32_view_stores_its_tag_and_frame) {
58
+ Tree tree;
59
+ RnWin32View *view = tree.box(42, 12, 34, 100, 50);
60
+
61
+ EXPECT_EQ(view->tag(), 42);
62
+ EXPECT_NEAR(view->frame().x, 12.0, 0.001);
63
+ EXPECT_NEAR(view->frame().y, 34.0, 0.001);
64
+ EXPECT_NEAR(view->frame().width, 100.0, 0.001);
65
+ EXPECT_NEAR(view->frame().height, 50.0, 0.001);
66
+ }
67
+
68
+ TEST(win32_children_insert_at_the_requested_index) {
69
+ Tree tree;
70
+ RnWin32View *root = tree.box(1, 0, 0, 200, 200);
71
+ RnWin32View *first = tree.box(2, 0, 0, 10, 10);
72
+ RnWin32View *second = tree.box(3, 0, 0, 10, 10);
73
+ RnWin32View *between = tree.box(4, 0, 0, 10, 10);
74
+
75
+ root->insertChild(first, 0);
76
+ root->insertChild(second, 1);
77
+ // An Insert's index counts positions in the parent's *final* child list, so
78
+ // this lands between the two. An append-only implementation passes every
79
+ // other test in this file.
80
+ root->insertChild(between, 1);
81
+
82
+ EXPECT_EQ(root->children().size(), size_t{3});
83
+ EXPECT_EQ(tagOfChild(root, 0), 2);
84
+ EXPECT_EQ(tagOfChild(root, 1), 4);
85
+ EXPECT_EQ(tagOfChild(root, 2), 3);
86
+ }
87
+
88
+ TEST(win32_view_removes_only_its_own_child) {
89
+ Tree tree;
90
+ RnWin32View *root = tree.box(1, 0, 0, 200, 200);
91
+ RnWin32View *mine = tree.box(2, 0, 0, 10, 10);
92
+ RnWin32View *someone_elses = tree.box(3, 0, 0, 10, 10);
93
+ RnWin32View *other = tree.box(4, 0, 0, 200, 200);
94
+
95
+ root->insertChild(mine, 0);
96
+ other->insertChild(someone_elses, 0);
97
+
98
+ root->removeChild(someone_elses);
99
+
100
+ EXPECT_EQ(root->children().size(), size_t{1});
101
+ EXPECT_EQ(other->children().size(), size_t{1});
102
+ EXPECT_EQ(someone_elses->parent()->tag(), 4);
103
+ }
104
+
105
+ TEST(win32_remove_detaches_but_does_not_destroy) {
106
+ Tree tree;
107
+ RnWin32View *root = tree.box(1, 0, 0, 200, 200);
108
+ RnWin32View *child = tree.box(2, 5, 5, 10, 10);
109
+
110
+ root->insertChild(child, 0);
111
+ root->removeChild(child);
112
+
113
+ // A reparent is a Remove and an Insert with no Delete between them, so the
114
+ // view has to survive the gap with everything about it intact.
115
+ EXPECT_EQ(root->children().size(), size_t{0});
116
+ EXPECT(child->parent() == nullptr);
117
+ EXPECT_NEAR(child->frame().x, 5.0, 0.001);
118
+ }
119
+
120
+ TEST(win32_insert_reparents_without_a_remove) {
121
+ Tree tree;
122
+ RnWin32View *first = tree.box(1, 0, 0, 200, 200);
123
+ RnWin32View *second = tree.box(2, 0, 0, 200, 200);
124
+ RnWin32View *child = tree.box(3, 0, 0, 10, 10);
125
+
126
+ first->insertChild(child, 0);
127
+ second->insertChild(child, 0);
128
+
129
+ EXPECT_EQ(first->children().size(), size_t{0});
130
+ EXPECT_EQ(second->children().size(), size_t{1});
131
+ EXPECT_EQ(child->parent()->tag(), 2);
132
+ }
133
+
134
+ TEST(win32_destroying_a_view_leaves_nothing_pointing_at_it) {
135
+ Tree tree;
136
+ RnWin32View *root = tree.box(1, 0, 0, 200, 200);
137
+ RnWin32View *child = tree.box(2, 0, 0, 10, 10);
138
+ RnWin32View *grandchild = tree.box(3, 0, 0, 5, 5);
139
+
140
+ root->insertChild(child, 0);
141
+ child->insertChild(grandchild, 0);
142
+
143
+ tree.destroy(child);
144
+
145
+ // The registry owns every view, so a Delete of a parent does not delete its
146
+ // children -- their own Deletes are separate mutations. What must not survive
147
+ // is a pointer to the freed view, on either side.
148
+ EXPECT_EQ(root->children().size(), size_t{0});
149
+ EXPECT(grandchild->parent() == nullptr);
150
+ }
151
+
152
+ TEST(win32_z_index_reorders_painting_and_not_the_child_list) {
153
+ Tree tree;
154
+ RnWin32View *root = tree.box(1, 0, 0, 200, 200);
155
+ RnWin32View *first = tree.box(2, 0, 0, 10, 10);
156
+ RnWin32View *second = tree.box(3, 0, 0, 10, 10);
157
+
158
+ root->insertChild(first, 0);
159
+ root->insertChild(second, 1);
160
+ first->setZIndex(10);
161
+
162
+ // Fabric's Insert and Remove index into this list, so it stays in mutation
163
+ // order no matter what zIndex says. That painting follows zIndex instead is
164
+ // asserted in test_win32_paint.cpp, where it can be seen.
165
+ EXPECT_EQ(tagOfChild(root, 0), 2);
166
+ EXPECT_EQ(tagOfChild(root, 1), 3);
167
+ }
168
+
169
+ TEST(win32_scroll_offset_is_reported_in_the_tree) {
170
+ Tree tree;
171
+ RnWin32View *root = tree.box(1, 0, 0, 200, 200);
172
+ root->setScrollOffset(0, 40);
173
+
174
+ const std::string described = root->describeTree();
175
+ EXPECT_EQ(described, std::string("view tag=1 frame=(0,0 200x200) scroll=(0,40)\n"));
176
+ }
177
+
178
+ TEST(win32_transform_reports_the_affine_part_of_matrix3d) {
179
+ Tree tree;
180
+ RnWin32View *root = tree.box(1, 0, 0, 200, 200);
181
+
182
+ // A quarter turn with a translation, in CSS matrix3d order, which is column
183
+ // major: elements 0, 1, 4, 5, 12 and 13 are the affine part. Getting the
184
+ // indices wrong is a transpose, and a transpose is invisible on any
185
+ // axis-aligned box -- which is why this asserts on an asymmetric matrix.
186
+ const float matrix[16] = {
187
+ 0, 1, 0, 0, //
188
+ -1, 0, 0, 0, //
189
+ 0, 0, 1, 0, //
190
+ 5, 7, 0, 1};
191
+ root->setTransform(matrix);
192
+
193
+ const std::string described = root->describeTree();
194
+ EXPECT_EQ(described,
195
+ std::string("view tag=1 frame=(0,0 200x200) transform=(0,1,-1,0,5,7)\n"));
196
+
197
+ root->setTransform(nullptr);
198
+ EXPECT_EQ(root->describeTree(), std::string("view tag=1 frame=(0,0 200x200)\n"));
199
+ }
200
+
201
+ TEST(win32_view_describes_its_tree_like_the_other_hosts) {
202
+ Tree tree;
203
+ RnWin32View *root = tree.box(1, 0, 0, 640, 420);
204
+ root->setBackgroundColor(0.12f, 0.13f, 0.16f, 1.0f, true);
205
+
206
+ RnWin32View *child = tree.box(2, 32, 32, 240, 160);
207
+ child->setOpacity(0.4f);
208
+ child->setClipsChildren(true);
209
+ root->insertChild(child, 0);
210
+
211
+ // Byte for byte what tests/test_appkit_view.mm expects of AppKit and what
212
+ // gtk/RnView.cpp produces. scripts/compare_hosts.sh diffs these dumps line by
213
+ // line, so this string is the contract rather than a convenience.
214
+ const std::string expected =
215
+ "view tag=1 frame=(0,0 640x420) bg=#1f2129ff\n"
216
+ " view tag=2 frame=(32,32 240x160) opacity=0.4 clip\n";
217
+ EXPECT_EQ(root->describeTree(), expected);
218
+ }
219
+
220
+ int main(int argc, char **argv) {
221
+ return basalt::testing::runAllTests(argc, argv);
222
+ }
@@ -0,0 +1,56 @@
1
+ // Windows' half of core/ColorScheme.h -- light or dark, as the system has it.
2
+ //
3
+ // The answer lives in the registry rather than in an API: Windows exposes the
4
+ // app theme as `AppsUseLightTheme` under the Personalize key, and there is no
5
+ // Win32 function that returns it. WinRT has UISettings, which would be a
6
+ // heavier dependency for one boolean and is unavailable in a plain Win32
7
+ // process without an apartment. The registry value is what every desktop
8
+ // framework reads, and it is what Explorer writes when the setting changes.
9
+ //
10
+ // Note the sense: the value is "apps use *light* theme", so 1 is light and 0 is
11
+ // dark, and an absent value means light -- which is the pre-2018 behaviour and
12
+ // still the right default.
13
+
14
+ #include "ColorScheme.h"
15
+
16
+ #include <windows.h>
17
+
18
+ namespace basalt {
19
+ namespace {
20
+
21
+ constexpr const wchar_t *kPersonalizeKey =
22
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
23
+ constexpr const wchar_t *kAppsUseLightTheme = L"AppsUseLightTheme";
24
+
25
+ } // namespace
26
+
27
+ ColorScheme systemColorScheme() {
28
+ DWORD value = 1;
29
+ DWORD size = sizeof(value);
30
+ const LSTATUS status = RegGetValueW(HKEY_CURRENT_USER,
31
+ kPersonalizeKey,
32
+ kAppsUseLightTheme,
33
+ RRF_RT_REG_DWORD,
34
+ nullptr,
35
+ &value,
36
+ &size);
37
+ if (status != ERROR_SUCCESS) {
38
+ // No value at all: a machine that predates the setting, or a policy that
39
+ // removed it. Light is what those show.
40
+ return ColorScheme::Light;
41
+ }
42
+ return value == 0 ? ColorScheme::Dark : ColorScheme::Light;
43
+ }
44
+
45
+ void startObservingColorScheme() {
46
+ // Not implemented, and deliberately not faked. Watching this properly means
47
+ // RegNotifyChangeKeyValue on a worker thread, or handling WM_SETTINGCHANGE
48
+ // with lParam "ImmersiveColorSet" on the host's window -- and the second is
49
+ // the better answer, because it arrives on the thread that would have to
50
+ // repaint. It belongs with the host for that reason.
51
+ //
52
+ // The other two desktops subscribe and, per docs/BACKLOG.md, have never been
53
+ // watched firing. So nothing is lost here that is proven to work there.
54
+ }
55
+
56
+ } // namespace basalt
@@ -0,0 +1,124 @@
1
+ // Which components this platform claims.
2
+ //
3
+ // `getDefaultComponentRegistryFactory()` is declared by ReactCommon and defined
4
+ // nowhere in it: each host supplies its own and, in doing so, says what it can
5
+ // put on screen. Phase 19 found out the hard way that it cannot be shared --
6
+ // `ParagraphComponentDescriptor` constructs a `TextLayoutManager`, so a
7
+ // platform without a text engine fails to link rather than failing to render
8
+ // text. See `core/ComponentRegistry.h`.
9
+ //
10
+ // The set here must match what `Win32MountingManager::hasComponent` answers
11
+ // for. When they disagree the registry wins, Fabric builds shadow nodes nothing
12
+ // can mount, and the app renders blank rectangles rather than reporting
13
+ // anything -- which is why the two live in the same directory and should be
14
+ // read together.
15
+
16
+ #include "ComponentRegistry.h"
17
+
18
+ #include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
19
+ #include <react/renderer/components/FBReactNativeSpec/ComponentDescriptors.h>
20
+ #include <react/renderer/components/image/ImageComponentDescriptor.h>
21
+ #include <react/renderer/components/iostextinput/TextInputComponentDescriptor.h>
22
+ #include <react/renderer/components/modal/ModalHostViewComponentDescriptor.h>
23
+ #include <react/renderer/components/scrollview/ScrollViewComponentDescriptor.h>
24
+ #include <react/renderer/components/switch/AppleSwitchComponentDescriptor.h>
25
+ #include <react/renderer/components/text/ParagraphComponentDescriptor.h>
26
+ #include <react/renderer/components/text/RawTextComponentDescriptor.h>
27
+ #include <react/renderer/components/text/TextComponentDescriptor.h>
28
+ #include <react/renderer/components/view/ViewComponentDescriptor.h>
29
+
30
+ namespace facebook::react {
31
+
32
+ ComponentRegistryFactory getDefaultComponentRegistryFactory() {
33
+ return [](const EventDispatcher::Weak &eventDispatcher,
34
+ const std::shared_ptr<const ContextContainer> &contextContainer) {
35
+ static auto providerRegistry = []() {
36
+ auto registry = std::make_shared<ComponentDescriptorProviderRegistry>();
37
+ registry->add(concreteComponentDescriptorProvider<ViewComponentDescriptor>());
38
+
39
+ // Text is three descriptors and only one of them mounts. <Text> becomes a
40
+ // Text node, its string a RawText node, and the outermost <Text> a
41
+ // Paragraph that folds the whole subtree into a single AttributedString.
42
+ registry->add(concreteComponentDescriptorProvider<ParagraphComponentDescriptor>());
43
+ registry->add(concreteComponentDescriptorProvider<TextComponentDescriptor>());
44
+ registry->add(concreteComponentDescriptorProvider<RawTextComponentDescriptor>());
45
+
46
+ // Image's descriptor pulls an ImageManager out of the ContextContainer,
47
+ // creating one if absent. React Native's cxx ImageManager is a stub, so
48
+ // it produces no pixels; the mounting manager loads them from the props,
49
+ // which is what Android does too.
50
+ registry->add(concreteComponentDescriptorProvider<ImageComponentDescriptor>());
51
+
52
+ // ScrollView's content child arrives as "ScrollContentView", which
53
+ // React Native's own registry rewrites to "View" before it reaches here,
54
+ // so it needs no entry of its own. What the descriptor is really for is
55
+ // ScrollViewState: without it the substituted UnimplementedNativeView has
56
+ // nowhere to keep a content offset, so a ScrollView renders and never
57
+ // scrolls -- silently, which is why the absence used to be worth naming.
58
+ registry->add(concreteComponentDescriptorProvider<ScrollViewComponentDescriptor>());
59
+
60
+ // React Native's *iOS* TextInput, whose shadow node, props, state and
61
+ // event emitter are pure C++ and measure through a TextLayoutManager --
62
+ // the DirectWrite one, here. Android's variant includes fbjni and calls
63
+ // into a Java FabricUIManager, so it cannot be used outside an Android
64
+ // build. Both other desktops made the same choice; see
65
+ // docs/DECISIONS.md. Its component name is "TextInput", which is what
66
+ // React Native's own componentNameByReactViewName maps
67
+ // RCTSinglelineTextInputView to, so nothing here renames anything.
68
+ registry->add(concreteComponentDescriptorProvider<TextInputComponentDescriptor>());
69
+
70
+ // Deliberately absent, and the absence is a decision rather than an
71
+ // oversight: ExpoImage's seam exists in core/ExpoImageComponent.h and its
72
+ // props class is portable, but nothing here mounts one yet.
73
+ //
74
+ // See docs/BACKLOG.md.
75
+
76
+ // --- The controls -----------------------------------------------------
77
+ //
78
+ // Everything above is a box with something drawn in it. These are not:
79
+ // each one is a toolkit control, or a window, and each one needed the
80
+ // view layer to grow a peer before registering it here would have meant
81
+ // anything. Registering a descriptor with nothing to mount it does not
82
+ // error -- Fabric builds the shadow nodes, lays them out, and the app
83
+ // renders blank rectangles -- so this list and the mounting manager's
84
+ // `hasComponent` are two statements of the same fact and have to agree.
85
+
86
+ // <ActivityIndicator>. React Native's JavaScript writes the 20pt or 36pt
87
+ // box into the view's style, so the shadow node needs no measurement of
88
+ // its own; what the host draws inside it is a spinner.
89
+ registry->add(concreteComponentDescriptorProvider<ActivityIndicatorViewComponentDescriptor>());
90
+ // <Switch>. The descriptor is React Native's iOS one -- portable C++
91
+ // apart from a `measureContent` that instantiates a UISwitch, which
92
+ // core/DesktopControls.cpp replaces with a constant so a switch is the
93
+ // same size on all three desktops.
94
+ registry->add(concreteComponentDescriptorProvider<SwitchComponentDescriptor>());
95
+ // <Modal>. Not a second window: the shadow node is a root-kind node sized
96
+ // from its state and positioned absolutely, so a modal is an overlay
97
+ // filling the surface -- which is also what it is on the web. The host
98
+ // commits the window's size into that state; see
99
+ // core/DesktopControls.h's modalScreenSizeNeedsUpdate for why it must.
100
+ registry->add(concreteComponentDescriptorProvider<ModalHostViewComponentDescriptor>());
101
+ // <RefreshControl>. The spinner a scroll view shows while it refreshes.
102
+ // The gesture that starts it is in core/PullToRefresh.h, because a
103
+ // desktop has no rubber band to release.
104
+ registry->add(concreteComponentDescriptorProvider<PullToRefreshViewComponentDescriptor>());
105
+ // What React Native substitutes for a component no platform registered.
106
+ // Registering it is what turns "nothing happens" into a mounted view the
107
+ // tree dump can show, and it is what the comments above mean when they
108
+ // say the substitution is silent.
109
+ registry->add(concreteComponentDescriptorProvider<UnimplementedNativeViewComponentDescriptor>());
110
+ // React DevTools' element highlighter. It draws nothing here yet -- the
111
+ // highlight arrives as a command rather than as props -- but a DevTools
112
+ // session mounts one, and an unregistered component in the tree is a
113
+ // blank rectangle in the middle of the app.
114
+ registry->add(concreteComponentDescriptorProvider<DebuggingOverlayComponentDescriptor>());
115
+
116
+ return registry;
117
+ }();
118
+
119
+ return providerRegistry->createComponentDescriptorRegistry(
120
+ {eventDispatcher, contextContainer, nullptr});
121
+ };
122
+ }
123
+
124
+ } // namespace facebook::react