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,46 @@
|
|
|
1
|
+
#include "Win32TitleBarLayout.h"
|
|
2
|
+
|
|
3
|
+
#include "RnWin32View.h"
|
|
4
|
+
|
|
5
|
+
#include <cmath>
|
|
6
|
+
#include <string>
|
|
7
|
+
|
|
8
|
+
namespace basalt::win32 {
|
|
9
|
+
|
|
10
|
+
bool isTitleBarDragRegionAt(RnWin32View *root, float x, float y) {
|
|
11
|
+
for (RnWin32View *view = hitTest(root, x, y); view != nullptr; view = view->parent()) {
|
|
12
|
+
const std::string &id = view->nativeId();
|
|
13
|
+
if (id == kTitleBarDragRegionId) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (id == kTitleBarNoDragRegionId) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
CaptionMetrics captionMetricsForDpi(uint32_t dpi) {
|
|
24
|
+
const float scale = dpi == 0 ? 1.0f : static_cast<float>(dpi) / 96.0f;
|
|
25
|
+
return CaptionMetrics{std::round(kCaptionHeightDips * scale),
|
|
26
|
+
std::round(kCaptionButtonWidthDips * scale)};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
CaptionButton captionButtonAt(float x, float y, float clientWidth, const CaptionMetrics &metrics) {
|
|
30
|
+
if (y < 0 || y >= metrics.height || x < 0 || x >= clientWidth || metrics.buttonWidth <= 0) {
|
|
31
|
+
return CaptionButton::None;
|
|
32
|
+
}
|
|
33
|
+
const float fromRight = clientWidth - x;
|
|
34
|
+
if (fromRight <= metrics.buttonWidth) {
|
|
35
|
+
return CaptionButton::Close;
|
|
36
|
+
}
|
|
37
|
+
if (fromRight <= 2 * metrics.buttonWidth) {
|
|
38
|
+
return CaptionButton::Maximize;
|
|
39
|
+
}
|
|
40
|
+
if (fromRight <= 3 * metrics.buttonWidth) {
|
|
41
|
+
return CaptionButton::Minimize;
|
|
42
|
+
}
|
|
43
|
+
return CaptionButton::None;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
} // namespace basalt::win32
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Where a hidden title bar's pieces are: which caption button a point is over,
|
|
2
|
+
// how big the caption is at a DPI, and whether a point drags the window.
|
|
3
|
+
//
|
|
4
|
+
// The geometry half of Win32TitleBar, kept apart from it so that it needs no
|
|
5
|
+
// window, no DWM and no React Native -- only a view tree and a size -- and can
|
|
6
|
+
// be tested with none of them. A custom caption fails in ways a person notices
|
|
7
|
+
// at once and a test does not, unless the test asks exactly these questions.
|
|
8
|
+
|
|
9
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
#include "TitleBarRegions.h"
|
|
12
|
+
|
|
13
|
+
#include <cstdint>
|
|
14
|
+
|
|
15
|
+
namespace basalt::win32 {
|
|
16
|
+
|
|
17
|
+
class RnWin32View;
|
|
18
|
+
|
|
19
|
+
// What <TitleBar.DragRegion> and <TitleBar.NoDragRegion> set as a view's
|
|
20
|
+
// nativeID, under this namespace's own names so that everything already
|
|
21
|
+
// written against them keeps compiling.
|
|
22
|
+
//
|
|
23
|
+
// The values come from core/TitleBarRegions.h rather than being spelled again
|
|
24
|
+
// here: GTK reads the same two strings, and src/TitleBar.tsx writes them, so a
|
|
25
|
+
// literal in this file would be the third copy of something that only works
|
|
26
|
+
// when all of them agree.
|
|
27
|
+
inline constexpr const char *kTitleBarDragRegionId = basalt::kTitleBarDragRegionId;
|
|
28
|
+
inline constexpr const char *kTitleBarNoDragRegionId = basalt::kTitleBarNoDragRegionId;
|
|
29
|
+
|
|
30
|
+
// Whether a point, in the root's coordinates, drags the window.
|
|
31
|
+
//
|
|
32
|
+
// The deepest view under the point and then each of its ancestors are asked in
|
|
33
|
+
// turn, and the nearest one marked either way decides. So a button inside a
|
|
34
|
+
// drag region stays a button once it is marked no-drag, a label inside a drag
|
|
35
|
+
// region drags without being marked at all, and a point under nothing marked
|
|
36
|
+
// is ordinary client area.
|
|
37
|
+
bool isTitleBarDragRegionAt(RnWin32View *root, float x, float y);
|
|
38
|
+
|
|
39
|
+
enum class CaptionButton { None, Minimize, Maximize, Close };
|
|
40
|
+
|
|
41
|
+
// Windows 11's own caption sizes, in device-independent pixels.
|
|
42
|
+
inline constexpr float kCaptionHeightDips = 32.0f;
|
|
43
|
+
inline constexpr float kCaptionButtonWidthDips = 46.0f;
|
|
44
|
+
|
|
45
|
+
// Those sizes at a DPI, in client pixels -- which on this host are also the
|
|
46
|
+
// surface root's units, because the root is sized to the client rectangle.
|
|
47
|
+
struct CaptionMetrics {
|
|
48
|
+
float height{0};
|
|
49
|
+
float buttonWidth{0};
|
|
50
|
+
};
|
|
51
|
+
CaptionMetrics captionMetricsForDpi(uint32_t dpi);
|
|
52
|
+
|
|
53
|
+
// The caption button under a point in client coordinates, for a window
|
|
54
|
+
// `clientWidth` wide. The three sit flush right, in the order Windows puts
|
|
55
|
+
// them: minimise, maximise, close.
|
|
56
|
+
CaptionButton captionButtonAt(float x, float y, float clientWidth, const CaptionMetrics &metrics);
|
|
57
|
+
|
|
58
|
+
} // namespace basalt::win32
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
#include "Win32TouchDispatcher.h"
|
|
2
|
+
|
|
3
|
+
#include "Gestures.h"
|
|
4
|
+
|
|
5
|
+
#include <react/renderer/components/view/PointerEvent.h>
|
|
6
|
+
#include <react/renderer/components/view/TouchEvent.h>
|
|
7
|
+
|
|
8
|
+
#include <cstdint>
|
|
9
|
+
|
|
10
|
+
namespace basalt {
|
|
11
|
+
|
|
12
|
+
using facebook::react::HighResTimeStamp;
|
|
13
|
+
using facebook::react::Point;
|
|
14
|
+
using facebook::react::PointerEvent;
|
|
15
|
+
using facebook::react::Tag;
|
|
16
|
+
using facebook::react::Touch;
|
|
17
|
+
using facebook::react::TouchEvent;
|
|
18
|
+
using facebook::react::TouchEventEmitter;
|
|
19
|
+
using facebook::react::Touches;
|
|
20
|
+
using win32::RnWin32View;
|
|
21
|
+
|
|
22
|
+
namespace {
|
|
23
|
+
|
|
24
|
+
// A desktop pointer is one touch point, and React Native identifies touches by
|
|
25
|
+
// number. Zero is the first (and here only) finger. The same number identifies
|
|
26
|
+
// the pointer in pointer events, where React Native keys its hover tracking by
|
|
27
|
+
// it.
|
|
28
|
+
constexpr int kPointerIdentifier = 0;
|
|
29
|
+
|
|
30
|
+
// The payload for a hovering pointer, at a point in root coordinates with the
|
|
31
|
+
// target's origin in the same space.
|
|
32
|
+
PointerEvent hoverEvent(double x, double y, double originX, double originY) {
|
|
33
|
+
PointerEvent event{};
|
|
34
|
+
event.pointerId = kPointerIdentifier;
|
|
35
|
+
event.pointerType = "mouse";
|
|
36
|
+
event.clientPoint = Point{.x = static_cast<facebook::react::Float>(x),
|
|
37
|
+
.y = static_cast<facebook::react::Float>(y)};
|
|
38
|
+
event.screenPoint = event.clientPoint;
|
|
39
|
+
event.offsetPoint = Point{.x = static_cast<facebook::react::Float>(x - originX),
|
|
40
|
+
.y = static_cast<facebook::react::Float>(y - originY)};
|
|
41
|
+
event.width = 1;
|
|
42
|
+
event.height = 1;
|
|
43
|
+
// A hovering mouse presses nothing. `button` is -1 rather than 0 because 0 is
|
|
44
|
+
// the left button; -1 is W3C's "no button changed state".
|
|
45
|
+
event.button = -1;
|
|
46
|
+
event.buttons = 0;
|
|
47
|
+
event.isPrimary = true;
|
|
48
|
+
event.timeStamp = HighResTimeStamp::now();
|
|
49
|
+
return event;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// A point through a 2D affine matrix in Direct2D's Matrix3x2F order and its
|
|
53
|
+
// row-vector convention, which is the order `localToParent` writes.
|
|
54
|
+
void mapPoint(const float m[6], double x, double y, double &outX, double &outY) {
|
|
55
|
+
const double mappedX = m[0] * x + m[2] * y + m[4];
|
|
56
|
+
const double mappedY = m[1] * x + m[3] * y + m[5];
|
|
57
|
+
outX = mappedX;
|
|
58
|
+
outY = mappedY;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The views under a point, innermost first, each with its origin in the
|
|
62
|
+
// surface root's coordinates. That is what a gesture recogniser needs and a
|
|
63
|
+
// touch does not: a touch is reported against one target, while a gesture may
|
|
64
|
+
// be attached to any ancestor of the view that was hit.
|
|
65
|
+
//
|
|
66
|
+
// The origin is carried *up* the chain rather than recomputed per view, so the
|
|
67
|
+
// walk stays linear and each view's contribution -- its own transform, then its
|
|
68
|
+
// parent's scroll offset -- is applied exactly once. AppKit gets the same
|
|
69
|
+
// answer from convertPoint:toView: and GTK from the widget's allocation; here
|
|
70
|
+
// there is no toolkit geometry, so the composition is written out. It is the
|
|
71
|
+
// mirror of what `hitTest` did on the way down, which is the only thing that
|
|
72
|
+
// makes the two agree about a rotated or scrolled ancestor.
|
|
73
|
+
//
|
|
74
|
+
// Built only when something is attached; see core/Gestures.h.
|
|
75
|
+
std::vector<basalt::HitView> hitChain(RnWin32View *root, double x, double y) {
|
|
76
|
+
std::vector<basalt::HitView> chain;
|
|
77
|
+
RnWin32View *hit = win32::hitTest(root, static_cast<float>(x), static_cast<float>(y));
|
|
78
|
+
if (hit == nullptr) {
|
|
79
|
+
return chain;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
double originX = 0.0;
|
|
83
|
+
double originY = 0.0;
|
|
84
|
+
for (RnWin32View *view = hit; view != nullptr; view = view->parent()) {
|
|
85
|
+
chain.push_back(basalt::HitView{
|
|
86
|
+
.tag = static_cast<int>(view->tag()), .originX = originX, .originY = originY});
|
|
87
|
+
if (view == root) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
RnWin32View *parent = view->parent();
|
|
91
|
+
if (parent == nullptr) {
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
float local[6];
|
|
95
|
+
view->localToParent(local);
|
|
96
|
+
mapPoint(local, originX, originY, originX, originY);
|
|
97
|
+
originX -= parent->scrollX();
|
|
98
|
+
originY -= parent->scrollY();
|
|
99
|
+
}
|
|
100
|
+
return chain;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
} // namespace
|
|
104
|
+
|
|
105
|
+
Win32TouchDispatcher::Win32TouchDispatcher(
|
|
106
|
+
Win32MountingManager *mountingManager,
|
|
107
|
+
RnWin32View *surfaceRoot)
|
|
108
|
+
: mountingManager_(mountingManager), surfaceRoot_(surfaceRoot) {}
|
|
109
|
+
|
|
110
|
+
void Win32TouchDispatcher::synthesiseTap(double x, double y) {
|
|
111
|
+
synthesiseTap(x, y, basalt::PointerButton::Primary);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
void Win32TouchDispatcher::synthesiseTap(double x, double y, basalt::PointerButton button) {
|
|
115
|
+
dispatchTouchStart(x, y, button);
|
|
116
|
+
dispatchTouchEnd(x, y, button);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
void Win32TouchDispatcher::synthesiseHover(double x, double y) {
|
|
120
|
+
if (x < 0 || y < 0) {
|
|
121
|
+
dispatchHoverLeave();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
dispatchHover(x, y);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
void Win32TouchDispatcher::synthesiseDrag(
|
|
128
|
+
double fromX, double fromY, double toX, double toY, int steps) {
|
|
129
|
+
dispatchTouchStart(fromX, fromY, basalt::PointerButton::Primary);
|
|
130
|
+
const int count = steps < 1 ? 1 : steps;
|
|
131
|
+
for (int step = 1; step <= count; step++) {
|
|
132
|
+
const double progress = static_cast<double>(step) / count;
|
|
133
|
+
dispatchTouchMove(fromX + (toX - fromX) * progress, fromY + (toY - fromY) * progress);
|
|
134
|
+
}
|
|
135
|
+
dispatchTouchEnd(toX, toY, basalt::PointerButton::Primary);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// Hit testing
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
Tag hitTestTag(RnWin32View *root, double x, double y) {
|
|
143
|
+
RnWin32View *hit = win32::hitTest(root, static_cast<float>(x), static_cast<float>(y));
|
|
144
|
+
return hit == nullptr ? 0 : static_cast<Tag>(hit->tag());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
// Dispatch
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
void Win32TouchDispatcher::dispatchTouchStart(double x, double y, basalt::PointerButton button) {
|
|
152
|
+
if (surfaceRoot_ == nullptr) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// The innermost view, and where inside it the press landed. Needed for the
|
|
157
|
+
// pointer event whichever button it was; PointerEventsProcessor walks up from
|
|
158
|
+
// there itself.
|
|
159
|
+
Tag target = 0;
|
|
160
|
+
double originX = 0;
|
|
161
|
+
double originY = 0;
|
|
162
|
+
for (const auto &view : hitChain(surfaceRoot_, x, y)) {
|
|
163
|
+
target = static_cast<Tag>(view.tag);
|
|
164
|
+
originX = view.originX;
|
|
165
|
+
originY = view.originY;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
if (target == 0) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Every button produces a pointer event, which is where `button` can be said
|
|
173
|
+
// at all.
|
|
174
|
+
emitPointerButton(true, target, originX, originY, x, y, button);
|
|
175
|
+
|
|
176
|
+
// Only the primary one goes any further. A secondary click is not a press --
|
|
177
|
+
// the web fires no `click` for one and no desktop treats it as an activation
|
|
178
|
+
// -- so it must not reach the responder system, a gesture handler, or
|
|
179
|
+
// `onPress`. See core/PointerButtons.h.
|
|
180
|
+
//
|
|
181
|
+
// This host was the odd one out in the other direction: it handled only
|
|
182
|
+
// WM_LBUTTONDOWN, so a right-click did nothing whatsoever.
|
|
183
|
+
if (!basalt::isPressButton(button)) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!basalt::gestures().empty()) {
|
|
188
|
+
basalt::gestures().pointerDown(
|
|
189
|
+
hitChain(surfaceRoot_, x, y), x, y, basalt::monotonicMilliseconds());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
activeTarget_ = target;
|
|
193
|
+
isDown_ = true;
|
|
194
|
+
emit(TouchKind::Start, target, x, y);
|
|
195
|
+
|
|
196
|
+
// A gesture that claimed the pointer on contact -- a native handler -- takes
|
|
197
|
+
// it away from React Native's responder system immediately.
|
|
198
|
+
yieldToGesture(x, y);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
void Win32TouchDispatcher::dispatchTouchMove(double x, double y) {
|
|
202
|
+
if (!basalt::gestures().empty()) {
|
|
203
|
+
basalt::gestures().pointerMove(x, y, basalt::monotonicMilliseconds());
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Motion with no button down is hover, which the touch model has no place
|
|
207
|
+
// for. Reporting it would look to the responder system like a finger dragging
|
|
208
|
+
// across the screen at all times.
|
|
209
|
+
if (!isDown_ || activeTarget_ == 0) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (yieldToGesture(x, y)) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
emit(TouchKind::Move, activeTarget_, x, y);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
void Win32TouchDispatcher::dispatchTouchEnd(double x, double y, basalt::PointerButton button) {
|
|
219
|
+
// The release half of the pointer event, for every button. Reported against
|
|
220
|
+
// whatever is under the pointer now rather than what was under it on press,
|
|
221
|
+
// which is what a release means when nothing was captured.
|
|
222
|
+
if (surfaceRoot_ != nullptr) {
|
|
223
|
+
for (const auto &view : hitChain(surfaceRoot_, x, y)) {
|
|
224
|
+
emitPointerButton(
|
|
225
|
+
false, static_cast<Tag>(view.tag), view.originX, view.originY, x, y, button);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// And nothing else for a button that never pressed anything -- there is no
|
|
231
|
+
// touch to end, no gesture to finish and no <Switch> to toggle.
|
|
232
|
+
if (!basalt::isPressButton(button)) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!basalt::gestures().empty()) {
|
|
237
|
+
basalt::gestures().pointerUp(x, y, basalt::monotonicMilliseconds());
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!isDown_ || activeTarget_ == 0) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const Tag target = activeTarget_;
|
|
244
|
+
isDown_ = false;
|
|
245
|
+
activeTarget_ = 0;
|
|
246
|
+
emit(TouchKind::End, target, x, y);
|
|
247
|
+
|
|
248
|
+
// A <Switch> on this host is painted rather than mounted, so nothing
|
|
249
|
+
// underneath it turns a click into a toggle -- GTK's GtkSwitch and AppKit's
|
|
250
|
+
// NSSwitch both do that for themselves. This is the one line that makes up
|
|
251
|
+
// the difference; it is a no-op for every view that is not a switch.
|
|
252
|
+
if (mountingManager_ != nullptr) {
|
|
253
|
+
mountingManager_->pressedView(target);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
void Win32TouchDispatcher::dispatchTouchCancel() {
|
|
258
|
+
if (!basalt::gestures().empty()) {
|
|
259
|
+
basalt::gestures().pointerCancel();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (!isDown_ || activeTarget_ == 0) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const Tag target = activeTarget_;
|
|
266
|
+
isDown_ = false;
|
|
267
|
+
activeTarget_ = 0;
|
|
268
|
+
emit(TouchKind::Cancel, target, 0, 0);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
// Hover
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
|
|
275
|
+
void Win32TouchDispatcher::dispatchHover(double x, double y) {
|
|
276
|
+
// The same chain the gesture path uses -- this is the one place where the
|
|
277
|
+
// absence of a toolkit pays off, because `hitChain` already composes the
|
|
278
|
+
// transforms and scroll offsets that GTK and AppKit answer for themselves.
|
|
279
|
+
//
|
|
280
|
+
// Only the innermost view is reported against -- React Native's
|
|
281
|
+
// PointerEventsProcessor walks up from it itself -- but the whole chain is
|
|
282
|
+
// still needed, because a listener on any ancestor is reason to dispatch.
|
|
283
|
+
Tag target = 0;
|
|
284
|
+
double originX = 0;
|
|
285
|
+
double originY = 0;
|
|
286
|
+
std::uint16_t listeners = HoverListenerNone;
|
|
287
|
+
for (const auto &view : hitChain(surfaceRoot_, x, y)) {
|
|
288
|
+
if (target == 0) {
|
|
289
|
+
target = static_cast<Tag>(view.tag);
|
|
290
|
+
originX = view.originX;
|
|
291
|
+
originY = view.originY;
|
|
292
|
+
}
|
|
293
|
+
listeners |= mountingManager_->hoverListenersForTag(static_cast<Tag>(view.tag));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (target == 0) {
|
|
297
|
+
dispatchHoverLeave();
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (!hover_.admitMove(static_cast<int>(target), listeners)) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
emitPointerMove(target, originX, originY, x, y);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
void Win32TouchDispatcher::dispatchHoverLeave() {
|
|
307
|
+
const int target = hover_.admitLeave();
|
|
308
|
+
if (target == 0) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const auto emitter = std::dynamic_pointer_cast<const TouchEventEmitter>(
|
|
312
|
+
mountingManager_->eventEmitterForTag(static_cast<Tag>(target)));
|
|
313
|
+
if (emitter == nullptr) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
// A leave from a platform is not forwarded: the processor reads it as the
|
|
317
|
+
// pointer being gone and unwinds the path it is holding. So the coordinates
|
|
318
|
+
// on it are never seen by an app, which is just as well -- WM_MOUSELEAVE
|
|
319
|
+
// reports no position at all.
|
|
320
|
+
emitter->onPointerLeave(hoverEvent(0, 0, 0, 0));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
void Win32TouchDispatcher::emitPointerButton(bool down,
|
|
324
|
+
Tag target,
|
|
325
|
+
double originX,
|
|
326
|
+
double originY,
|
|
327
|
+
double x,
|
|
328
|
+
double y,
|
|
329
|
+
basalt::PointerButton button) {
|
|
330
|
+
const auto emitter = std::dynamic_pointer_cast<const TouchEventEmitter>(
|
|
331
|
+
mountingManager_->eventEmitterForTag(target));
|
|
332
|
+
if (emitter == nullptr) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
PointerEvent event = hoverEvent(x, y, originX, originY);
|
|
336
|
+
event.button = static_cast<int>(button);
|
|
337
|
+
// Held *during* the event, which is a different number from `button` in the
|
|
338
|
+
// same event: on release nothing is held any more. See core/PointerButtons.h.
|
|
339
|
+
event.buttons = down ? static_cast<int>(basalt::buttonsMaskFor(button)) : 0;
|
|
340
|
+
if (down) {
|
|
341
|
+
emitter->onPointerDown(event);
|
|
342
|
+
} else {
|
|
343
|
+
emitter->onPointerUp(event);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
void Win32TouchDispatcher::emitPointerMove(
|
|
348
|
+
Tag target, double originX, double originY, double x, double y) {
|
|
349
|
+
const auto emitter = std::dynamic_pointer_cast<const TouchEventEmitter>(
|
|
350
|
+
mountingManager_->eventEmitterForTag(target));
|
|
351
|
+
if (emitter == nullptr) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
emitter->onPointerMove(hoverEvent(x, y, originX, originY));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// A gesture recogniser that has activated owns the pointer, and React Native's
|
|
358
|
+
// responder system must be told the touch it was following is gone -- otherwise
|
|
359
|
+
// panning across a <Pressable> pans *and* presses it. This is what RNGH's
|
|
360
|
+
// setJSResponder does on the platforms it was written for; here both sides are
|
|
361
|
+
// fed from this one place, so it is a cancel rather than a negotiation.
|
|
362
|
+
bool Win32TouchDispatcher::yieldToGesture(double x, double y) {
|
|
363
|
+
if (!isDown_ || activeTarget_ == 0 || !basalt::gestures().hasActiveHandler()) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
const Tag target = activeTarget_;
|
|
367
|
+
isDown_ = false;
|
|
368
|
+
activeTarget_ = 0;
|
|
369
|
+
emit(TouchKind::Cancel, target, x, y);
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
void Win32TouchDispatcher::emit(TouchKind kind, Tag target, double x, double y) {
|
|
374
|
+
if (mountingManager_ == nullptr) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const auto emitter = std::dynamic_pointer_cast<const TouchEventEmitter>(
|
|
378
|
+
mountingManager_->eventEmitterForTag(target));
|
|
379
|
+
if (emitter == nullptr) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const auto now = HighResTimeStamp::now();
|
|
384
|
+
|
|
385
|
+
Touch touch{};
|
|
386
|
+
touch.identifier = kPointerIdentifier;
|
|
387
|
+
touch.target = target;
|
|
388
|
+
// Coordinates arrive relative to the surface root, which is what React Native
|
|
389
|
+
// calls the page.
|
|
390
|
+
touch.pagePoint = Point{.x = static_cast<facebook::react::Float>(x),
|
|
391
|
+
.y = static_cast<facebook::react::Float>(y)};
|
|
392
|
+
touch.screenPoint = touch.pagePoint;
|
|
393
|
+
// Relative to the target, which is what `offsetPoint` means and what
|
|
394
|
+
// `locationX`/`locationY` are built from. It carried the page point until
|
|
395
|
+
// now: right only for a view at the surface's origin, and wrong by that
|
|
396
|
+
// view's position for every other.
|
|
397
|
+
//
|
|
398
|
+
// The page point is the fallback, which is what every touch carried before:
|
|
399
|
+
// the target unmounted between the press and this event, or a chain with no
|
|
400
|
+
// inverse. Refusing would drop the touch.
|
|
401
|
+
float offsetX = static_cast<float>(x);
|
|
402
|
+
float offsetY = static_cast<float>(y);
|
|
403
|
+
if (mountingManager_ != nullptr) {
|
|
404
|
+
if (RnWin32View *view = mountingManager_->viewForTag(target)) {
|
|
405
|
+
view->pageToLocal(surfaceRoot_, static_cast<float>(x), static_cast<float>(y),
|
|
406
|
+
offsetX, offsetY);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
touch.offsetPoint = Point{.x = static_cast<facebook::react::Float>(offsetX),
|
|
410
|
+
.y = static_cast<facebook::react::Float>(offsetY)};
|
|
411
|
+
touch.force = 1.0F;
|
|
412
|
+
touch.timeStamp = now;
|
|
413
|
+
|
|
414
|
+
Touches changed{};
|
|
415
|
+
changed.insert(touch);
|
|
416
|
+
|
|
417
|
+
const bool isEnd = kind == TouchKind::End || kind == TouchKind::Cancel;
|
|
418
|
+
|
|
419
|
+
TouchEvent event{};
|
|
420
|
+
event.changedTouches = changed;
|
|
421
|
+
// On touchend the finger is gone, so it is no longer in `touches`. Getting
|
|
422
|
+
// this wrong leaves the responder system believing a touch is still active,
|
|
423
|
+
// which swallows the next press.
|
|
424
|
+
event.touches = isEnd ? Touches{} : changed;
|
|
425
|
+
event.targetTouches = event.touches;
|
|
426
|
+
|
|
427
|
+
switch (kind) {
|
|
428
|
+
case TouchKind::Start:
|
|
429
|
+
emitter->onTouchStart(event);
|
|
430
|
+
break;
|
|
431
|
+
case TouchKind::Move:
|
|
432
|
+
emitter->onTouchMove(event);
|
|
433
|
+
break;
|
|
434
|
+
case TouchKind::End:
|
|
435
|
+
emitter->onTouchEnd(event);
|
|
436
|
+
break;
|
|
437
|
+
case TouchKind::Cancel:
|
|
438
|
+
emitter->onTouchCancel(event);
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
} // namespace basalt
|