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,120 @@
1
+ // The one thing between Skia and the screen on this host: a pixel format.
2
+ //
3
+ // gtk/GtkSkiaPeer.cpp renders a `<Canvas>` into an `SkImageInfo::MakeN32Premul`
4
+ // surface and hands the bytes to `gdk_memory_texture_new` as
5
+ // `GDK_MEMORY_B8G8R8A8_PREMULTIPLIED`. That is a claim about two libraries
6
+ // agreeing on byte order, made in a comment, and it is the kind of claim that
7
+ // fails silently: every red in the app comes out blue, nothing logs, nothing
8
+ // crashes, and the bug looks like a Skia problem rather than a layout of bytes.
9
+ //
10
+ // So this draws known colours through the real path and reads them back. It
11
+ // does not test the peer's plumbing -- registration, sizing, the weak
12
+ // reference -- which needs a manager and therefore a runtime. It tests the
13
+ // assumption underneath all of it.
14
+ //
15
+ // Only built with Skia; see the CMakeLists.
16
+
17
+ #include "TestHarness.h"
18
+
19
+ #include <gtk/gtk.h>
20
+
21
+ #include "include/core/SkCanvas.h"
22
+ #include "include/core/SkColor.h"
23
+ #include "include/core/SkImageInfo.h"
24
+ #include "include/core/SkPaint.h"
25
+ #include "include/core/SkPixmap.h"
26
+ #include "include/core/SkRect.h"
27
+ #include "include/core/SkSurface.h"
28
+
29
+ #include <cstdint>
30
+ #include <sstream>
31
+ #include <vector>
32
+
33
+ namespace {
34
+
35
+ // The peer's conversion, on its own so the test runs the same three calls.
36
+ GdkTexture *toTexture(const sk_sp<SkSurface> &surface, int width, int height) {
37
+ SkPixmap pixels;
38
+ if (!surface->peekPixels(&pixels)) {
39
+ return nullptr;
40
+ }
41
+ GBytes *bytes = g_bytes_new(pixels.addr(), pixels.computeByteSize());
42
+ GdkTexture *texture = gdk_memory_texture_new(
43
+ width, height, GDK_MEMORY_B8G8R8A8_PREMULTIPLIED, bytes, pixels.rowBytes());
44
+ g_bytes_unref(bytes);
45
+ return texture;
46
+ }
47
+
48
+ struct Rgba {
49
+ uint8_t red = 0;
50
+ uint8_t green = 0;
51
+ uint8_t blue = 0;
52
+ uint8_t alpha = 0;
53
+ };
54
+
55
+ // One pixel out of a downloaded texture. `gdk_texture_download` writes
56
+ // GDK_MEMORY_DEFAULT, which is premultiplied BGRA -- so this names the channels
57
+ // rather than trusting the reader to remember which byte is which.
58
+ Rgba pixelAt(const std::vector<uint8_t> &data, int stride, int x, int y) {
59
+ const uint8_t *p = data.data() + static_cast<size_t>(y) * stride + static_cast<size_t>(x) * 4;
60
+ Rgba out;
61
+ out.blue = p[0];
62
+ out.green = p[1];
63
+ out.red = p[2];
64
+ out.alpha = p[3];
65
+ return out;
66
+ }
67
+
68
+ } // namespace
69
+
70
+ TEST(skia_a_canvas_surface_becomes_a_texture_of_the_same_colours) {
71
+ constexpr int kWidth = 4;
72
+ constexpr int kHeight = 2;
73
+
74
+ sk_sp<SkSurface> surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(kWidth, kHeight));
75
+ EXPECT(surface != nullptr);
76
+
77
+ SkCanvas *canvas = surface->getCanvas();
78
+ canvas->clear(SK_ColorTRANSPARENT);
79
+ SkPaint paint;
80
+ // Opaque red, and opaque blue beside it. Red alone would pass even if the
81
+ // two libraries disagreed and swapped it with blue -- there would be nothing
82
+ // to notice. Two channels that differ is what makes the swap visible.
83
+ paint.setColor(SK_ColorRED);
84
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
85
+ paint.setColor(SK_ColorBLUE);
86
+ canvas->drawRect(SkRect::MakeXYWH(1, 0, 1, 1), paint);
87
+
88
+ GdkTexture *texture = toTexture(surface, kWidth, kHeight);
89
+ EXPECT(texture != nullptr);
90
+ if (texture == nullptr) {
91
+ return;
92
+ }
93
+
94
+ EXPECT_EQ(gdk_texture_get_width(texture), kWidth);
95
+ EXPECT_EQ(gdk_texture_get_height(texture), kHeight);
96
+
97
+ const int stride = kWidth * 4;
98
+ std::vector<uint8_t> data(static_cast<size_t>(stride) * kHeight, 0);
99
+ gdk_texture_download(texture, data.data(), static_cast<gsize>(stride));
100
+
101
+ const Rgba red = pixelAt(data, stride, 0, 0);
102
+ EXPECT_EQ(static_cast<int>(red.red), 255);
103
+ EXPECT_EQ(static_cast<int>(red.green), 0);
104
+ EXPECT_EQ(static_cast<int>(red.blue), 0);
105
+ EXPECT_EQ(static_cast<int>(red.alpha), 255);
106
+
107
+ const Rgba blue = pixelAt(data, stride, 1, 0);
108
+ EXPECT_EQ(static_cast<int>(blue.red), 0);
109
+ EXPECT_EQ(static_cast<int>(blue.green), 0);
110
+ EXPECT_EQ(static_cast<int>(blue.blue), 255);
111
+ EXPECT_EQ(static_cast<int>(blue.alpha), 255);
112
+
113
+ // And the part nothing drew stays transparent, which is what lets a canvas
114
+ // sit over the view behind it rather than punching a black hole in it. The
115
+ // renderer clears to transparent before every picture for this reason.
116
+ const Rgba untouched = pixelAt(data, stride, 3, 1);
117
+ EXPECT_EQ(static_cast<int>(untouched.alpha), 0);
118
+
119
+ g_object_unref(texture);
120
+ }
@@ -0,0 +1,147 @@
1
+ // Tests for the Pango text layer.
2
+ //
3
+ // These assert relationships rather than pixel counts. Font metrics differ
4
+ // between machines and fontconfig setups, so "this string is 214.5 points wide"
5
+ // is not a fact worth encoding; "constraining the width makes it wrap, and
6
+ // wrapping makes it taller" is.
7
+
8
+ #include "TestHarness.h"
9
+
10
+ #include "PangoTextLayout.h"
11
+
12
+ #include <react/renderer/attributedstring/AttributedString.h>
13
+ #include <react/renderer/attributedstring/ParagraphAttributes.h>
14
+
15
+ #include <sstream>
16
+
17
+ using facebook::react::AttributedString;
18
+ using facebook::react::EllipsizeMode;
19
+ using facebook::react::FontWeight;
20
+ using facebook::react::ParagraphAttributes;
21
+ using facebook::react::TextAttributes;
22
+
23
+ namespace {
24
+
25
+ AttributedString makeText(const std::string &text, float fontSize = 16.0F) {
26
+ TextAttributes attributes;
27
+ attributes.fontSize = fontSize;
28
+
29
+ AttributedString::Fragment fragment;
30
+ fragment.string = text;
31
+ fragment.textAttributes = attributes;
32
+
33
+ AttributedString result;
34
+ result.appendFragment(std::move(fragment));
35
+ return result;
36
+ }
37
+
38
+ struct Measured {
39
+ float width;
40
+ float height;
41
+ };
42
+
43
+ Measured measure(const AttributedString &text, const ParagraphAttributes &attributes, float maxWidth) {
44
+ PangoLayout *layout = basalt::buildTextLayout(text, attributes, maxWidth);
45
+ Measured measured{};
46
+ basalt::textLayoutSize(layout, &measured.width, &measured.height);
47
+ g_object_unref(layout);
48
+ return measured;
49
+ }
50
+
51
+ const char *kLongText =
52
+ "Yoga asked Pango how wide this paragraph wants to be, and Pango answered in "
53
+ "1024ths of a pixel, which is a unit nobody enjoys debugging.";
54
+
55
+ } // namespace
56
+
57
+ TEST(text_has_a_size) {
58
+ const auto measured = measure(makeText("Hello"), ParagraphAttributes{}, -1.0F);
59
+ EXPECT(measured.width > 0.0F);
60
+ EXPECT(measured.height > 0.0F);
61
+ }
62
+
63
+ TEST(larger_font_measures_larger) {
64
+ const auto small = measure(makeText("Hello", 12.0F), ParagraphAttributes{}, -1.0F);
65
+ const auto large = measure(makeText("Hello", 36.0F), ParagraphAttributes{}, -1.0F);
66
+
67
+ EXPECT(large.width > small.width);
68
+ EXPECT(large.height > small.height);
69
+ }
70
+
71
+ TEST(constraining_the_width_wraps_and_grows_taller) {
72
+ const auto unconstrained = measure(makeText(kLongText), ParagraphAttributes{}, -1.0F);
73
+ const auto wrapped = measure(makeText(kLongText), ParagraphAttributes{}, 200.0F);
74
+
75
+ // Wrapping is the whole point: narrower, and more than one line tall.
76
+ EXPECT(wrapped.width <= 200.5F);
77
+ EXPECT(wrapped.width < unconstrained.width);
78
+ EXPECT(wrapped.height > unconstrained.height);
79
+ }
80
+
81
+ TEST(number_of_lines_truncates) {
82
+ ParagraphAttributes oneLine;
83
+ oneLine.maximumNumberOfLines = 1;
84
+ oneLine.ellipsizeMode = EllipsizeMode::Tail;
85
+
86
+ const auto unlimited = measure(makeText(kLongText), ParagraphAttributes{}, 200.0F);
87
+ const auto limited = measure(makeText(kLongText), oneLine, 200.0F);
88
+
89
+ EXPECT(limited.height < unlimited.height);
90
+ }
91
+
92
+ TEST(default_ellipsize_mode_does_not_collapse_a_paragraph) {
93
+ // React Native defaults ellipsizeMode to Tail, and Pango ellipsizes to a
94
+ // *single line* when no height is set. Translating that default faithfully
95
+ // once turned every wrapping paragraph into one line, so this is a
96
+ // regression test for the bug rather than a test of Pango.
97
+ ParagraphAttributes defaults;
98
+ defaults.ellipsizeMode = EllipsizeMode::Tail;
99
+ EXPECT_EQ(defaults.maximumNumberOfLines, 0);
100
+
101
+ const auto measured = measure(makeText(kLongText), defaults, 200.0F);
102
+ const auto oneLineTall = measure(makeText("Hello"), ParagraphAttributes{}, -1.0F);
103
+
104
+ EXPECT(measured.height > oneLineTall.height * 1.5F);
105
+ }
106
+
107
+ TEST(font_size_is_absolute_not_points) {
108
+ // set_size would resolve against the context's resolution, making a fontSize
109
+ // of 100 render at about 133px on a 96dpi context. React Native's fontSize is
110
+ // in logical pixels, so a single line of 100pt text should be roughly 100
111
+ // tall, not a third taller again.
112
+ const auto measured = measure(makeText("Hg", 100.0F), ParagraphAttributes{}, -1.0F);
113
+
114
+ EXPECT(measured.height > 90.0F);
115
+ EXPECT(measured.height < 130.0F);
116
+ }
117
+
118
+ TEST(bold_is_wider_than_regular) {
119
+ TextAttributes regular;
120
+ regular.fontSize = 24.0F;
121
+
122
+ TextAttributes bold;
123
+ bold.fontSize = 24.0F;
124
+ bold.fontWeight = FontWeight::Bold;
125
+
126
+ const auto build = [](const TextAttributes &attributes) {
127
+ AttributedString::Fragment fragment;
128
+ fragment.string = "Weight";
129
+ fragment.textAttributes = attributes;
130
+ AttributedString text;
131
+ text.appendFragment(std::move(fragment));
132
+ return text;
133
+ };
134
+
135
+ const auto regularSize = measure(build(regular), ParagraphAttributes{}, -1.0F);
136
+ const auto boldSize = measure(build(bold), ParagraphAttributes{}, -1.0F);
137
+
138
+ // Not guaranteed by typography in general, but true of every sans-serif
139
+ // family a desktop ships as its default. If this fails the font description
140
+ // is probably not being applied at all.
141
+ EXPECT(boldSize.width >= regularSize.width);
142
+ }
143
+
144
+ TEST(empty_text_measures_to_zero_width) {
145
+ const auto measured = measure(makeText(""), ParagraphAttributes{}, -1.0F);
146
+ EXPECT_NEAR(measured.width, 0.0, 0.001);
147
+ }