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,137 @@
|
|
|
1
|
+
// The title bar's metrics and what it does to the window.
|
|
2
|
+
//
|
|
3
|
+
// What the two styles look like is in the screenshots that went with phase 58;
|
|
4
|
+
// what is assertable is the arithmetic an app lays itself out against, and
|
|
5
|
+
// whether the window really took the style rather than the metrics being
|
|
6
|
+
// computed from a flag nothing acted on.
|
|
7
|
+
|
|
8
|
+
#include "TestHarness.h"
|
|
9
|
+
|
|
10
|
+
#include "GtkTitleBar.h"
|
|
11
|
+
|
|
12
|
+
#include <sstream>
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
// A window and the controls the host overlays, built the way main_gtk.cpp
|
|
17
|
+
// builds them. Never shown: the metrics are a property of the widgets rather
|
|
18
|
+
// than of anything on screen.
|
|
19
|
+
struct Fixture {
|
|
20
|
+
GtkWindow *window;
|
|
21
|
+
GtkWidget *controls;
|
|
22
|
+
|
|
23
|
+
Fixture() {
|
|
24
|
+
window = GTK_WINDOW(gtk_window_new());
|
|
25
|
+
controls = gtk_window_controls_new(GTK_PACK_END);
|
|
26
|
+
GtkWidget *overlay = gtk_overlay_new();
|
|
27
|
+
gtk_overlay_add_overlay(GTK_OVERLAY(overlay), controls);
|
|
28
|
+
gtk_window_set_child(window, overlay);
|
|
29
|
+
basalt::titleBar().attach(window, controls);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
~Fixture() { gtk_window_destroy(window); }
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
} // namespace
|
|
36
|
+
|
|
37
|
+
TEST(titlebar_native_style_costs_the_app_nothing) {
|
|
38
|
+
Fixture fixture;
|
|
39
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Native);
|
|
40
|
+
|
|
41
|
+
// GTK's decorations sit above the child rather than over it, so they take
|
|
42
|
+
// nothing from the app. Windows and macOS report the same zeroes, arrived at
|
|
43
|
+
// differently -- there the system's caption is outside the client area.
|
|
44
|
+
const auto metrics = basalt::titleBar().metrics();
|
|
45
|
+
EXPECT(metrics.style == basalt::TitleBarStyle::Native);
|
|
46
|
+
EXPECT_EQ(metrics.height, 0.0);
|
|
47
|
+
EXPECT_EQ(metrics.buttonsWidth, 0.0);
|
|
48
|
+
EXPECT(gtk_window_get_decorated(fixture.window));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
TEST(titlebar_hidden_style_reports_what_to_leave_clear) {
|
|
52
|
+
Fixture fixture;
|
|
53
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Hidden);
|
|
54
|
+
|
|
55
|
+
const auto metrics = basalt::titleBar().metrics();
|
|
56
|
+
EXPECT(metrics.style == basalt::TitleBarStyle::Hidden);
|
|
57
|
+
// Measured from the controls rather than assumed: how big they are is the
|
|
58
|
+
// theme's business, and a hard-coded size would be wrong on every desktop
|
|
59
|
+
// that disagrees.
|
|
60
|
+
EXPECT(metrics.height > 0.0);
|
|
61
|
+
EXPECT(metrics.buttonsWidth > 0.0);
|
|
62
|
+
|
|
63
|
+
// The window really did take the style.
|
|
64
|
+
EXPECT(!gtk_window_get_decorated(fixture.window));
|
|
65
|
+
// And the host's stand-in buttons are up, because GTK's own went with the
|
|
66
|
+
// decorations -- which is where this host differs from the other two.
|
|
67
|
+
EXPECT(gtk_widget_get_visible(fixture.controls));
|
|
68
|
+
|
|
69
|
+
// Back again, so an unmounted request restores the window.
|
|
70
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Native);
|
|
71
|
+
EXPECT(gtk_window_get_decorated(fixture.window));
|
|
72
|
+
EXPECT(!gtk_widget_get_visible(fixture.controls));
|
|
73
|
+
EXPECT_EQ(basalt::titleBar().metrics().height, 0.0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
TEST(titlebar_takes_a_title) {
|
|
77
|
+
Fixture fixture;
|
|
78
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Native);
|
|
79
|
+
basalt::titleBar().setTitle(std::string("Inbox"));
|
|
80
|
+
|
|
81
|
+
const char *title = gtk_window_get_title(fixture.window);
|
|
82
|
+
EXPECT_EQ(std::string(title != nullptr ? title : ""), std::string("Inbox"));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The caption buttons' functions, for an app drawing its own header.
|
|
86
|
+
//
|
|
87
|
+
// What these *do* was checked live -- a probe drove the window from 900x700 to
|
|
88
|
+
// maximised and back on both hosts, then minimised and closed it. What a test
|
|
89
|
+
// can pin is the half that has no window on the other end: the module outlives
|
|
90
|
+
// the window at shutdown, and a JavaScript call arriving then must not take the
|
|
91
|
+
// process with it.
|
|
92
|
+
TEST(titlebar_actions_are_safe_with_no_window) {
|
|
93
|
+
basalt::titleBar().attach(nullptr, nullptr);
|
|
94
|
+
basalt::titleBar().minimize();
|
|
95
|
+
basalt::titleBar().toggleMaximize();
|
|
96
|
+
basalt::titleBar().close();
|
|
97
|
+
basalt::titleBar().startDrag();
|
|
98
|
+
EXPECT(true);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
TEST(titlebar_toggles_maximize) {
|
|
102
|
+
Fixture fixture;
|
|
103
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Native);
|
|
104
|
+
|
|
105
|
+
// An unmapped window still records the request, which is what this asks
|
|
106
|
+
// about -- whether the toggle reads the state and flips it rather than
|
|
107
|
+
// always maximising.
|
|
108
|
+
basalt::titleBar().toggleMaximize();
|
|
109
|
+
EXPECT(gtk_window_is_maximized(fixture.window));
|
|
110
|
+
basalt::titleBar().toggleMaximize();
|
|
111
|
+
EXPECT(!gtk_window_is_maximized(fixture.window));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// The window going away underneath it, which is what happens at quit and what
|
|
115
|
+
// Window.close() does on purpose -- with JavaScript still running and still
|
|
116
|
+
// able to call any of this.
|
|
117
|
+
TEST(titlebar_survives_its_window_being_destroyed) {
|
|
118
|
+
GtkWindow *window = GTK_WINDOW(gtk_window_new());
|
|
119
|
+
GtkWidget *controls = gtk_window_controls_new(GTK_PACK_END);
|
|
120
|
+
GtkWidget *overlay = gtk_overlay_new();
|
|
121
|
+
gtk_overlay_add_overlay(GTK_OVERLAY(overlay), controls);
|
|
122
|
+
gtk_window_set_child(window, overlay);
|
|
123
|
+
|
|
124
|
+
basalt::titleBar().attach(window, controls);
|
|
125
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Hidden);
|
|
126
|
+
|
|
127
|
+
gtk_window_destroy(window);
|
|
128
|
+
|
|
129
|
+
// Every one of these is reachable from JavaScript after the window has gone.
|
|
130
|
+
basalt::titleBar().setTitle(std::string("late"));
|
|
131
|
+
basalt::titleBar().setColors(0xFF4285F4u, std::nullopt, std::nullopt);
|
|
132
|
+
basalt::titleBar().minimize();
|
|
133
|
+
basalt::titleBar().toggleMaximize();
|
|
134
|
+
basalt::titleBar().close();
|
|
135
|
+
const auto metrics = basalt::titleBar().metrics();
|
|
136
|
+
EXPECT_EQ(metrics.height, 0.0);
|
|
137
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Which parts of an app-drawn header drag the window.
|
|
2
|
+
//
|
|
3
|
+
// The same questions tests/test_win32_titlebar.cpp asks of Windows, in the
|
|
4
|
+
// same order: a label inside a drag region drags, a no-drag region inside one
|
|
5
|
+
// stays pressable, a drag region nested inside *that* drags again, and a point
|
|
6
|
+
// under nothing marked is ordinary client area. Most of what this tests is
|
|
7
|
+
// whether the two desktops still agree, which is why the shape is copied
|
|
8
|
+
// rather than reinvented.
|
|
9
|
+
//
|
|
10
|
+
// What it cannot test is the move itself. gdk_toplevel_begin_move hands the
|
|
11
|
+
// window to the compositor, which needs a real pointer grab from a real press;
|
|
12
|
+
// synthesising one needs a session this suite does not have. So the seam is
|
|
13
|
+
// the hit test -- the part that decides *whether* to begin a move -- and the
|
|
14
|
+
// begin itself is left to the end-to-end suite and to a person with a mouse.
|
|
15
|
+
|
|
16
|
+
#include "TestHarness.h"
|
|
17
|
+
|
|
18
|
+
#include "GtkTitleBarLayout.h"
|
|
19
|
+
#include "RnView.h"
|
|
20
|
+
#include "TitleBarRegions.h"
|
|
21
|
+
|
|
22
|
+
#include <sstream>
|
|
23
|
+
|
|
24
|
+
namespace {
|
|
25
|
+
|
|
26
|
+
RnView *addChild(RnView *parent,
|
|
27
|
+
int tag,
|
|
28
|
+
float x,
|
|
29
|
+
float y,
|
|
30
|
+
float width,
|
|
31
|
+
float height,
|
|
32
|
+
const char *nativeId = nullptr) {
|
|
33
|
+
RnView *child = rn_view_new(tag);
|
|
34
|
+
g_object_ref_sink(child);
|
|
35
|
+
rn_view_set_frame(child, x, y, width, height);
|
|
36
|
+
rn_view_set_native_id(child, nativeId);
|
|
37
|
+
rn_view_insert_child(parent, child, 0);
|
|
38
|
+
return child;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// A real, shown window, for the reason tests/test_hittest.cpp gives: this
|
|
42
|
+
// walks up from gtk_widget_pick, and pick skips widgets that are not mapped.
|
|
43
|
+
// A tree allocated in isolation would be picked as nothing at all -- and
|
|
44
|
+
// "nothing" answers false, which is also the honest answer for a point that
|
|
45
|
+
// does not drag. Every test below would pass without testing anything.
|
|
46
|
+
//
|
|
47
|
+
// That is what the first test guards against: it expects *true*, so if the
|
|
48
|
+
// tree ever stops being allocated the suite fails rather than going quietly
|
|
49
|
+
// green.
|
|
50
|
+
struct Scene {
|
|
51
|
+
GtkWidget *window;
|
|
52
|
+
RnView *root;
|
|
53
|
+
|
|
54
|
+
explicit Scene(int width, int height) {
|
|
55
|
+
root = rn_view_new(1);
|
|
56
|
+
window = gtk_window_new();
|
|
57
|
+
gtk_window_set_default_size(GTK_WINDOW(window), width, height);
|
|
58
|
+
gtk_window_set_child(GTK_WINDOW(window), GTK_WIDGET(root));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
~Scene() { gtk_window_destroy(GTK_WINDOW(window)); }
|
|
62
|
+
|
|
63
|
+
Scene(const Scene &) = delete;
|
|
64
|
+
Scene &operator=(const Scene &) = delete;
|
|
65
|
+
|
|
66
|
+
void show() {
|
|
67
|
+
gtk_widget_set_visible(window, TRUE);
|
|
68
|
+
pump();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Bounded, so a machine that never produces a frame fails an assertion
|
|
72
|
+
// rather than hanging the suite.
|
|
73
|
+
static void pump() {
|
|
74
|
+
const gint64 deadline = g_get_monotonic_time() + 500000; // 500ms
|
|
75
|
+
while (g_get_monotonic_time() < deadline) {
|
|
76
|
+
while (g_main_context_iteration(nullptr, FALSE)) {
|
|
77
|
+
}
|
|
78
|
+
g_usleep(1000);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
} // namespace
|
|
84
|
+
|
|
85
|
+
TEST(titlebar_a_label_inside_a_drag_region_drags_the_window) {
|
|
86
|
+
Scene scene(800, 600);
|
|
87
|
+
RnView *header = addChild(scene.root, 2, 0.0F, 0.0F, 800.0F, 32.0F, basalt::kTitleBarDragRegionId);
|
|
88
|
+
// Unmarked, and inside the header: the marked ancestor decides.
|
|
89
|
+
addChild(header, 3, 10.0F, 5.0F, 100.0F, 20.0F);
|
|
90
|
+
scene.show();
|
|
91
|
+
|
|
92
|
+
EXPECT(basalt::isTitleBarDragRegionAt(scene.root, 20.0, 10.0));
|
|
93
|
+
EXPECT(basalt::isTitleBarDragRegionAt(scene.root, 400.0, 10.0));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
TEST(titlebar_a_no_drag_region_inside_a_drag_region_stays_pressable) {
|
|
97
|
+
Scene scene(800, 600);
|
|
98
|
+
RnView *header = addChild(scene.root, 2, 0.0F, 0.0F, 800.0F, 32.0F, basalt::kTitleBarDragRegionId);
|
|
99
|
+
RnView *button =
|
|
100
|
+
addChild(header, 3, 600.0F, 0.0F, 50.0F, 32.0F, basalt::kTitleBarNoDragRegionId);
|
|
101
|
+
scene.show();
|
|
102
|
+
|
|
103
|
+
EXPECT(!basalt::isTitleBarDragRegionAt(scene.root, 620.0, 10.0));
|
|
104
|
+
|
|
105
|
+
// And a drag region nested inside that button drags again: the nearest
|
|
106
|
+
// marker decides, not the outermost.
|
|
107
|
+
addChild(button, 4, 5.0F, 5.0F, 10.0F, 10.0F, basalt::kTitleBarDragRegionId);
|
|
108
|
+
Scene::pump();
|
|
109
|
+
EXPECT(basalt::isTitleBarDragRegionAt(scene.root, 607.0, 7.0));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
TEST(titlebar_a_point_under_nothing_marked_is_ordinary_client_area) {
|
|
113
|
+
Scene scene(800, 600);
|
|
114
|
+
addChild(scene.root, 2, 0.0F, 0.0F, 800.0F, 32.0F, basalt::kTitleBarDragRegionId);
|
|
115
|
+
addChild(scene.root, 3, 0.0F, 32.0F, 800.0F, 568.0F);
|
|
116
|
+
scene.show();
|
|
117
|
+
|
|
118
|
+
EXPECT(!basalt::isTitleBarDragRegionAt(scene.root, 400.0, 300.0));
|
|
119
|
+
// A miss altogether -- outside the root -- is not a drag either.
|
|
120
|
+
EXPECT(!basalt::isTitleBarDragRegionAt(scene.root, 900.0, 10.0));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
TEST(titlebar_a_root_marked_whole_drags_everywhere_it_is_not_overridden) {
|
|
124
|
+
// An app that wants its entire surface to drag, which is a real shape for a
|
|
125
|
+
// small tool window, and the case that fails if the walk stops before the
|
|
126
|
+
// root rather than after it.
|
|
127
|
+
Scene scene(400, 300);
|
|
128
|
+
rn_view_set_native_id(scene.root, basalt::kTitleBarDragRegionId);
|
|
129
|
+
addChild(scene.root, 2, 100.0F, 100.0F, 80.0F, 40.0F, basalt::kTitleBarNoDragRegionId);
|
|
130
|
+
scene.show();
|
|
131
|
+
|
|
132
|
+
EXPECT(basalt::isTitleBarDragRegionAt(scene.root, 10.0, 10.0));
|
|
133
|
+
EXPECT(!basalt::isTitleBarDragRegionAt(scene.root, 140.0, 120.0));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
TEST(titlebar_drag_regions_are_safe_without_a_tree) {
|
|
137
|
+
EXPECT(!basalt::isTitleBarDragRegionAt(nullptr, 0.0, 0.0));
|
|
138
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Parsing the React Native version this host was built against.
|
|
2
|
+
//
|
|
3
|
+
// It matters because React Native's development builds compare it against the
|
|
4
|
+
// bundle's own version and complain loudly when they disagree, and because
|
|
5
|
+
// ReactCxxPlatform hardcodes 1000.0.0, so the comparison can never pass without
|
|
6
|
+
// this. A prerelease tag is the interesting case: read carelessly, "0.88.0-rc.2"
|
|
7
|
+
// turns into patch 0 and a silently discarded tag, or worse, a patch parsed
|
|
8
|
+
// from "0-rc".
|
|
9
|
+
|
|
10
|
+
#include "TestHarness.h"
|
|
11
|
+
|
|
12
|
+
#include "PlatformConstantsModule.h"
|
|
13
|
+
|
|
14
|
+
using basalt::DesktopPlatformConstantsModule;
|
|
15
|
+
|
|
16
|
+
TEST(version_parses_a_stable_release) {
|
|
17
|
+
const auto version = DesktopPlatformConstantsModule::parseVersion("0.87.1");
|
|
18
|
+
EXPECT_EQ(version.major, 0);
|
|
19
|
+
EXPECT_EQ(version.minor, 87);
|
|
20
|
+
EXPECT_EQ(version.patch, 1);
|
|
21
|
+
EXPECT(version.prerelease.empty());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
TEST(version_parses_the_main_placeholder) {
|
|
25
|
+
// What React Native calls itself on main, and the value this whole class
|
|
26
|
+
// exists to stop being reported against a release.
|
|
27
|
+
const auto version = DesktopPlatformConstantsModule::parseVersion("1000.0.0");
|
|
28
|
+
EXPECT_EQ(version.major, 1000);
|
|
29
|
+
EXPECT_EQ(version.minor, 0);
|
|
30
|
+
EXPECT_EQ(version.patch, 0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
TEST(version_keeps_a_prerelease_tag_out_of_the_patch) {
|
|
34
|
+
const auto version = DesktopPlatformConstantsModule::parseVersion("0.88.0-rc.2");
|
|
35
|
+
EXPECT_EQ(version.major, 0);
|
|
36
|
+
EXPECT_EQ(version.minor, 88);
|
|
37
|
+
EXPECT_EQ(version.patch, 0);
|
|
38
|
+
EXPECT_EQ(version.prerelease, std::string("rc.2"));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
TEST(version_survives_something_it_does_not_understand) {
|
|
42
|
+
// Never a crash and never a wrong number: a version that cannot be parsed
|
|
43
|
+
// reports zeroes, which disagrees with every bundle and so fails loudly
|
|
44
|
+
// rather than matching one by accident.
|
|
45
|
+
const auto version = DesktopPlatformConstantsModule::parseVersion("");
|
|
46
|
+
EXPECT_EQ(version.major, 0);
|
|
47
|
+
EXPECT_EQ(version.minor, 0);
|
|
48
|
+
EXPECT_EQ(version.patch, 0);
|
|
49
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Tests for the widget layer: frames, child ordering, scrolling and clipping.
|
|
2
|
+
//
|
|
3
|
+
// RnView has no React Native dependency, so these need no Fabric machinery.
|
|
4
|
+
|
|
5
|
+
#include "TestHarness.h"
|
|
6
|
+
|
|
7
|
+
#include "RnView.h"
|
|
8
|
+
|
|
9
|
+
#include <sstream>
|
|
10
|
+
|
|
11
|
+
namespace {
|
|
12
|
+
|
|
13
|
+
// Allocating a widget tree the way GTK would, so the layout manager runs.
|
|
14
|
+
void layout(RnView *root, int width, int height) {
|
|
15
|
+
gtk_widget_set_size_request(GTK_WIDGET(root), width, height);
|
|
16
|
+
gtk_widget_allocate(GTK_WIDGET(root), width, height, -1, nullptr);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
int childCount(RnView *view) {
|
|
20
|
+
int count = 0;
|
|
21
|
+
for (GtkWidget *child = gtk_widget_get_first_child(GTK_WIDGET(view)); child != nullptr;
|
|
22
|
+
child = gtk_widget_get_next_sibling(child)) {
|
|
23
|
+
++count;
|
|
24
|
+
}
|
|
25
|
+
return count;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
} // namespace
|
|
29
|
+
|
|
30
|
+
TEST(view_stores_its_tag_and_frame) {
|
|
31
|
+
RnView *view = rn_view_new(7);
|
|
32
|
+
g_object_ref_sink(view);
|
|
33
|
+
|
|
34
|
+
EXPECT_EQ(rn_view_get_tag(view), 7);
|
|
35
|
+
|
|
36
|
+
rn_view_set_frame(view, 1.5F, 2.5F, 30.0F, 40.0F);
|
|
37
|
+
graphene_rect_t frame;
|
|
38
|
+
rn_view_get_frame(view, &frame);
|
|
39
|
+
EXPECT_NEAR(frame.origin.x, 1.5, 0.001);
|
|
40
|
+
EXPECT_NEAR(frame.origin.y, 2.5, 0.001);
|
|
41
|
+
EXPECT_NEAR(frame.size.width, 30.0, 0.001);
|
|
42
|
+
EXPECT_NEAR(frame.size.height, 40.0, 0.001);
|
|
43
|
+
|
|
44
|
+
g_object_unref(view);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
TEST(children_insert_at_the_requested_index) {
|
|
48
|
+
RnView *parent = rn_view_new(1);
|
|
49
|
+
g_object_ref_sink(parent);
|
|
50
|
+
|
|
51
|
+
RnView *a = rn_view_new(10);
|
|
52
|
+
RnView *b = rn_view_new(11);
|
|
53
|
+
RnView *c = rn_view_new(12);
|
|
54
|
+
g_object_ref_sink(a);
|
|
55
|
+
g_object_ref_sink(b);
|
|
56
|
+
g_object_ref_sink(c);
|
|
57
|
+
|
|
58
|
+
rn_view_insert_child(parent, a, 0);
|
|
59
|
+
rn_view_insert_child(parent, b, 1);
|
|
60
|
+
rn_view_insert_child(parent, c, 1);
|
|
61
|
+
|
|
62
|
+
EXPECT_EQ(childCount(parent), 3);
|
|
63
|
+
EXPECT_EQ(rn_view_get_tag(RN_VIEW(gtk_widget_get_first_child(GTK_WIDGET(parent)))), 10);
|
|
64
|
+
EXPECT_EQ(rn_view_get_tag(RN_VIEW(gtk_widget_get_next_sibling(
|
|
65
|
+
gtk_widget_get_first_child(GTK_WIDGET(parent))))),
|
|
66
|
+
12);
|
|
67
|
+
|
|
68
|
+
rn_view_remove_child(parent, c);
|
|
69
|
+
EXPECT_EQ(childCount(parent), 2);
|
|
70
|
+
|
|
71
|
+
g_object_unref(c);
|
|
72
|
+
g_object_unref(parent);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
TEST(layout_places_children_at_their_frames) {
|
|
76
|
+
RnView *parent = rn_view_new(1);
|
|
77
|
+
g_object_ref_sink(parent);
|
|
78
|
+
|
|
79
|
+
RnView *child = rn_view_new(2);
|
|
80
|
+
g_object_ref_sink(child);
|
|
81
|
+
rn_view_set_frame(child, 20.0F, 30.0F, 40.0F, 50.0F);
|
|
82
|
+
rn_view_insert_child(parent, child, 0);
|
|
83
|
+
|
|
84
|
+
layout(parent, 200, 200);
|
|
85
|
+
|
|
86
|
+
graphene_rect_t bounds;
|
|
87
|
+
EXPECT(gtk_widget_compute_bounds(GTK_WIDGET(child), GTK_WIDGET(parent), &bounds));
|
|
88
|
+
EXPECT_NEAR(bounds.origin.x, 20.0, 0.5);
|
|
89
|
+
EXPECT_NEAR(bounds.origin.y, 30.0, 0.5);
|
|
90
|
+
EXPECT_NEAR(bounds.size.width, 40.0, 0.5);
|
|
91
|
+
EXPECT_NEAR(bounds.size.height, 50.0, 0.5);
|
|
92
|
+
|
|
93
|
+
g_object_unref(parent);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
TEST(scroll_offset_shifts_children) {
|
|
97
|
+
RnView *scroller = rn_view_new(1);
|
|
98
|
+
g_object_ref_sink(scroller);
|
|
99
|
+
|
|
100
|
+
RnView *content = rn_view_new(2);
|
|
101
|
+
g_object_ref_sink(content);
|
|
102
|
+
rn_view_set_frame(content, 0.0F, 0.0F, 100.0F, 1000.0F);
|
|
103
|
+
rn_view_insert_child(scroller, content, 0);
|
|
104
|
+
|
|
105
|
+
layout(scroller, 100, 200);
|
|
106
|
+
graphene_rect_t before;
|
|
107
|
+
EXPECT(gtk_widget_compute_bounds(GTK_WIDGET(content), GTK_WIDGET(scroller), &before));
|
|
108
|
+
EXPECT_NEAR(before.origin.y, 0.0, 0.5);
|
|
109
|
+
|
|
110
|
+
// Scrolling moves the children, not the scroller's own frame. This is what
|
|
111
|
+
// makes gtk_widget_pick follow the scroll for free.
|
|
112
|
+
rn_view_set_scroll_offset(scroller, 0.0, 150.0);
|
|
113
|
+
layout(scroller, 100, 200);
|
|
114
|
+
|
|
115
|
+
graphene_rect_t after;
|
|
116
|
+
EXPECT(gtk_widget_compute_bounds(GTK_WIDGET(content), GTK_WIDGET(scroller), &after));
|
|
117
|
+
EXPECT_NEAR(after.origin.y, -150.0, 0.5);
|
|
118
|
+
|
|
119
|
+
double x = 0;
|
|
120
|
+
double y = 0;
|
|
121
|
+
rn_view_get_scroll_offset(scroller, &x, &y);
|
|
122
|
+
EXPECT_NEAR(x, 0.0, 0.001);
|
|
123
|
+
EXPECT_NEAR(y, 150.0, 0.001);
|
|
124
|
+
|
|
125
|
+
g_object_unref(scroller);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
TEST(measure_reports_zero_so_gtk_never_second_guesses_yoga) {
|
|
129
|
+
RnView *view = rn_view_new(1);
|
|
130
|
+
g_object_ref_sink(view);
|
|
131
|
+
rn_view_set_frame(view, 0.0F, 0.0F, 123.0F, 456.0F);
|
|
132
|
+
|
|
133
|
+
int minimum = -1;
|
|
134
|
+
int natural = -1;
|
|
135
|
+
int minimumBaseline = 0;
|
|
136
|
+
int naturalBaseline = 0;
|
|
137
|
+
gtk_widget_measure(
|
|
138
|
+
GTK_WIDGET(view), GTK_ORIENTATION_HORIZONTAL, -1, &minimum, &natural, &minimumBaseline, &naturalBaseline);
|
|
139
|
+
|
|
140
|
+
// React Native is the only source of truth for size. If this ever reports the
|
|
141
|
+
// frame instead, GTK starts participating in layout and the two disagree.
|
|
142
|
+
EXPECT_EQ(minimum, 0);
|
|
143
|
+
EXPECT_EQ(natural, 0);
|
|
144
|
+
|
|
145
|
+
g_object_unref(view);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
TEST(dispose_unparents_children_rather_than_leaking_them) {
|
|
149
|
+
RnView *parent = rn_view_new(1);
|
|
150
|
+
g_object_ref_sink(parent);
|
|
151
|
+
|
|
152
|
+
RnView *child = rn_view_new(2);
|
|
153
|
+
g_object_ref_sink(child);
|
|
154
|
+
rn_view_insert_child(parent, child, 0);
|
|
155
|
+
|
|
156
|
+
// A Delete can arrive with children still attached. GTK warns and leaks if a
|
|
157
|
+
// widget is finalised while it still has any, so dispose unparents them.
|
|
158
|
+
g_object_unref(parent);
|
|
159
|
+
|
|
160
|
+
EXPECT(gtk_widget_get_parent(GTK_WIDGET(child)) == nullptr);
|
|
161
|
+
g_object_unref(child);
|
|
162
|
+
}
|