basalt-win32 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/runWindows.js +35 -0
- package/cli/shared.js +75 -0
- package/native/CMakeLists.txt +398 -0
- package/native/tests/test_win32_accessibility.cpp +271 -0
- package/native/tests/test_win32_clipboard.cpp +121 -0
- package/native/tests/test_win32_focus.cpp +195 -0
- package/native/tests/test_win32_hittest.cpp +410 -0
- package/native/tests/test_win32_image.cpp +239 -0
- package/native/tests/test_win32_imageloader.cpp +208 -0
- package/native/tests/test_win32_input.cpp +361 -0
- package/native/tests/test_win32_key_events.cpp +84 -0
- package/native/tests/test_win32_mounting.cpp +410 -0
- package/native/tests/test_win32_paint.cpp +505 -0
- package/native/tests/test_win32_scrollview.cpp +385 -0
- package/native/tests/test_win32_text.cpp +258 -0
- package/native/tests/test_win32_textinput.cpp +609 -0
- package/native/tests/test_win32_titlebar.cpp +111 -0
- package/native/tests/test_win32_titlebar_actions.cpp +62 -0
- package/native/tests/test_win32_view.cpp +222 -0
- package/native/win32/ColorSchemeWin32.cpp +56 -0
- package/native/win32/ComponentRegistryWin32.cpp +124 -0
- package/native/win32/DirectWriteLayout.cpp +116 -0
- package/native/win32/DirectWriteLayout.h +36 -0
- package/native/win32/DirectWriteLayoutManager.cpp +124 -0
- package/native/win32/FontRegistryDirectWrite.cpp +117 -0
- package/native/win32/PlatformServicesWin32.cpp +643 -0
- package/native/win32/RnWin32Accessible.cpp +258 -0
- package/native/win32/RnWin32Accessible.h +91 -0
- package/native/win32/RnWin32Image.cpp +265 -0
- package/native/win32/RnWin32Image.h +93 -0
- package/native/win32/RnWin32TextLayout.cpp +310 -0
- package/native/win32/RnWin32TextLayout.h +160 -0
- package/native/win32/RnWin32View.cpp +1281 -0
- package/native/win32/RnWin32View.h +506 -0
- package/native/win32/Win32AnimationChoreographer.cpp +76 -0
- package/native/win32/Win32AnimationChoreographer.h +61 -0
- package/native/win32/Win32Clip.h +173 -0
- package/native/win32/Win32DropTarget.cpp +567 -0
- package/native/win32/Win32DropTarget.h +42 -0
- package/native/win32/Win32Focus.cpp +262 -0
- package/native/win32/Win32Focus.h +109 -0
- package/native/win32/Win32ImageLoader.cpp +167 -0
- package/native/win32/Win32ImageLoader.h +104 -0
- package/native/win32/Win32KeyEvents.cpp +126 -0
- package/native/win32/Win32KeyEvents.h +20 -0
- package/native/win32/Win32MenuBar.cpp +352 -0
- package/native/win32/Win32MenuBar.h +16 -0
- package/native/win32/Win32MountingManager.cpp +727 -0
- package/native/win32/Win32MountingManager.h +203 -0
- package/native/win32/Win32Packaging.cpp +161 -0
- package/native/win32/Win32Packaging.h +56 -0
- package/native/win32/Win32RunLoopObserver.cpp +48 -0
- package/native/win32/Win32RunLoopObserver.h +42 -0
- package/native/win32/Win32ScrollView.cpp +515 -0
- package/native/win32/Win32ScrollView.h +208 -0
- package/native/win32/Win32SkiaContext.cpp +221 -0
- package/native/win32/Win32SkiaContext.h +53 -0
- package/native/win32/Win32SkiaModule.cpp +80 -0
- package/native/win32/Win32SkiaModule.h +50 -0
- package/native/win32/Win32SkiaPeer.cpp +358 -0
- package/native/win32/Win32SkiaPeer.h +66 -0
- package/native/win32/Win32SkiaUnicode.cpp +42 -0
- package/native/win32/Win32Snapshot.cpp +201 -0
- package/native/win32/Win32Snapshot.h +66 -0
- package/native/win32/Win32Strings.h +54 -0
- package/native/win32/Win32TextInput.cpp +811 -0
- package/native/win32/Win32TextInput.h +232 -0
- package/native/win32/Win32TitleBar.cpp +562 -0
- package/native/win32/Win32TitleBar.h +146 -0
- package/native/win32/Win32TitleBarLayout.cpp +46 -0
- package/native/win32/Win32TitleBarLayout.h +58 -0
- package/native/win32/Win32TouchDispatcher.cpp +443 -0
- package/native/win32/Win32TouchDispatcher.h +153 -0
- package/native/win32/Win32UiThread.h +27 -0
- package/native/win32/Win32WindowControl.cpp +329 -0
- package/native/win32/Win32WindowModule.cpp +209 -0
- package/native/win32/Win32WindowModule.h +72 -0
- package/native/win32/demo_layout_win32.cpp +232 -0
- package/native/win32/main_win32.cpp +2481 -0
- package/native/win32/mount_harness_win32.cpp +316 -0
- package/package.json +26 -0
- package/react-native.config.js +22 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
// What actually reached the pixels.
|
|
2
|
+
//
|
|
3
|
+
// Every other suite in this project asserts on the view tree: that a view has a
|
|
4
|
+
// colour, a frame and a transform. `docs/BACKLOG.md` has carried the gap that
|
|
5
|
+
// leaves since GTK -- "GTK's cairo renderer mangled every transform in the demo
|
|
6
|
+
// and no test noticed" -- because closing it on the other two platforms means a
|
|
7
|
+
// display server or an offscreen window and a display cycle.
|
|
8
|
+
//
|
|
9
|
+
// Direct2D renders into a WIC bitmap with no window and no display, so here it
|
|
10
|
+
// costs a function call. These are the assertions that would have caught that
|
|
11
|
+
// bug: a transform composed in the wrong order, a clip that does not clip, an
|
|
12
|
+
// opacity applied per-brush instead of per-subtree, and a zIndex that reorders
|
|
13
|
+
// nothing all leave the tree dump identical and the picture wrong.
|
|
14
|
+
|
|
15
|
+
#include "TestHarness.h"
|
|
16
|
+
|
|
17
|
+
#include "RnWin32View.h"
|
|
18
|
+
#include "Win32Snapshot.h"
|
|
19
|
+
|
|
20
|
+
#include <d2d1.h>
|
|
21
|
+
#include <wrl/client.h>
|
|
22
|
+
|
|
23
|
+
#include <memory>
|
|
24
|
+
#include <sstream>
|
|
25
|
+
#include <vector>
|
|
26
|
+
|
|
27
|
+
using basalt::win32::RnPixel;
|
|
28
|
+
using basalt::win32::RnPixels;
|
|
29
|
+
using basalt::win32::RnWin32View;
|
|
30
|
+
|
|
31
|
+
namespace {
|
|
32
|
+
|
|
33
|
+
// Rounding through premultiplied storage costs a unit or two, and Direct2D
|
|
34
|
+
// antialiases edges -- so every point these tests ask about is well inside or
|
|
35
|
+
// well outside a shape, never on its boundary.
|
|
36
|
+
constexpr double kTolerance = 3.0;
|
|
37
|
+
|
|
38
|
+
class Tree {
|
|
39
|
+
public:
|
|
40
|
+
RnWin32View *box(int32_t tag, float x, float y, float w, float h) {
|
|
41
|
+
auto view = std::make_unique<RnWin32View>(tag);
|
|
42
|
+
view->setFrame(x, y, w, h);
|
|
43
|
+
RnWin32View *raw = view.get();
|
|
44
|
+
views_.push_back(std::move(view));
|
|
45
|
+
return raw;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
RnWin32View *
|
|
49
|
+
colouredBox(int32_t tag, float x, float y, float w, float h, float r, float g, float b) {
|
|
50
|
+
RnWin32View *view = box(tag, x, y, w, h);
|
|
51
|
+
view->setBackgroundColor(r, g, b, 1.0f, true);
|
|
52
|
+
return view;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private:
|
|
56
|
+
std::vector<std::unique_ptr<RnWin32View>> views_;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Stands in for the Skia surface behind a `<Canvas>`: fills the box it is
|
|
60
|
+
// handed, and remembers what it was handed.
|
|
61
|
+
//
|
|
62
|
+
// A fake rather than the real thing because the seam is what these tests are
|
|
63
|
+
// about. Whether Skia drew the right picture is Skia's business; whether this
|
|
64
|
+
// host puts what it drew in the right place, inside the clip and under the
|
|
65
|
+
// opacity, is the part that is this host's and the part that has been wrong on
|
|
66
|
+
// every platform at least once.
|
|
67
|
+
class FillPainter final : public basalt::win32::RnWin32Painter {
|
|
68
|
+
public:
|
|
69
|
+
FillPainter(float red, float green, float blue) : red_(red), green_(green), blue_(blue) {}
|
|
70
|
+
|
|
71
|
+
void draw(ID2D1RenderTarget *target, float boxWidth, float boxHeight) override {
|
|
72
|
+
calls++;
|
|
73
|
+
lastWidth = boxWidth;
|
|
74
|
+
lastHeight = boxHeight;
|
|
75
|
+
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush;
|
|
76
|
+
if (FAILED(target->CreateSolidColorBrush(D2D1::ColorF(red_, green_, blue_, 1.0f),
|
|
77
|
+
brush.GetAddressOf()))) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
target->FillRectangle(D2D1::RectF(0.0f, 0.0f, boxWidth, boxHeight), brush.Get());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
int calls = 0;
|
|
84
|
+
float lastWidth = 0.0f;
|
|
85
|
+
float lastHeight = 0.0f;
|
|
86
|
+
|
|
87
|
+
private:
|
|
88
|
+
float red_;
|
|
89
|
+
float green_;
|
|
90
|
+
float blue_;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
} // namespace
|
|
94
|
+
|
|
95
|
+
// A macro rather than a function so a failure reports the line that asked,
|
|
96
|
+
// which is the only thing that makes a wrong pixel findable.
|
|
97
|
+
#define EXPECT_PIXEL(pixels, x, y, r, g, b, a) \
|
|
98
|
+
do { \
|
|
99
|
+
const RnPixel pixel = (pixels).at((x), (y)); \
|
|
100
|
+
EXPECT_NEAR(pixel.red, (r), kTolerance); \
|
|
101
|
+
EXPECT_NEAR(pixel.green, (g), kTolerance); \
|
|
102
|
+
EXPECT_NEAR(pixel.blue, (b), kTolerance); \
|
|
103
|
+
EXPECT_NEAR(pixel.alpha, (a), kTolerance); \
|
|
104
|
+
} while (false)
|
|
105
|
+
|
|
106
|
+
#define EXPECT_TRANSPARENT(pixels, x, y) EXPECT_NEAR((pixels).at((x), (y)).alpha, 0, kTolerance)
|
|
107
|
+
|
|
108
|
+
TEST(win32_paint_puts_a_child_at_its_frame) {
|
|
109
|
+
Tree tree;
|
|
110
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
111
|
+
root->insertChild(tree.colouredBox(2, 20, 30, 40, 10, 1.0f, 0.0f, 0.0f), 0);
|
|
112
|
+
|
|
113
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
114
|
+
EXPECT(!pixels.empty());
|
|
115
|
+
|
|
116
|
+
EXPECT_PIXEL(pixels, 25, 35, 255, 0, 0, 255);
|
|
117
|
+
EXPECT_TRANSPARENT(pixels, 5, 5);
|
|
118
|
+
EXPECT_TRANSPARENT(pixels, 65, 35);
|
|
119
|
+
EXPECT_TRANSPARENT(pixels, 25, 45);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
TEST(win32_paint_composites_opacity_over_the_whole_subtree) {
|
|
123
|
+
Tree tree;
|
|
124
|
+
RnWin32View *root = tree.box(1, 0, 0, 60, 60);
|
|
125
|
+
RnWin32View *faded = tree.colouredBox(2, 0, 0, 60, 60, 1.0f, 0.0f, 0.0f);
|
|
126
|
+
faded->setOpacity(0.4f);
|
|
127
|
+
root->insertChild(faded, 0);
|
|
128
|
+
|
|
129
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
130
|
+
EXPECT(!pixels.empty());
|
|
131
|
+
|
|
132
|
+
// Still fully red, at 40% coverage. An opacity folded into the brush's alpha
|
|
133
|
+
// would look the same here and wrong the moment two children overlap, which
|
|
134
|
+
// is what the layer in RnWin32View::paint is for.
|
|
135
|
+
EXPECT_PIXEL(pixels, 30, 30, 255, 0, 0, 102);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
TEST(win32_paint_clips_children_only_when_overflow_is_hidden) {
|
|
139
|
+
Tree tree;
|
|
140
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
141
|
+
RnWin32View *parent = tree.box(2, 0, 0, 50, 50);
|
|
142
|
+
parent->insertChild(tree.colouredBox(3, 0, 0, 100, 100, 0.0f, 1.0f, 0.0f), 0);
|
|
143
|
+
root->insertChild(parent, 0);
|
|
144
|
+
|
|
145
|
+
const RnPixels visible = basalt::win32::renderToPixels(*root);
|
|
146
|
+
EXPECT(!visible.empty());
|
|
147
|
+
EXPECT_PIXEL(visible, 25, 25, 0, 255, 0, 255);
|
|
148
|
+
// overflow: visible is the default, so the child escapes its parent's box.
|
|
149
|
+
EXPECT_PIXEL(visible, 75, 25, 0, 255, 0, 255);
|
|
150
|
+
|
|
151
|
+
parent->setClipsChildren(true);
|
|
152
|
+
const RnPixels clipped = basalt::win32::renderToPixels(*root);
|
|
153
|
+
EXPECT(!clipped.empty());
|
|
154
|
+
EXPECT_PIXEL(clipped, 25, 25, 0, 255, 0, 255);
|
|
155
|
+
EXPECT_TRANSPARENT(clipped, 75, 25);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
TEST(win32_paint_rotates_about_the_centre_and_in_the_right_direction) {
|
|
159
|
+
Tree tree;
|
|
160
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
161
|
+
// Deliberately wide and short, so a quarter turn changes which pixels are
|
|
162
|
+
// covered rather than only which way up they are.
|
|
163
|
+
RnWin32View *card = tree.colouredBox(2, 10, 40, 80, 20, 0.0f, 0.0f, 1.0f);
|
|
164
|
+
// A marker in the card's top-left corner. Without it a quarter turn and a
|
|
165
|
+
// three-quarter turn are indistinguishable, because the card is symmetric
|
|
166
|
+
// under a half turn -- and the difference between them is exactly the
|
|
167
|
+
// transpose that no test on an axis-aligned box can see.
|
|
168
|
+
card->insertChild(tree.colouredBox(3, 0, 0, 10, 10, 1.0f, 1.0f, 1.0f), 0);
|
|
169
|
+
root->insertChild(card, 0);
|
|
170
|
+
|
|
171
|
+
const RnPixels upright = basalt::win32::renderToPixels(*root);
|
|
172
|
+
EXPECT(!upright.empty());
|
|
173
|
+
// The card spans x 10..90, y 40..60; the marker is its corner, x 10..20,
|
|
174
|
+
// y 40..50.
|
|
175
|
+
EXPECT_PIXEL(upright, 30, 50, 0, 0, 255, 255);
|
|
176
|
+
EXPECT_PIXEL(upright, 15, 45, 255, 255, 255, 255);
|
|
177
|
+
EXPECT_TRANSPARENT(upright, 50, 20);
|
|
178
|
+
|
|
179
|
+
// CSS rotate(90deg), in matrix3d's column-major order: m11=0, m12=1, m21=-1,
|
|
180
|
+
// m22=0. On screen, with y downwards, that turns clockwise.
|
|
181
|
+
const float quarterTurn[16] = {
|
|
182
|
+
0, 1, 0, 0, //
|
|
183
|
+
-1, 0, 0, 0, //
|
|
184
|
+
0, 0, 1, 0, //
|
|
185
|
+
0, 0, 0, 1};
|
|
186
|
+
card->setTransform(quarterTurn);
|
|
187
|
+
|
|
188
|
+
const RnPixels turned = basalt::win32::renderToPixels(*root);
|
|
189
|
+
EXPECT(!turned.empty());
|
|
190
|
+
|
|
191
|
+
// The card's centre is (50,50) and it is now 20 wide and 80 tall about it, so
|
|
192
|
+
// the pixels it covers have swapped: (50,60) is inside and (20,50) is not.
|
|
193
|
+
EXPECT_PIXEL(turned, 50, 60, 0, 0, 255, 255);
|
|
194
|
+
EXPECT_TRANSPARENT(turned, 20, 50);
|
|
195
|
+
|
|
196
|
+
// And the marker has swung to x 50..60, y 10..20 -- clockwise. The transposed
|
|
197
|
+
// matrix, which is the mistake no axis-aligned test can see, would put it at
|
|
198
|
+
// x 40..50, y 80..90 instead. Both ends are asserted, because the card covers
|
|
199
|
+
// both and only the marker's colour tells them apart.
|
|
200
|
+
EXPECT_PIXEL(turned, 55, 15, 255, 255, 255, 255);
|
|
201
|
+
EXPECT_PIXEL(turned, 45, 85, 0, 0, 255, 255);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
TEST(win32_paint_orders_children_by_z_index) {
|
|
205
|
+
Tree tree;
|
|
206
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
207
|
+
RnWin32View *under = tree.colouredBox(2, 0, 0, 50, 50, 1.0f, 0.0f, 0.0f);
|
|
208
|
+
RnWin32View *over = tree.colouredBox(3, 0, 0, 50, 50, 0.0f, 1.0f, 0.0f);
|
|
209
|
+
root->insertChild(under, 0);
|
|
210
|
+
root->insertChild(over, 1);
|
|
211
|
+
|
|
212
|
+
// Document order: the later child wins.
|
|
213
|
+
const RnPixels ordered = basalt::win32::renderToPixels(*root);
|
|
214
|
+
EXPECT(!ordered.empty());
|
|
215
|
+
EXPECT_PIXEL(ordered, 25, 25, 0, 255, 0, 255);
|
|
216
|
+
|
|
217
|
+
// A negative zIndex on the later child puts it underneath, and the child list
|
|
218
|
+
// does not move -- which test_win32_view.cpp asserts separately, because
|
|
219
|
+
// Fabric indexes into that list.
|
|
220
|
+
over->setZIndex(-1);
|
|
221
|
+
const RnPixels restacked = basalt::win32::renderToPixels(*root);
|
|
222
|
+
EXPECT(!restacked.empty());
|
|
223
|
+
EXPECT_PIXEL(restacked, 25, 25, 255, 0, 0, 255);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
TEST(win32_paint_moves_children_by_the_scroll_offset) {
|
|
227
|
+
Tree tree;
|
|
228
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
229
|
+
RnWin32View *row = tree.colouredBox(2, 0, 50, 100, 20, 1.0f, 0.0f, 0.0f);
|
|
230
|
+
root->insertChild(row, 0);
|
|
231
|
+
|
|
232
|
+
const RnPixels unscrolled = basalt::win32::renderToPixels(*root);
|
|
233
|
+
EXPECT(!unscrolled.empty());
|
|
234
|
+
EXPECT_PIXEL(unscrolled, 50, 55, 255, 0, 0, 255);
|
|
235
|
+
EXPECT_TRANSPARENT(unscrolled, 50, 25);
|
|
236
|
+
|
|
237
|
+
// Scrolled down by 30, the row that was at y=50 is drawn at y=20 -- and its
|
|
238
|
+
// frame has not changed, which is the invariant: Yoga decides where things
|
|
239
|
+
// are and the offset is applied at paint time.
|
|
240
|
+
root->setScrollOffset(0, 30);
|
|
241
|
+
const RnPixels scrolled = basalt::win32::renderToPixels(*root);
|
|
242
|
+
EXPECT(!scrolled.empty());
|
|
243
|
+
EXPECT_PIXEL(scrolled, 50, 25, 255, 0, 0, 255);
|
|
244
|
+
EXPECT_TRANSPARENT(scrolled, 50, 55);
|
|
245
|
+
EXPECT_NEAR(row->frame().y, 50.0, 0.001);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
TEST(win32_paint_rounds_the_background_to_the_corner_radius) {
|
|
249
|
+
Tree tree;
|
|
250
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
251
|
+
RnWin32View *rounded = tree.colouredBox(2, 0, 0, 100, 100, 1.0f, 0.0f, 0.0f);
|
|
252
|
+
rounded->setCornerRadius(40.0f);
|
|
253
|
+
root->insertChild(rounded, 0);
|
|
254
|
+
|
|
255
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
256
|
+
EXPECT(!pixels.empty());
|
|
257
|
+
|
|
258
|
+
// The middle is filled and the corner is not, which is the whole claim. The
|
|
259
|
+
// background is clipped to the radius whether or not children are.
|
|
260
|
+
EXPECT_PIXEL(pixels, 50, 50, 255, 0, 0, 255);
|
|
261
|
+
EXPECT_TRANSPARENT(pixels, 2, 2);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// Per-corner radii and borders
|
|
266
|
+
//
|
|
267
|
+
// This host drew one circular radius -- the top-left horizontal one -- and no
|
|
268
|
+
// border at all, while GTK and AppKit drew both. The tree dump said so, which
|
|
269
|
+
// is how scripts/compare_hosts.sh caught it; these say what reached the pixels.
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
|
|
272
|
+
TEST(win32_paint_rounds_each_corner_on_its_own) {
|
|
273
|
+
Tree tree;
|
|
274
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
275
|
+
RnWin32View *box = tree.colouredBox(2, 0, 0, 100, 100, 1.0f, 0.0f, 0.0f);
|
|
276
|
+
// Top-left and bottom-right rounded, the other two square: what
|
|
277
|
+
// borderTopLeftRadius and borderBottomRightRadius alone ask for.
|
|
278
|
+
const float radii[8] = {40, 40, 0, 0, 40, 40, 0, 0};
|
|
279
|
+
box->setCornerRadii(radii);
|
|
280
|
+
root->insertChild(box, 0);
|
|
281
|
+
|
|
282
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
283
|
+
EXPECT(!pixels.empty());
|
|
284
|
+
EXPECT_TRANSPARENT(pixels, 3, 3);
|
|
285
|
+
EXPECT_TRANSPARENT(pixels, 96, 96);
|
|
286
|
+
// The two square corners are still there, which one radius for all four
|
|
287
|
+
// could never have produced.
|
|
288
|
+
EXPECT_PIXEL(pixels, 97, 2, 255, 0, 0, 255);
|
|
289
|
+
EXPECT_PIXEL(pixels, 2, 97, 255, 0, 0, 255);
|
|
290
|
+
EXPECT_PIXEL(pixels, 50, 50, 255, 0, 0, 255);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
TEST(win32_paint_rounds_a_corner_elliptically) {
|
|
294
|
+
Tree tree;
|
|
295
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
296
|
+
RnWin32View *box = tree.colouredBox(2, 0, 0, 100, 100, 1.0f, 0.0f, 0.0f);
|
|
297
|
+
// 60 across and 20 down at the top-left. (15,3) is inside a 20-radius circle
|
|
298
|
+
// and outside this ellipse; (50,15) is inside the ellipse. Only an elliptical
|
|
299
|
+
// corner answers both.
|
|
300
|
+
const float radii[8] = {60, 20, 0, 0, 0, 0, 0, 0};
|
|
301
|
+
box->setCornerRadii(radii);
|
|
302
|
+
root->insertChild(box, 0);
|
|
303
|
+
|
|
304
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
305
|
+
EXPECT(!pixels.empty());
|
|
306
|
+
EXPECT_TRANSPARENT(pixels, 15, 3);
|
|
307
|
+
EXPECT_PIXEL(pixels, 50, 15, 255, 0, 0, 255);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
TEST(win32_paint_draws_a_border_inside_the_box) {
|
|
311
|
+
Tree tree;
|
|
312
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
313
|
+
RnWin32View *box = tree.colouredBox(2, 0, 0, 100, 100, 0.0f, 0.0f, 1.0f);
|
|
314
|
+
const float widths[4] = {10, 10, 10, 10};
|
|
315
|
+
const float colours[16] = {1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1};
|
|
316
|
+
box->setBorders(widths, colours);
|
|
317
|
+
root->insertChild(box, 0);
|
|
318
|
+
|
|
319
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
320
|
+
EXPECT(!pixels.empty());
|
|
321
|
+
// Inside the frame on every side -- the border is part of the box, which is
|
|
322
|
+
// what Yoga already inset the content by -- and the middle untouched.
|
|
323
|
+
EXPECT_PIXEL(pixels, 50, 4, 255, 0, 0, 255);
|
|
324
|
+
EXPECT_PIXEL(pixels, 95, 50, 255, 0, 0, 255);
|
|
325
|
+
EXPECT_PIXEL(pixels, 50, 95, 255, 0, 0, 255);
|
|
326
|
+
EXPECT_PIXEL(pixels, 4, 50, 255, 0, 0, 255);
|
|
327
|
+
EXPECT_PIXEL(pixels, 50, 50, 0, 0, 255, 255);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
TEST(win32_paint_colours_each_edge_and_mitres_the_corners) {
|
|
331
|
+
Tree tree;
|
|
332
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
333
|
+
RnWin32View *box = tree.box(2, 0, 0, 100, 100);
|
|
334
|
+
const float widths[4] = {10, 10, 10, 10};
|
|
335
|
+
// Top red, right green, bottom blue, left yellow.
|
|
336
|
+
const float colours[16] = {1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1};
|
|
337
|
+
box->setBorders(widths, colours);
|
|
338
|
+
root->insertChild(box, 0);
|
|
339
|
+
|
|
340
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
341
|
+
EXPECT(!pixels.empty());
|
|
342
|
+
EXPECT_PIXEL(pixels, 50, 4, 255, 0, 0, 255);
|
|
343
|
+
EXPECT_PIXEL(pixels, 95, 50, 0, 255, 0, 255);
|
|
344
|
+
EXPECT_PIXEL(pixels, 50, 95, 0, 0, 255, 255);
|
|
345
|
+
EXPECT_PIXEL(pixels, 4, 50, 255, 255, 0, 255);
|
|
346
|
+
// The top-left corner is split down its diagonal, as CSS splits it: above the
|
|
347
|
+
// line from (0,0) to (10,10) is the top edge's, below it the left edge's.
|
|
348
|
+
EXPECT_PIXEL(pixels, 7, 2, 255, 0, 0, 255);
|
|
349
|
+
EXPECT_PIXEL(pixels, 2, 7, 255, 255, 0, 255);
|
|
350
|
+
// No background, so the middle is nothing.
|
|
351
|
+
EXPECT_TRANSPARENT(pixels, 50, 50);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
TEST(win32_paint_draws_the_border_over_the_children) {
|
|
355
|
+
Tree tree;
|
|
356
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
357
|
+
RnWin32View *parent = tree.box(2, 0, 0, 100, 100);
|
|
358
|
+
const float widths[4] = {10, 10, 10, 10};
|
|
359
|
+
const float colours[16] = {1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1};
|
|
360
|
+
parent->setBorders(widths, colours);
|
|
361
|
+
root->insertChild(parent, 0);
|
|
362
|
+
// A child covering the whole box, border included: the border still shows,
|
|
363
|
+
// because it is painted after the content on every platform.
|
|
364
|
+
parent->insertChild(tree.colouredBox(3, 0, 0, 100, 100, 0.0f, 1.0f, 0.0f), 0);
|
|
365
|
+
|
|
366
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
367
|
+
EXPECT(!pixels.empty());
|
|
368
|
+
EXPECT_PIXEL(pixels, 50, 4, 255, 0, 0, 255);
|
|
369
|
+
EXPECT_PIXEL(pixels, 50, 50, 0, 255, 0, 255);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
TEST(win32_paint_clips_children_to_each_corner) {
|
|
373
|
+
Tree tree;
|
|
374
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
375
|
+
RnWin32View *parent = tree.box(2, 0, 0, 100, 100);
|
|
376
|
+
const float radii[8] = {40, 40, 0, 0, 0, 0, 0, 0};
|
|
377
|
+
parent->setCornerRadii(radii);
|
|
378
|
+
parent->setClipsChildren(true);
|
|
379
|
+
root->insertChild(parent, 0);
|
|
380
|
+
parent->insertChild(tree.colouredBox(3, 0, 0, 100, 100, 0.0f, 1.0f, 0.0f), 0);
|
|
381
|
+
|
|
382
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
383
|
+
EXPECT(!pixels.empty());
|
|
384
|
+
// overflow: hidden cuts the child at the rounded corner and nowhere else.
|
|
385
|
+
EXPECT_TRANSPARENT(pixels, 3, 3);
|
|
386
|
+
EXPECT_PIXEL(pixels, 97, 3, 0, 255, 0, 255);
|
|
387
|
+
EXPECT_PIXEL(pixels, 3, 97, 0, 255, 0, 255);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
TEST(win32_describe_prints_radii_and_borders_as_gtk_does) {
|
|
391
|
+
// The line js/index.js's header produces on Linux, which is the one the
|
|
392
|
+
// cross-host comparison failed on: two rounded corners, one colour on three
|
|
393
|
+
// edges and another on the fourth.
|
|
394
|
+
RnWin32View view(16);
|
|
395
|
+
view.setFrame(0, 0, 50, 50);
|
|
396
|
+
const float radii[8] = {18, 18, 0, 0, 18, 18, 0, 0};
|
|
397
|
+
view.setCornerRadii(radii);
|
|
398
|
+
const float widths[4] = {4, 4, 4, 4};
|
|
399
|
+
const float gold[4] = {242 / 255.0f, 193 / 255.0f, 78 / 255.0f, 1.0f};
|
|
400
|
+
const float coral[4] = {242 / 255.0f, 111 / 255.0f, 86 / 255.0f, 1.0f};
|
|
401
|
+
float colours[16];
|
|
402
|
+
for (int c = 0; c < 4; c++) {
|
|
403
|
+
colours[0 + c] = gold[c];
|
|
404
|
+
colours[4 + c] = gold[c];
|
|
405
|
+
colours[8 + c] = gold[c];
|
|
406
|
+
colours[12 + c] = coral[c];
|
|
407
|
+
}
|
|
408
|
+
view.setBorders(widths, colours);
|
|
409
|
+
|
|
410
|
+
const std::string tree = view.describeTree();
|
|
411
|
+
EXPECT(tree.find(" radii=(18,18,0,0,18,18,0,0)") != std::string::npos);
|
|
412
|
+
EXPECT(tree.find(" borderw=(4,4,4,4) borderc=(#f2c14eff,#f2c14eff,#f2c14eff,#f26f56ff)") !=
|
|
413
|
+
std::string::npos);
|
|
414
|
+
|
|
415
|
+
// And a border that cannot be seen is not a border: GTK prints none for it,
|
|
416
|
+
// so neither does this.
|
|
417
|
+
const float clear[16] = {};
|
|
418
|
+
view.setBorders(widths, clear);
|
|
419
|
+
EXPECT(view.describeTree().find("borderw=") == std::string::npos);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
TEST(win32_paint_gives_a_painter_its_views_box) {
|
|
423
|
+
Tree tree;
|
|
424
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
425
|
+
RnWin32View *canvas = tree.box(2, 20, 30, 40, 10);
|
|
426
|
+
auto painter = std::make_shared<FillPainter>(1.0f, 0.0f, 0.0f);
|
|
427
|
+
canvas->setPainter(painter);
|
|
428
|
+
root->insertChild(canvas, 0);
|
|
429
|
+
|
|
430
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
431
|
+
EXPECT(!pixels.empty());
|
|
432
|
+
|
|
433
|
+
// Once, with the frame Yoga resolved -- not the root's, and not in pixels.
|
|
434
|
+
EXPECT_EQ(painter->calls, 1);
|
|
435
|
+
EXPECT_NEAR(painter->lastWidth, 40.0, 0.01);
|
|
436
|
+
EXPECT_NEAR(painter->lastHeight, 10.0, 0.01);
|
|
437
|
+
|
|
438
|
+
// And at the view's origin, so what it drew at 0,0 lands at 20,30.
|
|
439
|
+
EXPECT_PIXEL(pixels, 25, 35, 255, 0, 0, 255);
|
|
440
|
+
EXPECT_TRANSPARENT(pixels, 5, 5);
|
|
441
|
+
EXPECT_TRANSPARENT(pixels, 65, 35);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
TEST(win32_paint_clips_and_fades_a_painter_with_the_views_above_it) {
|
|
445
|
+
// The whole argument for drawing a <Canvas> in the walk instead of giving it
|
|
446
|
+
// a surface of its own: it is clipped and faded by its ancestors without
|
|
447
|
+
// knowing that any of them exist. A sibling layer would need every one of
|
|
448
|
+
// these reimplemented, which is where the other toolkits' bugs live.
|
|
449
|
+
Tree tree;
|
|
450
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
451
|
+
RnWin32View *clip = tree.box(2, 0, 0, 50, 50);
|
|
452
|
+
clip->setClipsChildren(true);
|
|
453
|
+
clip->setOpacity(0.4f);
|
|
454
|
+
RnWin32View *canvas = tree.box(3, 0, 0, 100, 100);
|
|
455
|
+
canvas->setPainter(std::make_shared<FillPainter>(0.0f, 1.0f, 0.0f));
|
|
456
|
+
clip->insertChild(canvas, 0);
|
|
457
|
+
root->insertChild(clip, 0);
|
|
458
|
+
|
|
459
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
460
|
+
EXPECT(!pixels.empty());
|
|
461
|
+
|
|
462
|
+
// Inside the clip: green at 40%.
|
|
463
|
+
EXPECT_PIXEL(pixels, 25, 25, 0, 255, 0, 102);
|
|
464
|
+
// Outside it: nothing, though the painter filled all hundred points.
|
|
465
|
+
EXPECT_TRANSPARENT(pixels, 75, 25);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
TEST(win32_paint_scrolls_a_painter_with_the_content_around_it) {
|
|
469
|
+
Tree tree;
|
|
470
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
471
|
+
RnWin32View *scroller = tree.box(2, 0, 0, 100, 100);
|
|
472
|
+
RnWin32View *canvas = tree.box(3, 0, 40, 100, 20);
|
|
473
|
+
canvas->setPainter(std::make_shared<FillPainter>(0.0f, 0.0f, 1.0f));
|
|
474
|
+
scroller->insertChild(canvas, 0);
|
|
475
|
+
root->insertChild(scroller, 0);
|
|
476
|
+
|
|
477
|
+
const RnPixels still = basalt::win32::renderToPixels(*root);
|
|
478
|
+
EXPECT(!still.empty());
|
|
479
|
+
EXPECT_PIXEL(still, 50, 45, 0, 0, 255, 255);
|
|
480
|
+
|
|
481
|
+
scroller->setScrollOffset(0, 30);
|
|
482
|
+
const RnPixels scrolled = basalt::win32::renderToPixels(*root);
|
|
483
|
+
EXPECT(!scrolled.empty());
|
|
484
|
+
// Up by thirty: the painter is not told, and does not need to be.
|
|
485
|
+
EXPECT_PIXEL(scrolled, 50, 15, 0, 0, 255, 255);
|
|
486
|
+
EXPECT_TRANSPARENT(scrolled, 50, 45);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
TEST(win32_paint_skips_a_painter_on_a_hidden_view) {
|
|
490
|
+
Tree tree;
|
|
491
|
+
RnWin32View *root = tree.box(1, 0, 0, 100, 100);
|
|
492
|
+
RnWin32View *canvas = tree.box(2, 0, 0, 100, 100);
|
|
493
|
+
auto painter = std::make_shared<FillPainter>(1.0f, 1.0f, 0.0f);
|
|
494
|
+
canvas->setPainter(painter);
|
|
495
|
+
canvas->setHidden(true);
|
|
496
|
+
root->insertChild(canvas, 0);
|
|
497
|
+
|
|
498
|
+
const RnPixels pixels = basalt::win32::renderToPixels(*root);
|
|
499
|
+
EXPECT(!pixels.empty());
|
|
500
|
+
|
|
501
|
+
// `display: none` reaches the painter as not being called at all, which for a
|
|
502
|
+
// canvas is the difference between idle and rendering a frame nobody sees.
|
|
503
|
+
EXPECT_EQ(painter->calls, 0);
|
|
504
|
+
EXPECT_TRANSPARENT(pixels, 50, 50);
|
|
505
|
+
}
|