basalt-gtk 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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/cli/runLinux.js +29 -0
  3. package/cli/shared.js +75 -0
  4. package/native/CMakeLists.txt +362 -0
  5. package/native/gtk/ColorSchemeGtk.cpp +58 -0
  6. package/native/gtk/ComponentRegistryGtk.cpp +101 -0
  7. package/native/gtk/FontRegistryFontconfig.cpp +127 -0
  8. package/native/gtk/GtkAnimationChoreographer.cpp +73 -0
  9. package/native/gtk/GtkAnimationChoreographer.h +49 -0
  10. package/native/gtk/GtkDropTarget.cpp +269 -0
  11. package/native/gtk/GtkDropTarget.h +40 -0
  12. package/native/gtk/GtkFocus.cpp +296 -0
  13. package/native/gtk/GtkFocus.h +113 -0
  14. package/native/gtk/GtkImageLoader.cpp +128 -0
  15. package/native/gtk/GtkImageLoader.h +87 -0
  16. package/native/gtk/GtkKeyEvents.cpp +91 -0
  17. package/native/gtk/GtkKeyEvents.h +21 -0
  18. package/native/gtk/GtkMenuBar.cpp +45 -0
  19. package/native/gtk/GtkMountingManager.cpp +1027 -0
  20. package/native/gtk/GtkMountingManager.h +146 -0
  21. package/native/gtk/GtkRunLoopObserver.cpp +77 -0
  22. package/native/gtk/GtkRunLoopObserver.h +41 -0
  23. package/native/gtk/GtkScrollView.cpp +626 -0
  24. package/native/gtk/GtkScrollView.h +209 -0
  25. package/native/gtk/GtkSkiaContext.cpp +248 -0
  26. package/native/gtk/GtkSkiaContext.h +62 -0
  27. package/native/gtk/GtkSkiaModule.cpp +80 -0
  28. package/native/gtk/GtkSkiaModule.h +51 -0
  29. package/native/gtk/GtkSkiaPeer.cpp +285 -0
  30. package/native/gtk/GtkSkiaPeer.h +63 -0
  31. package/native/gtk/GtkTextInput.cpp +428 -0
  32. package/native/gtk/GtkTextInput.h +123 -0
  33. package/native/gtk/GtkTextPeer.cpp +425 -0
  34. package/native/gtk/GtkTextPeer.h +87 -0
  35. package/native/gtk/GtkTitleBar.cpp +273 -0
  36. package/native/gtk/GtkTitleBar.h +96 -0
  37. package/native/gtk/GtkTitleBarLayout.cpp +45 -0
  38. package/native/gtk/GtkTitleBarLayout.h +36 -0
  39. package/native/gtk/GtkTouchDispatcher.cpp +671 -0
  40. package/native/gtk/GtkTouchDispatcher.h +139 -0
  41. package/native/gtk/GtkWindowControl.cpp +223 -0
  42. package/native/gtk/GtkWindowModule.cpp +194 -0
  43. package/native/gtk/GtkWindowModule.h +62 -0
  44. package/native/gtk/PangoTextLayout.cpp +295 -0
  45. package/native/gtk/PangoTextLayout.h +54 -0
  46. package/native/gtk/PangoTextLayoutManager.cpp +140 -0
  47. package/native/gtk/PlatformServicesGtk.cpp +583 -0
  48. package/native/gtk/RnView.cpp +1407 -0
  49. package/native/gtk/RnView.h +339 -0
  50. package/native/gtk/demo_layout_gtk.cpp +57 -0
  51. package/native/gtk/main_gtk.cpp +1981 -0
  52. package/native/gtk/mount_harness_gtk.cpp +143 -0
  53. package/native/tests/TestMainGtk.cpp +25 -0
  54. package/native/tests/notification_stub.cpp +103 -0
  55. package/native/tests/test_accessibility.cpp +251 -0
  56. package/native/tests/test_focus.cpp +194 -0
  57. package/native/tests/test_hittest.cpp +242 -0
  58. package/native/tests/test_image.cpp +135 -0
  59. package/native/tests/test_key_events_gtk.cpp +112 -0
  60. package/native/tests/test_mounting.cpp +415 -0
  61. package/native/tests/test_scrollview.cpp +258 -0
  62. package/native/tests/test_skia_texture.cpp +120 -0
  63. package/native/tests/test_text.cpp +147 -0
  64. package/native/tests/test_textinput.cpp +598 -0
  65. package/native/tests/test_titlebar.cpp +137 -0
  66. package/native/tests/test_titlebar_drag.cpp +138 -0
  67. package/native/tests/test_version.cpp +49 -0
  68. package/native/tests/test_view.cpp +162 -0
  69. package/native/tests/test_viewprops.cpp +254 -0
  70. package/package.json +26 -0
  71. package/react-native.config.js +21 -0
@@ -0,0 +1,254 @@
1
+ // Tests for the View props added last: borders, radii, transform, zIndex and
2
+ // display.
3
+ //
4
+ // The transform tests earn their place. React Native's matrix is CSS matrix3d
5
+ // order and graphene's is nominally row-major, and the two coincide in memory
6
+ // for translation but are transposes of each other for rotation. Asserting on
7
+ // where a corner actually lands is the only way to know which is happening.
8
+
9
+ #include "TestHarness.h"
10
+
11
+ #include "RnView.h"
12
+
13
+ #include <cmath>
14
+ #include <sstream>
15
+ #include <string>
16
+
17
+ namespace {
18
+
19
+ void layout(RnView *root, int width, int height) {
20
+ gtk_widget_set_size_request(GTK_WIDGET(root), width, height);
21
+ gtk_widget_allocate(GTK_WIDGET(root), width, height, -1, nullptr);
22
+ }
23
+
24
+ RnView *addChild(RnView *parent, int tag, float x, float y, float width, float height) {
25
+ RnView *child = rn_view_new(tag);
26
+ g_object_ref_sink(child);
27
+ rn_view_set_frame(child, x, y, width, height);
28
+ rn_view_insert_child(parent, child, 0);
29
+ return child;
30
+ }
31
+
32
+ // Where a point inside the child ends up in the parent, after the transform.
33
+ bool mapPoint(RnView *child, RnView *parent, float x, float y, graphene_point_t *out) {
34
+ const graphene_point_t point = {x, y};
35
+ return gtk_widget_compute_point(GTK_WIDGET(child), GTK_WIDGET(parent), &point, out);
36
+ }
37
+
38
+ // Half a turn about Y, which mirrors the widget: a determinant of -1, and so
39
+ // a back face pointed away from the viewer.
40
+ const float kFlippedAboutY[16] = {-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1};
41
+
42
+ } // namespace
43
+
44
+ TEST(transform_translate_moves_the_widget) {
45
+ RnView *root = rn_view_new(1);
46
+ g_object_ref_sink(root);
47
+ RnView *child = addChild(root, 10, 0.0F, 0.0F, 100.0F, 100.0F);
48
+
49
+ graphene_matrix_t matrix;
50
+ const graphene_point3d_t offset = {50.0F, 20.0F, 0.0F};
51
+ graphene_matrix_init_translate(&matrix, &offset);
52
+ rn_view_set_transform(child, &matrix);
53
+ layout(root, 400, 400);
54
+
55
+ graphene_point_t mapped;
56
+ EXPECT(mapPoint(child, root, 0.0F, 0.0F, &mapped));
57
+ EXPECT_NEAR(mapped.x, 50.0, 0.5);
58
+ EXPECT_NEAR(mapped.y, 20.0, 0.5);
59
+
60
+ g_object_unref(root);
61
+ }
62
+
63
+ TEST(transform_is_anchored_on_the_centre) {
64
+ // Every other React Native platform anchors a transform on the view's centre,
65
+ // and transformOrigin is measured from there. A scale about the top-left
66
+ // would leave the origin where it is; about the centre it moves.
67
+ RnView *root = rn_view_new(1);
68
+ g_object_ref_sink(root);
69
+ RnView *child = addChild(root, 11, 0.0F, 0.0F, 100.0F, 100.0F);
70
+
71
+ graphene_matrix_t matrix;
72
+ graphene_matrix_init_scale(&matrix, 2.0F, 2.0F, 1.0F);
73
+ rn_view_set_transform(child, &matrix);
74
+ layout(root, 400, 400);
75
+
76
+ graphene_point_t mapped;
77
+ EXPECT(mapPoint(child, root, 0.0F, 0.0F, &mapped));
78
+ // Doubling about the centre (50,50) sends the top-left corner to (-50,-50).
79
+ EXPECT_NEAR(mapped.x, -50.0, 0.5);
80
+ EXPECT_NEAR(mapped.y, -50.0, 0.5);
81
+
82
+ // ...and the centre stays put, which is what "anchored" means.
83
+ EXPECT(mapPoint(child, root, 50.0F, 50.0F, &mapped));
84
+ EXPECT_NEAR(mapped.x, 50.0, 0.5);
85
+ EXPECT_NEAR(mapped.y, 50.0, 0.5);
86
+
87
+ g_object_unref(root);
88
+ }
89
+
90
+ TEST(transform_follows_into_hit_testing) {
91
+ // The reason a transform is composed during allocation rather than at paint
92
+ // time: a transformed view has to be picked where it looks, not where its
93
+ // frame says it is.
94
+ RnView *root = rn_view_new(1);
95
+ g_object_ref_sink(root);
96
+ RnView *child = addChild(root, 12, 0.0F, 0.0F, 100.0F, 100.0F);
97
+
98
+ graphene_matrix_t matrix;
99
+ const graphene_point3d_t offset = {200.0F, 0.0F, 0.0F};
100
+ graphene_matrix_init_translate(&matrix, &offset);
101
+ rn_view_set_transform(child, &matrix);
102
+ layout(root, 400, 400);
103
+
104
+ graphene_rect_t bounds;
105
+ EXPECT(gtk_widget_compute_bounds(GTK_WIDGET(child), GTK_WIDGET(root), &bounds));
106
+ EXPECT_NEAR(bounds.origin.x, 200.0, 0.5);
107
+
108
+ g_object_unref(root);
109
+ }
110
+
111
+ TEST(clearing_a_transform_restores_the_frame) {
112
+ RnView *root = rn_view_new(1);
113
+ g_object_ref_sink(root);
114
+ RnView *child = addChild(root, 13, 10.0F, 10.0F, 50.0F, 50.0F);
115
+
116
+ graphene_matrix_t matrix;
117
+ const graphene_point3d_t offset = {100.0F, 0.0F, 0.0F};
118
+ graphene_matrix_init_translate(&matrix, &offset);
119
+ rn_view_set_transform(child, &matrix);
120
+ layout(root, 400, 400);
121
+
122
+ rn_view_set_transform(child, nullptr);
123
+ layout(root, 400, 400);
124
+
125
+ graphene_rect_t bounds;
126
+ EXPECT(gtk_widget_compute_bounds(GTK_WIDGET(child), GTK_WIDGET(root), &bounds));
127
+ EXPECT_NEAR(bounds.origin.x, 10.0, 0.5);
128
+
129
+ g_object_unref(root);
130
+ }
131
+
132
+ TEST(an_identity_transform_costs_nothing) {
133
+ RnView *view = rn_view_new(1);
134
+ g_object_ref_sink(view);
135
+
136
+ graphene_matrix_t identity;
137
+ graphene_matrix_init_identity(&identity);
138
+ rn_view_set_transform(view, &identity);
139
+
140
+ // Not just an optimisation: an identity matrix must not make the view behave
141
+ // as though it were transformed.
142
+ char *description = rn_view_describe_tree(view);
143
+ EXPECT(description != nullptr);
144
+ g_free(description);
145
+
146
+ g_object_unref(view);
147
+ }
148
+
149
+ TEST(display_none_takes_a_view_out_of_layout) {
150
+ RnView *root = rn_view_new(1);
151
+ g_object_ref_sink(root);
152
+ RnView *child = addChild(root, 14, 0.0F, 0.0F, 100.0F, 100.0F);
153
+
154
+ rn_view_set_hidden(child, TRUE);
155
+ // RnLayout skips a child that should not be laid out, so an invisible view is
156
+ // neither placed nor painted.
157
+ EXPECT(!gtk_widget_should_layout(GTK_WIDGET(child)));
158
+
159
+ layout(root, 400, 400);
160
+ g_object_unref(root);
161
+ }
162
+
163
+
164
+ // A back face turned away from the viewer is hidden, and says so in the tree
165
+ // -- without which the only prop here that removes a view entirely is the one
166
+ // the dump cannot show, and scripts/compare_hosts.sh compares two views that
167
+ // look identical and are not.
168
+ TEST(a_back_face_turned_away_is_hidden) {
169
+ RnView *view = rn_view_new(22);
170
+ g_object_ref_sink(view);
171
+ rn_view_set_frame(view, 0.0F, 0.0F, 64.0F, 64.0F);
172
+ rn_view_set_hides_back_face(view, TRUE);
173
+
174
+ graphene_matrix_t flipped;
175
+ graphene_matrix_init_from_float(&flipped, kFlippedAboutY);
176
+ rn_view_set_transform(view, &flipped);
177
+ EXPECT(!gtk_widget_get_visible(GTK_WIDGET(view)));
178
+
179
+ char *description = rn_view_describe_tree(view);
180
+ EXPECT(std::string(description).find(" hidden") != std::string::npos);
181
+ g_free(description);
182
+
183
+ // Turning the prop off shows it again. The early return this used to have
184
+ // left the widget hidden for good.
185
+ rn_view_set_hides_back_face(view, FALSE);
186
+ EXPECT(gtk_widget_get_visible(GTK_WIDGET(view)));
187
+
188
+ g_object_unref(view);
189
+ }
190
+
191
+ // The two reasons a view can be hidden are independent, and Fabric applies
192
+ // them through different calls: props first, then layout metrics. So a card
193
+ // turned away from the viewer had `display: none`'s "no" written over the top
194
+ // of it on the very same mount, and came back.
195
+ TEST(display_none_and_a_back_face_do_not_cancel_each_other) {
196
+ RnView *view = rn_view_new(22);
197
+ g_object_ref_sink(view);
198
+ rn_view_set_hides_back_face(view, TRUE);
199
+
200
+ graphene_matrix_t flipped;
201
+ graphene_matrix_init_from_float(&flipped, kFlippedAboutY);
202
+ rn_view_set_transform(view, &flipped);
203
+
204
+ // What GtkMountingManager says about every view that is not display: none.
205
+ rn_view_set_hidden(view, FALSE);
206
+ EXPECT(!gtk_widget_get_visible(GTK_WIDGET(view)));
207
+
208
+ // And the other way: facing the viewer must not reveal what the app hid.
209
+ rn_view_set_hidden(view, TRUE);
210
+ rn_view_set_transform(view, nullptr);
211
+ EXPECT(!gtk_widget_get_visible(GTK_WIDGET(view)));
212
+
213
+ g_object_unref(view);
214
+ }
215
+
216
+ TEST(borders_and_radii_are_accepted_and_described) {
217
+ RnView *view = rn_view_new(20);
218
+ g_object_ref_sink(view);
219
+ rn_view_set_frame(view, 0.0F, 0.0F, 100.0F, 100.0F);
220
+
221
+ const graphene_size_t radii[4] = {{8.0F, 8.0F}, {8.0F, 8.0F}, {0.0F, 0.0F}, {0.0F, 0.0F}};
222
+ rn_view_set_border_radii(view, radii);
223
+
224
+ const float widths[4] = {2.0F, 2.0F, 2.0F, 2.0F};
225
+ const GdkRGBA colors[4] = {
226
+ {1.0F, 0.0F, 0.0F, 1.0F}, {1.0F, 0.0F, 0.0F, 1.0F},
227
+ {1.0F, 0.0F, 0.0F, 1.0F}, {1.0F, 0.0F, 0.0F, 1.0F}};
228
+ rn_view_set_borders(view, widths, colors);
229
+
230
+ // Clearing must be possible too; a view that loses its border should lose it.
231
+ rn_view_set_border_radii(view, nullptr);
232
+ rn_view_set_borders(view, nullptr, nullptr);
233
+
234
+ g_object_unref(view);
235
+ }
236
+
237
+ TEST(z_index_reorders_painting_not_the_child_list) {
238
+ RnView *root = rn_view_new(1);
239
+ g_object_ref_sink(root);
240
+ RnView *first = addChild(root, 30, 0.0F, 0.0F, 10.0F, 10.0F);
241
+ RnView *second = rn_view_new(31);
242
+ g_object_ref_sink(second);
243
+ rn_view_insert_child(root, second, 1);
244
+
245
+ rn_view_set_z_index(first, 10);
246
+ rn_view_set_z_index(second, 1);
247
+
248
+ // Painting order changes, but Fabric indexes into the child list on every
249
+ // Insert and Remove, so that list must not be resorted.
250
+ EXPECT_EQ(rn_view_get_tag(RN_VIEW(gtk_widget_get_first_child(GTK_WIDGET(root)))), 30);
251
+ EXPECT_EQ(rn_view_get_tag(RN_VIEW(gtk_widget_get_last_child(GTK_WIDGET(root)))), 31);
252
+
253
+ g_object_unref(root);
254
+ }
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "basalt-gtk",
3
+ "version": "0.0.0-rc5",
4
+ "description": "The Linux view layer for basalt-core: a GTK4 widget 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-gtk"
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,21 @@
1
+ /**
2
+ * What React Native's CLI reads out of this package.
3
+ *
4
+ * Just the `run-linux` command. The platform *declarations* -- which make
5
+ * `--platform linux` a thing the CLI accepts at all -- are in
6
+ * basalt-core, because they are the same on every desktop and an app
7
+ * targeting only macOS 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 both packages installed gets the platforms from one and
11
+ * the command from the other without either knowing about the other.
12
+ *
13
+ * @format
14
+ */
15
+
16
+ // @ts-check
17
+ 'use strict';
18
+
19
+ module.exports = {
20
+ commands: [require('./cli/runLinux')],
21
+ };