basalt-appkit 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/runMacos.js +33 -0
- package/cli/shared.js +75 -0
- package/native/CMakeLists.txt +298 -0
- package/native/appkit/AppKitAnimationChoreographer.h +54 -0
- package/native/appkit/AppKitAnimationChoreographer.mm +125 -0
- package/native/appkit/AppKitDropTarget.h +34 -0
- package/native/appkit/AppKitDropTarget.mm +292 -0
- package/native/appkit/AppKitFocus.h +120 -0
- package/native/appkit/AppKitFocus.mm +293 -0
- package/native/appkit/AppKitImageLoader.h +80 -0
- package/native/appkit/AppKitImageLoader.mm +156 -0
- package/native/appkit/AppKitKeyEvents.h +26 -0
- package/native/appkit/AppKitKeyEvents.mm +97 -0
- package/native/appkit/AppKitMenuBar.mm +424 -0
- package/native/appkit/AppKitMountingManager.h +135 -0
- package/native/appkit/AppKitMountingManager.mm +946 -0
- package/native/appkit/AppKitRunLoopObserver.h +34 -0
- package/native/appkit/AppKitRunLoopObserver.mm +42 -0
- package/native/appkit/AppKitScrollView.h +168 -0
- package/native/appkit/AppKitScrollView.mm +550 -0
- package/native/appkit/AppKitSkiaContext.h +33 -0
- package/native/appkit/AppKitSkiaContext.mm +255 -0
- package/native/appkit/AppKitSkiaModule.h +53 -0
- package/native/appkit/AppKitSkiaModule.mm +81 -0
- package/native/appkit/AppKitSkiaPeer.h +37 -0
- package/native/appkit/AppKitSkiaPeer.mm +123 -0
- package/native/appkit/AppKitSkiaView.h +62 -0
- package/native/appkit/AppKitSnapshot.h +21 -0
- package/native/appkit/AppKitSnapshot.mm +91 -0
- package/native/appkit/AppKitTextInput.h +140 -0
- package/native/appkit/AppKitTextInput.mm +588 -0
- package/native/appkit/AppKitTextPeer.h +74 -0
- package/native/appkit/AppKitTextPeer.mm +353 -0
- package/native/appkit/AppKitTitleBar.h +90 -0
- package/native/appkit/AppKitTitleBar.mm +186 -0
- package/native/appkit/AppKitTouchDispatcher.h +138 -0
- package/native/appkit/AppKitTouchDispatcher.mm +514 -0
- package/native/appkit/AppKitWindowControl.mm +222 -0
- package/native/appkit/AppKitWindowModule.h +62 -0
- package/native/appkit/AppKitWindowModule.mm +194 -0
- package/native/appkit/ColorSchemeAppKit.mm +71 -0
- package/native/appkit/ComponentRegistryAppKit.mm +121 -0
- package/native/appkit/CoreTextLayout.h +43 -0
- package/native/appkit/CoreTextLayout.mm +252 -0
- package/native/appkit/CoreTextLayoutManager.mm +138 -0
- package/native/appkit/FontRegistryCoreText.mm +148 -0
- package/native/appkit/PlatformServicesAppKit.mm +540 -0
- package/native/appkit/RnAppKitView.h +417 -0
- package/native/appkit/RnAppKitView.mm +1605 -0
- package/native/appkit/RnTextLayout.h +38 -0
- package/native/appkit/RnTextLayout.mm +168 -0
- package/native/appkit/demo_layout_appkit.mm +93 -0
- package/native/appkit/main_appkit.mm +1860 -0
- package/native/appkit/mount_harness_appkit.mm +197 -0
- package/native/tests/test_appkit_accessibility.mm +170 -0
- package/native/tests/test_appkit_image.mm +237 -0
- package/native/tests/test_appkit_input.mm +852 -0
- package/native/tests/test_appkit_key_events.mm +150 -0
- package/native/tests/test_appkit_mounting.mm +430 -0
- package/native/tests/test_appkit_scrollbar.mm +132 -0
- package/native/tests/test_appkit_services.mm +57 -0
- package/native/tests/test_appkit_skia.mm +212 -0
- package/native/tests/test_appkit_text.mm +180 -0
- package/native/tests/test_appkit_textinput.mm +748 -0
- package/native/tests/test_appkit_titlebar.mm +107 -0
- package/native/tests/test_appkit_view.mm +237 -0
- package/package.json +26 -0
- package/react-native.config.js +22 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// The title bar's metrics, which are the only part of it a test can reach.
|
|
2
|
+
//
|
|
3
|
+
// What the two styles *look* like is in the screenshots that went with phase
|
|
4
|
+
// 58; what is assertable is the arithmetic an app lays itself out against, and
|
|
5
|
+
// that arithmetic is the whole contract src/TitleBar.js has with the host.
|
|
6
|
+
//
|
|
7
|
+
// A real NSWindow, never ordered front. Its metrics are a property of the
|
|
8
|
+
// window rather than of anything on screen, so nothing here has to be shown.
|
|
9
|
+
|
|
10
|
+
#include "TestHarness.h"
|
|
11
|
+
|
|
12
|
+
#include <sstream>
|
|
13
|
+
|
|
14
|
+
#import "AppKitTitleBar.h"
|
|
15
|
+
|
|
16
|
+
TEST(titlebar_native_style_costs_the_app_nothing) {
|
|
17
|
+
@autoreleasepool {
|
|
18
|
+
NSWindow *window = [[NSWindow alloc]
|
|
19
|
+
initWithContentRect:NSMakeRect(0, 0, 400, 300)
|
|
20
|
+
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
|
|
21
|
+
NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
|
|
22
|
+
backing:NSBackingStoreBuffered
|
|
23
|
+
defer:NO];
|
|
24
|
+
basalt::titleBar().attach(window);
|
|
25
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Native);
|
|
26
|
+
|
|
27
|
+
// The system's title bar sits outside the content view, so it takes
|
|
28
|
+
// nothing from the app and there is nothing to leave clear. Windows
|
|
29
|
+
// reports the same zeroes for the same reason.
|
|
30
|
+
const auto metrics = basalt::titleBar().metrics();
|
|
31
|
+
EXPECT(metrics.style == basalt::TitleBarStyle::Native);
|
|
32
|
+
EXPECT_EQ(metrics.height, 0.0);
|
|
33
|
+
EXPECT_EQ(metrics.buttonsWidth, 0.0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
TEST(titlebar_hidden_style_reports_what_to_leave_clear) {
|
|
38
|
+
@autoreleasepool {
|
|
39
|
+
NSWindow *window = [[NSWindow alloc]
|
|
40
|
+
initWithContentRect:NSMakeRect(0, 0, 400, 300)
|
|
41
|
+
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
|
|
42
|
+
NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
|
|
43
|
+
backing:NSBackingStoreBuffered
|
|
44
|
+
defer:NO];
|
|
45
|
+
basalt::titleBar().attach(window);
|
|
46
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Hidden);
|
|
47
|
+
|
|
48
|
+
const auto metrics = basalt::titleBar().metrics();
|
|
49
|
+
EXPECT(metrics.style == basalt::TitleBarStyle::Hidden);
|
|
50
|
+
// A real height rather than a guess: the content view now extends under
|
|
51
|
+
// the title bar, and this is how much of it the app must not fill.
|
|
52
|
+
EXPECT(metrics.height > 0.0);
|
|
53
|
+
// And the traffic lights, which the system keeps drawing over the app's
|
|
54
|
+
// own header -- the counterpart of the caption buttons on Windows.
|
|
55
|
+
EXPECT(metrics.buttonsWidth > 0.0);
|
|
56
|
+
|
|
57
|
+
// The window really did take the style, rather than the metrics being
|
|
58
|
+
// computed from a flag nothing acted on.
|
|
59
|
+
EXPECT((window.styleMask & NSWindowStyleMaskFullSizeContentView) != 0);
|
|
60
|
+
EXPECT(window.titlebarAppearsTransparent);
|
|
61
|
+
|
|
62
|
+
// And back, so a request that is unmounted restores the window.
|
|
63
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Native);
|
|
64
|
+
EXPECT((window.styleMask & NSWindowStyleMaskFullSizeContentView) == 0);
|
|
65
|
+
EXPECT_EQ(basalt::titleBar().metrics().height, 0.0);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
TEST(titlebar_takes_a_title_and_a_colour) {
|
|
70
|
+
@autoreleasepool {
|
|
71
|
+
NSWindow *window = [[NSWindow alloc]
|
|
72
|
+
initWithContentRect:NSMakeRect(0, 0, 400, 300)
|
|
73
|
+
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable
|
|
74
|
+
backing:NSBackingStoreBuffered
|
|
75
|
+
defer:NO];
|
|
76
|
+
basalt::titleBar().attach(window);
|
|
77
|
+
basalt::titleBar().setStyle(basalt::TitleBarStyle::Native);
|
|
78
|
+
basalt::titleBar().setTitle(std::string("Inbox"));
|
|
79
|
+
EXPECT([window.title isEqualToString:@"Inbox"]);
|
|
80
|
+
|
|
81
|
+
// A dark caption asks for the dark appearance, because AppKit has no
|
|
82
|
+
// caption text colour of its own -- the appearance is what decides it.
|
|
83
|
+
basalt::titleBar().setColors(0xFF1A1E28u, std::nullopt, std::nullopt);
|
|
84
|
+
EXPECT(window.titlebarAppearsTransparent);
|
|
85
|
+
EXPECT([window.appearance.name isEqualToString:NSAppearanceNameDarkAqua]);
|
|
86
|
+
|
|
87
|
+
// And a light one asks for the light appearance.
|
|
88
|
+
basalt::titleBar().setColors(0xFFF5F5F5u, std::nullopt, std::nullopt);
|
|
89
|
+
EXPECT([window.appearance.name isEqualToString:NSAppearanceNameAqua]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// The caption buttons' functions. What they do was checked live -- a probe
|
|
94
|
+
// drove the window from 900x700 to maximised and back, then minimised and
|
|
95
|
+
// closed it. What a test pins is the half with no window on the other end: the
|
|
96
|
+
// module outlives the window at shutdown, and a call arriving then must not
|
|
97
|
+
// take the process with it.
|
|
98
|
+
TEST(titlebar_actions_are_safe_with_no_window) {
|
|
99
|
+
@autoreleasepool {
|
|
100
|
+
basalt::titleBar().attach(nil);
|
|
101
|
+
basalt::titleBar().minimize();
|
|
102
|
+
basalt::titleBar().toggleMaximize();
|
|
103
|
+
basalt::titleBar().close();
|
|
104
|
+
basalt::titleBar().startDrag();
|
|
105
|
+
EXPECT(true);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Tests for the macOS view layer.
|
|
2
|
+
//
|
|
3
|
+
// RnAppKitView has no React Native dependency, so these need no Fabric machinery
|
|
4
|
+
// and no window. They share the toolkit-free harness the GTK tests use, which
|
|
5
|
+
// is the first small piece of evidence that a second platform can reuse what is
|
|
6
|
+
// here rather than reimplement it.
|
|
7
|
+
|
|
8
|
+
#include "TestHarness.h"
|
|
9
|
+
|
|
10
|
+
#import "RnAppKitView.h"
|
|
11
|
+
|
|
12
|
+
#include <sstream>
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
// Components of a layer's background colour, or all -1 if it has none.
|
|
17
|
+
struct Background {
|
|
18
|
+
CGFloat r = -1, g = -1, b = -1, a = -1;
|
|
19
|
+
bool present = false;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
Background backgroundOf(RnAppKitView *view) {
|
|
23
|
+
Background result;
|
|
24
|
+
CGColorRef color = view.layer.backgroundColor;
|
|
25
|
+
if (color == nullptr) {
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
const CGFloat *components = CGColorGetComponents(color);
|
|
29
|
+
result.present = true;
|
|
30
|
+
result.r = components[0];
|
|
31
|
+
result.g = components[1];
|
|
32
|
+
result.b = components[2];
|
|
33
|
+
result.a = components[3];
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
RnAppKitView *box(NSInteger tag, CGFloat x, CGFloat y, CGFloat w, CGFloat h) {
|
|
38
|
+
RnAppKitView *view = [RnAppKitView viewWithTag:tag];
|
|
39
|
+
[view setRnFrameX:x y:y width:w height:h];
|
|
40
|
+
return view;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
NSInteger tagOfSubview(RnAppKitView *parent, NSUInteger index) {
|
|
44
|
+
return ((RnAppKitView *)parent.subviews[index]).rnTag;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
} // namespace
|
|
48
|
+
|
|
49
|
+
TEST(appkit_view_stores_its_tag_and_frame) {
|
|
50
|
+
@autoreleasepool {
|
|
51
|
+
RnAppKitView *view = box(7, 1.5, 2.5, 30, 40);
|
|
52
|
+
|
|
53
|
+
EXPECT_EQ((long)view.rnTag, 7L);
|
|
54
|
+
EXPECT_NEAR(view.frame.origin.x, 1.5, 0.001);
|
|
55
|
+
EXPECT_NEAR(view.frame.origin.y, 2.5, 0.001);
|
|
56
|
+
EXPECT_NEAR(view.frame.size.width, 30.0, 0.001);
|
|
57
|
+
EXPECT_NEAR(view.frame.size.height, 40.0, 0.001);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The reason the view exists at all. AppKit's origin is bottom-left; every
|
|
62
|
+
// frame Fabric produces assumes top-left, so a child at y=32 in a 420-tall
|
|
63
|
+
// parent must sit 32 from the top and not 32 from the bottom.
|
|
64
|
+
TEST(appkit_view_is_flipped_so_frames_are_top_left) {
|
|
65
|
+
@autoreleasepool {
|
|
66
|
+
RnAppKitView *parent = box(1, 0, 0, 640, 420);
|
|
67
|
+
RnAppKitView *child = box(2, 32, 32, 240, 160);
|
|
68
|
+
[parent insertRnChild:child atIndex:0];
|
|
69
|
+
|
|
70
|
+
EXPECT(parent.isFlipped);
|
|
71
|
+
|
|
72
|
+
// The child's own origin, expressed in the parent. Unflipped this would be
|
|
73
|
+
// 228 -- 420 - 32 - 160 -- which is the bug in numbers.
|
|
74
|
+
const NSPoint origin = [child convertPoint:NSZeroPoint toView:parent];
|
|
75
|
+
EXPECT_NEAR(origin.x, 32.0, 0.001);
|
|
76
|
+
EXPECT_NEAR(origin.y, 32.0, 0.001);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
TEST(appkit_view_children_insert_at_the_requested_index) {
|
|
81
|
+
@autoreleasepool {
|
|
82
|
+
RnAppKitView *parent = box(1, 0, 0, 100, 100);
|
|
83
|
+
RnAppKitView *a = box(10, 0, 0, 10, 10);
|
|
84
|
+
RnAppKitView *b = box(11, 0, 0, 10, 10);
|
|
85
|
+
RnAppKitView *c = box(12, 0, 0, 10, 10);
|
|
86
|
+
|
|
87
|
+
[parent insertRnChild:a atIndex:0];
|
|
88
|
+
[parent insertRnChild:b atIndex:1];
|
|
89
|
+
// Into the middle, which is the case an append-only implementation passes
|
|
90
|
+
// every other test without handling.
|
|
91
|
+
[parent insertRnChild:c atIndex:1];
|
|
92
|
+
|
|
93
|
+
EXPECT_EQ((long)parent.subviews.count, 3L);
|
|
94
|
+
EXPECT_EQ((long)tagOfSubview(parent, 0), 10L);
|
|
95
|
+
EXPECT_EQ((long)tagOfSubview(parent, 1), 12L);
|
|
96
|
+
EXPECT_EQ((long)tagOfSubview(parent, 2), 11L);
|
|
97
|
+
|
|
98
|
+
[parent removeRnChild:c];
|
|
99
|
+
EXPECT_EQ((long)parent.subviews.count, 2L);
|
|
100
|
+
EXPECT_EQ((long)tagOfSubview(parent, 0), 10L);
|
|
101
|
+
EXPECT_EQ((long)tagOfSubview(parent, 1), 11L);
|
|
102
|
+
|
|
103
|
+
// Removed, not destroyed: a reinsertion elsewhere has to still work.
|
|
104
|
+
EXPECT(c.superview == nil);
|
|
105
|
+
RnAppKitView *other = box(2, 0, 0, 100, 100);
|
|
106
|
+
[other insertRnChild:c atIndex:0];
|
|
107
|
+
EXPECT_EQ((long)tagOfSubview(other, 0), 12L);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Removing a view that is not ours must not detach it from whoever owns it.
|
|
112
|
+
TEST(appkit_view_removes_only_its_own_child) {
|
|
113
|
+
@autoreleasepool {
|
|
114
|
+
RnAppKitView *owner = box(1, 0, 0, 100, 100);
|
|
115
|
+
RnAppKitView *stranger = box(2, 0, 0, 100, 100);
|
|
116
|
+
RnAppKitView *child = box(10, 0, 0, 10, 10);
|
|
117
|
+
|
|
118
|
+
[owner insertRnChild:child atIndex:0];
|
|
119
|
+
[stranger removeRnChild:child];
|
|
120
|
+
|
|
121
|
+
EXPECT_EQ((long)owner.subviews.count, 1L);
|
|
122
|
+
EXPECT(child.superview == owner);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
TEST(appkit_view_background_reaches_the_layer) {
|
|
127
|
+
@autoreleasepool {
|
|
128
|
+
RnAppKitView *view = box(1, 0, 0, 10, 10);
|
|
129
|
+
EXPECT(!backgroundOf(view).present);
|
|
130
|
+
|
|
131
|
+
[view setRnBackgroundColorRed:0.25 green:0.5 blue:0.75 alpha:0.5 hasColor:YES];
|
|
132
|
+
const Background set = backgroundOf(view);
|
|
133
|
+
EXPECT(set.present);
|
|
134
|
+
EXPECT_NEAR(set.r, 0.25, 0.001);
|
|
135
|
+
EXPECT_NEAR(set.g, 0.5, 0.001);
|
|
136
|
+
EXPECT_NEAR(set.b, 0.75, 0.001);
|
|
137
|
+
EXPECT_NEAR(set.a, 0.5, 0.001);
|
|
138
|
+
|
|
139
|
+
// sRGB, not the display's space. The same colour has to come out the same
|
|
140
|
+
// on a wide-gamut Mac as it does on Linux, or "consistent across platforms"
|
|
141
|
+
// stops being true in the one place nobody thinks to check.
|
|
142
|
+
CGColorSpaceRef space = CGColorGetColorSpace(view.layer.backgroundColor);
|
|
143
|
+
CFStringRef name = CGColorSpaceCopyName(space);
|
|
144
|
+
EXPECT(name != nullptr && CFEqual(name, kCGColorSpaceSRGB));
|
|
145
|
+
if (name != nullptr) {
|
|
146
|
+
CFRelease(name);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// No background is not transparent black: the layer must have no colour at
|
|
150
|
+
// all, so whatever is behind it shows through unmodified.
|
|
151
|
+
[view setRnBackgroundColorRed:0.25 green:0.5 blue:0.75 alpha:0.5 hasColor:NO];
|
|
152
|
+
EXPECT(!backgroundOf(view).present);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
TEST(appkit_view_opacity_clipping_and_radius_reach_the_layer) {
|
|
157
|
+
@autoreleasepool {
|
|
158
|
+
RnAppKitView *view = box(1, 0, 0, 10, 10);
|
|
159
|
+
|
|
160
|
+
EXPECT_NEAR(view.layer.opacity, 1.0, 0.001);
|
|
161
|
+
EXPECT(!view.layer.masksToBounds);
|
|
162
|
+
EXPECT_NEAR(view.layer.cornerRadius, 0.0, 0.001);
|
|
163
|
+
|
|
164
|
+
[view setRnOpacity:0.4];
|
|
165
|
+
[view setRnClipsChildren:YES];
|
|
166
|
+
[view setRnCornerRadius:8];
|
|
167
|
+
|
|
168
|
+
EXPECT_NEAR(view.layer.opacity, 0.4, 0.001);
|
|
169
|
+
EXPECT(view.layer.masksToBounds);
|
|
170
|
+
EXPECT_NEAR(view.layer.cornerRadius, 8.0, 0.001);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// A scroll offset moves the children and leaves their frames alone. Through
|
|
175
|
+
// bounds.origin, so AppKit shifts drawing, clipping and hit testing together --
|
|
176
|
+
// an implementation that moved each child instead would have to undo it on
|
|
177
|
+
// every mutation, and would fight the frames Yoga produced.
|
|
178
|
+
TEST(appkit_scroll_offset_moves_children_without_moving_their_frames) {
|
|
179
|
+
@autoreleasepool {
|
|
180
|
+
RnAppKitView *page = box(99, 0, 0, 400, 400);
|
|
181
|
+
RnAppKitView *scroller = box(1, 0, 0, 200, 100);
|
|
182
|
+
RnAppKitView *content = box(2, 0, 0, 200, 600);
|
|
183
|
+
[page insertRnChild:scroller atIndex:0];
|
|
184
|
+
[scroller insertRnChild:content atIndex:0];
|
|
185
|
+
|
|
186
|
+
// Measured against the *page*, not the scroller. A view's own coordinate
|
|
187
|
+
// system is its bounds space, so converting into the scroller would report
|
|
188
|
+
// the frame back unchanged however far it had scrolled -- which is exactly
|
|
189
|
+
// the wrong question, and the first version of this test asked it.
|
|
190
|
+
EXPECT_NEAR(scroller.rnScrollOffset.y, 0.0, 0.001);
|
|
191
|
+
EXPECT_NEAR([content convertPoint:NSZeroPoint toView:page].y, 0.0, 0.001);
|
|
192
|
+
|
|
193
|
+
[scroller setRnScrollOffsetX:0 y:150];
|
|
194
|
+
|
|
195
|
+
EXPECT_NEAR(scroller.rnScrollOffset.y, 150.0, 0.001);
|
|
196
|
+
// The frame is untouched: it is still what the shadow tree assigned.
|
|
197
|
+
EXPECT_NEAR(content.frame.origin.y, 0.0, 0.001);
|
|
198
|
+
// But it now sits 150 above the scroller's visible top.
|
|
199
|
+
EXPECT_NEAR([content convertPoint:NSZeroPoint toView:page].y, -150.0, 0.001);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
TEST(appkit_scroll_offset_is_reported_in_the_tree) {
|
|
204
|
+
@autoreleasepool {
|
|
205
|
+
RnAppKitView *scroller = box(1, 0, 0, 200, 100);
|
|
206
|
+
[scroller setRnScrollOffsetX:0 y:40];
|
|
207
|
+
|
|
208
|
+
const std::string described = [scroller describeTree].UTF8String;
|
|
209
|
+
// The same `scroll=(x,y)` the GTK side emits, so a scrolled list shows up
|
|
210
|
+
// in the cross-platform diff.
|
|
211
|
+
EXPECT(described.find("scroll=(0,40)") != std::string::npos);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// The same format the GTK side emits, character for character, so the two
|
|
216
|
+
// platforms can be compared without reading two renderers.
|
|
217
|
+
TEST(appkit_view_describes_its_tree_like_the_gtk_one) {
|
|
218
|
+
@autoreleasepool {
|
|
219
|
+
RnAppKitView *root = box(1, 0, 0, 640, 420);
|
|
220
|
+
[root setRnBackgroundColorRed:0.12 green:0.13 blue:0.16 alpha:1.0 hasColor:YES];
|
|
221
|
+
|
|
222
|
+
RnAppKitView *child = box(2, 32, 32, 240, 160);
|
|
223
|
+
[child setRnOpacity:0.4];
|
|
224
|
+
[child setRnClipsChildren:YES];
|
|
225
|
+
[root insertRnChild:child atIndex:0];
|
|
226
|
+
|
|
227
|
+
const std::string described = [root describeTree].UTF8String;
|
|
228
|
+
const std::string expected =
|
|
229
|
+
"view tag=1 frame=(0,0 640x420) bg=#1f2129ff\n"
|
|
230
|
+
" view tag=2 frame=(32,32 240x160) opacity=0.4 clip\n";
|
|
231
|
+
EXPECT_EQ(described, expected);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
int main(int argc, char **argv) {
|
|
236
|
+
return basalt::testing::runAllTests(argc, argv);
|
|
237
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "basalt-appkit",
|
|
3
|
+
"version": "0.0.0-rc5",
|
|
4
|
+
"description": "The macOS view layer for basalt-core: an AppKit view 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-appkit"
|
|
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,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What React Native's CLI reads out of this package.
|
|
3
|
+
*
|
|
4
|
+
* Just the `run-macos` command. The platform *declarations* -- which make
|
|
5
|
+
* `--platform` accept this one at all -- are in basalt-core, because
|
|
6
|
+
* they are the same on every desktop and an app targeting only one of them
|
|
7
|
+
* 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 more than one of these packages installed gets the
|
|
11
|
+
* platforms from the shared one and a command from each of the others, without
|
|
12
|
+
* any of them knowing about the rest.
|
|
13
|
+
*
|
|
14
|
+
* @format
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// @ts-check
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
commands: [require('./cli/runMacos')],
|
|
22
|
+
};
|