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.
- package/LICENSE +21 -0
- package/cli/runLinux.js +29 -0
- package/cli/shared.js +75 -0
- package/native/CMakeLists.txt +362 -0
- package/native/gtk/ColorSchemeGtk.cpp +58 -0
- package/native/gtk/ComponentRegistryGtk.cpp +101 -0
- package/native/gtk/FontRegistryFontconfig.cpp +127 -0
- package/native/gtk/GtkAnimationChoreographer.cpp +73 -0
- package/native/gtk/GtkAnimationChoreographer.h +49 -0
- package/native/gtk/GtkDropTarget.cpp +269 -0
- package/native/gtk/GtkDropTarget.h +40 -0
- package/native/gtk/GtkFocus.cpp +296 -0
- package/native/gtk/GtkFocus.h +113 -0
- package/native/gtk/GtkImageLoader.cpp +128 -0
- package/native/gtk/GtkImageLoader.h +87 -0
- package/native/gtk/GtkKeyEvents.cpp +91 -0
- package/native/gtk/GtkKeyEvents.h +21 -0
- package/native/gtk/GtkMenuBar.cpp +45 -0
- package/native/gtk/GtkMountingManager.cpp +1027 -0
- package/native/gtk/GtkMountingManager.h +146 -0
- package/native/gtk/GtkRunLoopObserver.cpp +77 -0
- package/native/gtk/GtkRunLoopObserver.h +41 -0
- package/native/gtk/GtkScrollView.cpp +626 -0
- package/native/gtk/GtkScrollView.h +209 -0
- package/native/gtk/GtkSkiaContext.cpp +248 -0
- package/native/gtk/GtkSkiaContext.h +62 -0
- package/native/gtk/GtkSkiaModule.cpp +80 -0
- package/native/gtk/GtkSkiaModule.h +51 -0
- package/native/gtk/GtkSkiaPeer.cpp +285 -0
- package/native/gtk/GtkSkiaPeer.h +63 -0
- package/native/gtk/GtkTextInput.cpp +428 -0
- package/native/gtk/GtkTextInput.h +123 -0
- package/native/gtk/GtkTextPeer.cpp +425 -0
- package/native/gtk/GtkTextPeer.h +87 -0
- package/native/gtk/GtkTitleBar.cpp +273 -0
- package/native/gtk/GtkTitleBar.h +96 -0
- package/native/gtk/GtkTitleBarLayout.cpp +45 -0
- package/native/gtk/GtkTitleBarLayout.h +36 -0
- package/native/gtk/GtkTouchDispatcher.cpp +671 -0
- package/native/gtk/GtkTouchDispatcher.h +139 -0
- package/native/gtk/GtkWindowControl.cpp +223 -0
- package/native/gtk/GtkWindowModule.cpp +194 -0
- package/native/gtk/GtkWindowModule.h +62 -0
- package/native/gtk/PangoTextLayout.cpp +295 -0
- package/native/gtk/PangoTextLayout.h +54 -0
- package/native/gtk/PangoTextLayoutManager.cpp +140 -0
- package/native/gtk/PlatformServicesGtk.cpp +583 -0
- package/native/gtk/RnView.cpp +1407 -0
- package/native/gtk/RnView.h +339 -0
- package/native/gtk/demo_layout_gtk.cpp +57 -0
- package/native/gtk/main_gtk.cpp +1981 -0
- package/native/gtk/mount_harness_gtk.cpp +143 -0
- package/native/tests/TestMainGtk.cpp +25 -0
- package/native/tests/notification_stub.cpp +103 -0
- package/native/tests/test_accessibility.cpp +251 -0
- package/native/tests/test_focus.cpp +194 -0
- package/native/tests/test_hittest.cpp +242 -0
- package/native/tests/test_image.cpp +135 -0
- package/native/tests/test_key_events_gtk.cpp +112 -0
- package/native/tests/test_mounting.cpp +415 -0
- package/native/tests/test_scrollview.cpp +258 -0
- package/native/tests/test_skia_texture.cpp +120 -0
- package/native/tests/test_text.cpp +147 -0
- package/native/tests/test_textinput.cpp +598 -0
- package/native/tests/test_titlebar.cpp +137 -0
- package/native/tests/test_titlebar_drag.cpp +138 -0
- package/native/tests/test_version.cpp +49 -0
- package/native/tests/test_view.cpp +162 -0
- package/native/tests/test_viewprops.cpp +254 -0
- package/package.json +26 -0
- 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
|
+
}
|