react-native-windows 0.84.0-preview.5 → 0.84.0-preview.7
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/Libraries/Modal/Modal.windows.js +1 -7
- package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +58 -20
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +238 -57
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +18 -4
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +14 -9
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +0 -2
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +98 -44
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +4 -2
- package/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp +3 -3
- package/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.h +3 -1
- package/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp +0 -1
- package/Microsoft.ReactNative/Modules/Animated/AnimatedNode.cpp +3 -3
- package/Microsoft.ReactNative/Modules/Animated/AnimatedNode.h +3 -2
- package/Microsoft.ReactNative/Modules/Timing.h +2 -1
- package/PropertySheets/Generated/PackageVersion.g.props +2 -2
- package/PropertySheets/JSEngine.props +1 -1
- package/package.json +1 -1
|
@@ -250,7 +250,10 @@ void CompositionEventHandler::Initialize() noexcept {
|
|
|
250
250
|
if (strongThis->SurfaceId() == -1)
|
|
251
251
|
return;
|
|
252
252
|
|
|
253
|
-
auto
|
|
253
|
+
auto *rootView = strongThis->RootComponentView();
|
|
254
|
+
if (!rootView)
|
|
255
|
+
return;
|
|
256
|
+
auto focusedComponent = rootView->GetFocusedComponent();
|
|
254
257
|
auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
|
|
255
258
|
auto keyArgs =
|
|
256
259
|
winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
|
|
@@ -276,7 +279,10 @@ void CompositionEventHandler::Initialize() noexcept {
|
|
|
276
279
|
if (strongThis->SurfaceId() == -1)
|
|
277
280
|
return;
|
|
278
281
|
|
|
279
|
-
auto
|
|
282
|
+
auto *rootView = strongThis->RootComponentView();
|
|
283
|
+
if (!rootView)
|
|
284
|
+
return;
|
|
285
|
+
auto focusedComponent = rootView->GetFocusedComponent();
|
|
280
286
|
auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
|
|
281
287
|
auto keyArgs =
|
|
282
288
|
winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
|
|
@@ -303,7 +309,10 @@ void CompositionEventHandler::Initialize() noexcept {
|
|
|
303
309
|
if (strongThis->SurfaceId() == -1)
|
|
304
310
|
return;
|
|
305
311
|
|
|
306
|
-
auto
|
|
312
|
+
auto *rootView = strongThis->RootComponentView();
|
|
313
|
+
if (!rootView)
|
|
314
|
+
return;
|
|
315
|
+
auto focusedComponent = rootView->GetFocusedComponent();
|
|
307
316
|
auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
|
|
308
317
|
auto charArgs = winrt::make<
|
|
309
318
|
winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>(
|
|
@@ -330,7 +339,10 @@ void CompositionEventHandler::Initialize() noexcept {
|
|
|
330
339
|
if (strongThis->SurfaceId() == -1)
|
|
331
340
|
return;
|
|
332
341
|
|
|
333
|
-
auto
|
|
342
|
+
auto *rootView = strongThis->RootComponentView();
|
|
343
|
+
if (!rootView)
|
|
344
|
+
return;
|
|
345
|
+
auto focusedComponent = rootView->GetFocusedComponent();
|
|
334
346
|
if (focusedComponent) {
|
|
335
347
|
auto tag =
|
|
336
348
|
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)
|
|
@@ -358,6 +370,7 @@ CompositionEventHandler::~CompositionEventHandler() {
|
|
|
358
370
|
pointerSource.PointerMoved(m_pointerMovedToken);
|
|
359
371
|
pointerSource.PointerCaptureLost(m_pointerCaptureLostToken);
|
|
360
372
|
pointerSource.PointerWheelChanged(m_pointerWheelChangedToken);
|
|
373
|
+
pointerSource.PointerExited(m_pointerExitedToken);
|
|
361
374
|
auto keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(island);
|
|
362
375
|
keyboardSource.KeyDown(m_keyDownToken);
|
|
363
376
|
keyboardSource.KeyUp(m_keyUpToken);
|
|
@@ -380,10 +393,15 @@ facebook::react::SurfaceId CompositionEventHandler::SurfaceId() const noexcept {
|
|
|
380
393
|
return -1;
|
|
381
394
|
}
|
|
382
395
|
|
|
383
|
-
winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView
|
|
396
|
+
winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *
|
|
384
397
|
CompositionEventHandler::RootComponentView() const noexcept {
|
|
385
398
|
auto island = m_wkRootView.get();
|
|
386
|
-
|
|
399
|
+
if (!island) {
|
|
400
|
+
return nullptr;
|
|
401
|
+
}
|
|
402
|
+
return winrt::get_self<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>(island)
|
|
403
|
+
->GetComponentView()
|
|
404
|
+
.get();
|
|
387
405
|
}
|
|
388
406
|
|
|
389
407
|
void CompositionEventHandler::onPointerWheelChanged(
|
|
@@ -398,8 +416,11 @@ void CompositionEventHandler::onPointerWheelChanged(
|
|
|
398
416
|
|
|
399
417
|
// In the case of a sub rootview, we may have a non-zero origin. hitTest takes a pt in the parent coords, so we
|
|
400
418
|
// need to apply the current origin
|
|
401
|
-
|
|
402
|
-
|
|
419
|
+
auto *rootView = RootComponentView();
|
|
420
|
+
if (!rootView)
|
|
421
|
+
return;
|
|
422
|
+
ptScaled += rootView->layoutMetrics().frame.origin;
|
|
423
|
+
auto tag = rootView->hitTest(ptScaled, ptLocal);
|
|
403
424
|
|
|
404
425
|
if (tag == -1)
|
|
405
426
|
return;
|
|
@@ -553,7 +574,10 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
|
|
|
553
574
|
case WM_CHAR:
|
|
554
575
|
case WM_SYSCHAR: {
|
|
555
576
|
if (auto strongRootView = m_wkRootView.get()) {
|
|
556
|
-
auto
|
|
577
|
+
auto *rootView = RootComponentView();
|
|
578
|
+
if (!rootView)
|
|
579
|
+
break;
|
|
580
|
+
auto focusedComponent = rootView->GetFocusedComponent();
|
|
557
581
|
auto keyboardSource = winrt::make<CompositionKeyboardSource>(this);
|
|
558
582
|
auto args = winrt::make<
|
|
559
583
|
winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>(
|
|
@@ -576,7 +600,10 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
|
|
|
576
600
|
case WM_SYSKEYDOWN:
|
|
577
601
|
case WM_SYSKEYUP: {
|
|
578
602
|
if (auto strongRootView = m_wkRootView.get()) {
|
|
579
|
-
auto
|
|
603
|
+
auto *rootView = RootComponentView();
|
|
604
|
+
if (!rootView)
|
|
605
|
+
break;
|
|
606
|
+
auto focusedComponent = rootView->GetFocusedComponent();
|
|
580
607
|
auto keyboardSource = winrt::make<CompositionKeyboardSource>(this);
|
|
581
608
|
auto args = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
|
|
582
609
|
focusedComponent
|
|
@@ -608,9 +635,12 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
|
|
|
608
635
|
|
|
609
636
|
void CompositionEventHandler::onKeyDown(
|
|
610
637
|
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
|
|
611
|
-
RootComponentView()
|
|
638
|
+
auto *rootView = RootComponentView();
|
|
639
|
+
if (!rootView)
|
|
640
|
+
return;
|
|
641
|
+
rootView->UseKeyboardForProgrammaticFocus(true);
|
|
612
642
|
|
|
613
|
-
if (auto focusedComponent =
|
|
643
|
+
if (auto focusedComponent = rootView->GetFocusedComponent()) {
|
|
614
644
|
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)->OnKeyDown(args);
|
|
615
645
|
|
|
616
646
|
if (args.Handled())
|
|
@@ -633,7 +663,7 @@ void CompositionEventHandler::onKeyDown(
|
|
|
633
663
|
}
|
|
634
664
|
|
|
635
665
|
if (!fCtrl && args.Key() == winrt::Windows::System::VirtualKey::Tab) {
|
|
636
|
-
if (
|
|
666
|
+
if (rootView->TryMoveFocus(!fShift, winrt::Microsoft::ReactNative::FocusState::Keyboard)) {
|
|
637
667
|
args.Handled(true);
|
|
638
668
|
}
|
|
639
669
|
|
|
@@ -643,9 +673,12 @@ void CompositionEventHandler::onKeyDown(
|
|
|
643
673
|
|
|
644
674
|
void CompositionEventHandler::onKeyUp(
|
|
645
675
|
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
|
|
646
|
-
RootComponentView()
|
|
676
|
+
auto *rootView = RootComponentView();
|
|
677
|
+
if (!rootView)
|
|
678
|
+
return;
|
|
679
|
+
rootView->UseKeyboardForProgrammaticFocus(true);
|
|
647
680
|
|
|
648
|
-
if (auto focusedComponent =
|
|
681
|
+
if (auto focusedComponent = rootView->GetFocusedComponent()) {
|
|
649
682
|
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)->OnKeyUp(args);
|
|
650
683
|
|
|
651
684
|
if (args.Handled())
|
|
@@ -655,7 +688,10 @@ void CompositionEventHandler::onKeyUp(
|
|
|
655
688
|
|
|
656
689
|
void CompositionEventHandler::onCharacterReceived(
|
|
657
690
|
const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept {
|
|
658
|
-
|
|
691
|
+
auto *rootView = RootComponentView();
|
|
692
|
+
if (!rootView)
|
|
693
|
+
return;
|
|
694
|
+
if (auto focusedComponent = rootView->GetFocusedComponent()) {
|
|
659
695
|
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)
|
|
660
696
|
->OnCharacterReceived(args);
|
|
661
697
|
|
|
@@ -664,7 +700,7 @@ void CompositionEventHandler::onCharacterReceived(
|
|
|
664
700
|
}
|
|
665
701
|
}
|
|
666
702
|
|
|
667
|
-
std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPathToRoot(
|
|
703
|
+
std::vector<winrt::Microsoft::ReactNative::ComponentView> CompositionEventHandler::GetTouchableViewsInPathToRoot(
|
|
668
704
|
const winrt::Microsoft::ReactNative::ComponentView &componentView) {
|
|
669
705
|
std::vector<winrt::Microsoft::ReactNative::ComponentView> results;
|
|
670
706
|
auto view = componentView;
|
|
@@ -674,6 +710,7 @@ std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPat
|
|
|
674
710
|
}
|
|
675
711
|
view = view.Parent();
|
|
676
712
|
}
|
|
713
|
+
|
|
677
714
|
return results;
|
|
678
715
|
}
|
|
679
716
|
|
|
@@ -974,8 +1011,8 @@ void CompositionEventHandler::UpdateActiveTouch(
|
|
|
974
1011
|
// activeTouch.touch.isEraser = false;
|
|
975
1012
|
activeTouch.touch.pagePoint.x = ptScaled.x;
|
|
976
1013
|
activeTouch.touch.pagePoint.y = ptScaled.y;
|
|
977
|
-
activeTouch.touch.screenPoint.x =
|
|
978
|
-
activeTouch.touch.screenPoint.y =
|
|
1014
|
+
activeTouch.touch.screenPoint.x = ptScaled.x;
|
|
1015
|
+
activeTouch.touch.screenPoint.y = ptScaled.y;
|
|
979
1016
|
activeTouch.touch.offsetPoint.x = ptLocal.x;
|
|
980
1017
|
activeTouch.touch.offsetPoint.y = ptLocal.y;
|
|
981
1018
|
activeTouch.touch.timestamp = static_cast<facebook::react::Float>(
|
|
@@ -1028,9 +1065,12 @@ void CompositionEventHandler::getTargetPointerArgs(
|
|
|
1028
1065
|
|
|
1029
1066
|
// In the case of a sub rootview, we may have a non-zero origin. hitTest takes a pt in the parent coords, so we need
|
|
1030
1067
|
// to apply the current origin
|
|
1031
|
-
|
|
1068
|
+
auto *rootView = RootComponentView();
|
|
1069
|
+
if (!rootView)
|
|
1070
|
+
return;
|
|
1071
|
+
ptScaled += rootView->layoutMetrics().frame.origin;
|
|
1032
1072
|
|
|
1033
|
-
if (
|
|
1073
|
+
if (m_capturedPointers.count(pointerId)) {
|
|
1034
1074
|
assert(m_pointerCapturingComponentTag != -1);
|
|
1035
1075
|
tag = m_pointerCapturingComponentTag;
|
|
1036
1076
|
|
|
@@ -1042,7 +1082,7 @@ void CompositionEventHandler::getTargetPointerArgs(
|
|
|
1042
1082
|
ptLocal.y = ptScaled.y - (clientRect.top / strongRootView.ScaleFactor());
|
|
1043
1083
|
}
|
|
1044
1084
|
} else {
|
|
1045
|
-
tag =
|
|
1085
|
+
tag = rootView->hitTest(ptScaled, ptLocal);
|
|
1046
1086
|
}
|
|
1047
1087
|
}
|
|
1048
1088
|
|
|
@@ -1054,7 +1094,7 @@ void CompositionEventHandler::onPointerCaptureLost(
|
|
|
1054
1094
|
|
|
1055
1095
|
if (m_pointerCapturingComponentTag) {
|
|
1056
1096
|
// copy array to avoid iterator being invalidated during deletion
|
|
1057
|
-
std::
|
|
1097
|
+
std::unordered_set<PointerId> capturedPointers = m_capturedPointers;
|
|
1058
1098
|
|
|
1059
1099
|
for (auto pointerId : capturedPointers) {
|
|
1060
1100
|
releasePointerCapture(pointerId, m_pointerCapturingComponentTag);
|
|
@@ -1101,10 +1141,11 @@ void CompositionEventHandler::onPointerMoved(
|
|
|
1101
1141
|
|
|
1102
1142
|
auto handler = [&, targetView, pointerEvent, isActiveTouch](
|
|
1103
1143
|
std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
|
|
1144
|
+
auto *rootViewForEmitter = RootComponentView();
|
|
1104
1145
|
const auto eventEmitter = targetView
|
|
1105
1146
|
? winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(targetView)
|
|
1106
1147
|
->eventEmitterAtPoint(pointerEvent.offsetPoint)
|
|
1107
|
-
:
|
|
1148
|
+
: (rootViewForEmitter ? rootViewForEmitter->eventEmitterAtPoint(pointerEvent.offsetPoint) : nullptr);
|
|
1108
1149
|
|
|
1109
1150
|
if (eventEmitter != nullptr) {
|
|
1110
1151
|
eventEmitter->onPointerMove(pointerEvent);
|
|
@@ -1130,7 +1171,10 @@ void CompositionEventHandler::ClearAllHoveredForPointer(const facebook::react::P
|
|
|
1130
1171
|
// events. If we get null for the targetView, that means that the mouse is no over any components, so we have no
|
|
1131
1172
|
// element to send the move event to. However we need to send something so that any previously hovered elements
|
|
1132
1173
|
// are no longer hovered.
|
|
1133
|
-
auto
|
|
1174
|
+
auto *rootView = RootComponentView();
|
|
1175
|
+
if (!rootView)
|
|
1176
|
+
return;
|
|
1177
|
+
auto children = rootView->Children();
|
|
1134
1178
|
if (auto size = children.Size()) {
|
|
1135
1179
|
auto firstChild = children.GetAt(0);
|
|
1136
1180
|
if (auto childEventEmitter =
|
|
@@ -1170,28 +1214,67 @@ void CompositionEventHandler::onPointerExited(
|
|
|
1170
1214
|
}
|
|
1171
1215
|
}
|
|
1172
1216
|
|
|
1217
|
+
// Windows touch pointer IDs can be arbitrarily large (e.g. 2233). React Native's JS
|
|
1218
|
+
// touch handler uses identifiers as array indices and warns/misbehaves for values > 20.
|
|
1219
|
+
// This function maps each live Windows pointer to a small identifier in [0, 19] by
|
|
1220
|
+
// scanning m_activeTouches for in-use slots and cycling from the last assigned index.
|
|
1221
|
+
// Identifier MOUSE_POINTER_ID (1) is permanently reserved for mouse and never returned here.
|
|
1222
|
+
int CompositionEventHandler::AllocateTouchIdentifier() noexcept {
|
|
1223
|
+
constexpr int kMaxTouchIdentifier = 20;
|
|
1224
|
+
for (int i = 0; i < kMaxTouchIdentifier; i++) {
|
|
1225
|
+
int candidate = (m_touchId + i) % kMaxTouchIdentifier;
|
|
1226
|
+
if (candidate == static_cast<int>(MOUSE_POINTER_ID)) {
|
|
1227
|
+
continue; // reserved for mouse
|
|
1228
|
+
}
|
|
1229
|
+
bool inUse = std::any_of(m_activeTouches.begin(), m_activeTouches.end(), [candidate](const auto &pair) {
|
|
1230
|
+
return pair.second.touch.identifier == candidate;
|
|
1231
|
+
});
|
|
1232
|
+
if (!inUse) {
|
|
1233
|
+
m_touchId = (candidate + 1) % kMaxTouchIdentifier;
|
|
1234
|
+
return candidate;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
// All non-mouse slots occupied (> 19 simultaneous touch/pen points) — wrap anyway,
|
|
1238
|
+
// skipping the mouse-reserved slot.
|
|
1239
|
+
int fallback = m_touchId;
|
|
1240
|
+
m_touchId = (m_touchId + 1) % kMaxTouchIdentifier;
|
|
1241
|
+
if (fallback == static_cast<int>(MOUSE_POINTER_ID)) {
|
|
1242
|
+
fallback = m_touchId;
|
|
1243
|
+
m_touchId = (m_touchId + 1) % kMaxTouchIdentifier;
|
|
1244
|
+
}
|
|
1245
|
+
return fallback;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1173
1248
|
void CompositionEventHandler::onPointerPressed(
|
|
1174
1249
|
const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
|
|
1175
1250
|
winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
|
|
1176
1251
|
namespace Composition = winrt::Microsoft::ReactNative::Composition;
|
|
1177
1252
|
|
|
1178
|
-
RootComponentView()
|
|
1253
|
+
auto *rootView = RootComponentView();
|
|
1254
|
+
if (!rootView)
|
|
1255
|
+
return;
|
|
1256
|
+
rootView->UseKeyboardForProgrammaticFocus(false);
|
|
1179
1257
|
|
|
1180
1258
|
// Clears any active text selection when left pointer is pressed
|
|
1181
1259
|
if (pointerPoint.Properties().PointerUpdateKind() != Composition::Input::PointerUpdateKind::RightButtonPressed) {
|
|
1182
|
-
|
|
1260
|
+
rootView->ClearCurrentTextSelection();
|
|
1183
1261
|
}
|
|
1184
1262
|
|
|
1185
1263
|
PointerId pointerId = pointerPoint.PointerId();
|
|
1186
1264
|
|
|
1187
|
-
auto staleTouch =
|
|
1188
|
-
return pair.second.touch.identifier == pointerId;
|
|
1189
|
-
});
|
|
1265
|
+
auto staleTouch = m_activeTouches.find(pointerId);
|
|
1190
1266
|
|
|
1191
1267
|
if (staleTouch != m_activeTouches.end()) {
|
|
1192
|
-
// A pointer with this ID
|
|
1193
|
-
//
|
|
1194
|
-
|
|
1268
|
+
// A previous pointer with this ID was never properly released (e.g., app lost focus,
|
|
1269
|
+
// pointer left window). Cancel the stale touch and clean it up so the new press can proceed.
|
|
1270
|
+
// Copy and erase before dispatching to avoid holding a reference into m_activeTouches
|
|
1271
|
+
// across DispatchSynthesizedTouchCancelForActiveTouch, which calls HandleIncomingPointerEvent
|
|
1272
|
+
// and iterates m_activeTouches internally.
|
|
1273
|
+
ActiveTouch staleTouchCopy = std::move(staleTouch->second);
|
|
1274
|
+
m_activeTouches.erase(staleTouch);
|
|
1275
|
+
if (staleTouchCopy.eventEmitter) {
|
|
1276
|
+
DispatchSynthesizedTouchCancelForActiveTouch(staleTouchCopy, pointerPoint, keyModifiers);
|
|
1277
|
+
}
|
|
1195
1278
|
}
|
|
1196
1279
|
|
|
1197
1280
|
const auto eventType = TouchEventType::Start;
|
|
@@ -1212,7 +1295,18 @@ void CompositionEventHandler::onPointerPressed(
|
|
|
1212
1295
|
->OnPointerPressed(args);
|
|
1213
1296
|
|
|
1214
1297
|
ActiveTouch activeTouch{0};
|
|
1215
|
-
|
|
1298
|
+
switch (pointerPoint.PointerDeviceType()) {
|
|
1299
|
+
case Composition::Input::PointerDeviceType::Touch:
|
|
1300
|
+
activeTouch.touchType = UITouchType::Touch;
|
|
1301
|
+
break;
|
|
1302
|
+
case Composition::Input::PointerDeviceType::Pen:
|
|
1303
|
+
activeTouch.touchType = UITouchType::Pen;
|
|
1304
|
+
break;
|
|
1305
|
+
case Composition::Input::PointerDeviceType::Mouse:
|
|
1306
|
+
default:
|
|
1307
|
+
activeTouch.touchType = UITouchType::Mouse;
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1216
1310
|
|
|
1217
1311
|
// Map PointerUpdateKind to W3C button value
|
|
1218
1312
|
// https://developer.mozilla.org/docs/Web/API/MouseEvent/button
|
|
@@ -1250,14 +1344,27 @@ void CompositionEventHandler::onPointerPressed(
|
|
|
1250
1344
|
targetComponentView = targetComponentView.Parent();
|
|
1251
1345
|
}
|
|
1252
1346
|
|
|
1347
|
+
// Don't register the touch if no eventEmitter was found — inserting a null-emitter entry
|
|
1348
|
+
// into m_activeTouches would block future presses with the same pointer ID.
|
|
1349
|
+
if (!activeTouch.eventEmitter) {
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1253
1353
|
UpdateActiveTouch(activeTouch, ptScaled, ptLocal);
|
|
1254
1354
|
|
|
1255
1355
|
activeTouch.isPrimary = pointerId == 1;
|
|
1256
|
-
|
|
1356
|
+
// Map the Windows pointer ID to a small identifier (0–19) safe for use as a JS array index.
|
|
1357
|
+
// Windows touch IDs can be arbitrarily large (e.g. 2233), which causes React Native to warn
|
|
1358
|
+
// and corrupts touch state, leaving Pressables stuck after a scroll.
|
|
1359
|
+
// Mouse pointer ID is always 1 (MOUSE_POINTER_ID), which is already within the safe range —
|
|
1360
|
+
// use it directly to preserve stable, predictable identifier assignment for mouse input.
|
|
1361
|
+
activeTouch.touch.identifier = (pointerPoint.PointerDeviceType() == Composition::Input::PointerDeviceType::Mouse)
|
|
1362
|
+
? static_cast<int>(MOUSE_POINTER_ID)
|
|
1363
|
+
: AllocateTouchIdentifier();
|
|
1257
1364
|
|
|
1258
1365
|
// If the pointer has not been marked as hovering over views before the touch started, we register
|
|
1259
1366
|
// that the activeTouch should not maintain its hovered state once the pointer has been lifted.
|
|
1260
|
-
auto currentlyHoveredTags = m_currentlyHoveredViewsPerPointer.find(
|
|
1367
|
+
auto currentlyHoveredTags = m_currentlyHoveredViewsPerPointer.find(pointerId);
|
|
1261
1368
|
if (currentlyHoveredTags == m_currentlyHoveredViewsPerPointer.end() || currentlyHoveredTags->second.empty()) {
|
|
1262
1369
|
activeTouch.shouldLeaveWhenReleased = true;
|
|
1263
1370
|
}
|
|
@@ -1273,11 +1380,12 @@ void CompositionEventHandler::onPointerReleased(
|
|
|
1273
1380
|
winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
|
|
1274
1381
|
int pointerId = pointerPoint.PointerId();
|
|
1275
1382
|
|
|
1276
|
-
RootComponentView()
|
|
1383
|
+
auto *rootView = RootComponentView();
|
|
1384
|
+
if (!rootView)
|
|
1385
|
+
return;
|
|
1386
|
+
rootView->UseKeyboardForProgrammaticFocus(false);
|
|
1277
1387
|
|
|
1278
|
-
auto activeTouch =
|
|
1279
|
-
return pair.second.touch.identifier == pointerId;
|
|
1280
|
-
});
|
|
1388
|
+
auto activeTouch = m_activeTouches.find(pointerId);
|
|
1281
1389
|
|
|
1282
1390
|
if (activeTouch == m_activeTouches.end()) {
|
|
1283
1391
|
return;
|
|
@@ -1289,8 +1397,13 @@ void CompositionEventHandler::onPointerReleased(
|
|
|
1289
1397
|
facebook::react::Point ptLocal, ptScaled;
|
|
1290
1398
|
getTargetPointerArgs(fabricuiManager, pointerPoint, tag, ptScaled, ptLocal);
|
|
1291
1399
|
|
|
1292
|
-
if (tag == -1)
|
|
1400
|
+
if (tag == -1) {
|
|
1401
|
+
if (activeTouch->second.eventEmitter) {
|
|
1402
|
+
DispatchSynthesizedTouchCancelForActiveTouch(activeTouch->second, pointerPoint, keyModifiers);
|
|
1403
|
+
}
|
|
1404
|
+
m_activeTouches.erase(pointerId);
|
|
1293
1405
|
return;
|
|
1406
|
+
}
|
|
1294
1407
|
|
|
1295
1408
|
auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view;
|
|
1296
1409
|
auto args = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerRoutedEventArgs>(
|
|
@@ -1322,7 +1435,7 @@ bool CompositionEventHandler::CapturePointer(
|
|
|
1322
1435
|
}
|
|
1323
1436
|
|
|
1324
1437
|
m_pointerCapturingComponentTag = tag;
|
|
1325
|
-
m_capturedPointers.
|
|
1438
|
+
m_capturedPointers.insert(pointer.PointerId());
|
|
1326
1439
|
return true;
|
|
1327
1440
|
}
|
|
1328
1441
|
|
|
@@ -1337,11 +1450,9 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo
|
|
|
1337
1450
|
bool result = false;
|
|
1338
1451
|
|
|
1339
1452
|
if (m_pointerCapturingComponentTag == tag) {
|
|
1340
|
-
|
|
1341
|
-
if (it == m_capturedPointers.end()) {
|
|
1453
|
+
if (m_capturedPointers.erase(pointerId) == 0) {
|
|
1342
1454
|
return false;
|
|
1343
1455
|
}
|
|
1344
|
-
m_capturedPointers.erase(it);
|
|
1345
1456
|
|
|
1346
1457
|
if (std::shared_ptr<FabricUIManager> fabricuiManager =
|
|
1347
1458
|
::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) {
|
|
@@ -1352,7 +1463,7 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo
|
|
|
1352
1463
|
->OnPointerCaptureLost();
|
|
1353
1464
|
}
|
|
1354
1465
|
|
|
1355
|
-
if (m_capturedPointers.
|
|
1466
|
+
if (m_capturedPointers.empty()) {
|
|
1356
1467
|
m_pointerCapturingComponentTag = -1;
|
|
1357
1468
|
return true;
|
|
1358
1469
|
}
|
|
@@ -1467,16 +1578,83 @@ bool CompositionEventHandler::IsPointerWithinInitialTree(const ActiveTouch &acti
|
|
|
1467
1578
|
if (!initialComponentView)
|
|
1468
1579
|
return false;
|
|
1469
1580
|
|
|
1470
|
-
auto
|
|
1581
|
+
auto *rootView = RootComponentView();
|
|
1582
|
+
if (!rootView)
|
|
1583
|
+
return false;
|
|
1584
|
+
|
|
1585
|
+
facebook::react::Point ptLocal;
|
|
1586
|
+
auto currentTag = rootView->hitTest(activeTouch.touch.pagePoint, ptLocal);
|
|
1587
|
+
if (currentTag == -1)
|
|
1588
|
+
return false;
|
|
1589
|
+
|
|
1590
|
+
auto fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties());
|
|
1591
|
+
if (!fabricuiManager)
|
|
1592
|
+
return false;
|
|
1593
|
+
|
|
1594
|
+
auto initialTag = initialComponentView.Tag();
|
|
1595
|
+
auto &viewRegistry = fabricuiManager->GetViewRegistry();
|
|
1596
|
+
auto currentView = viewRegistry.componentViewDescriptorWithTag(currentTag).view;
|
|
1597
|
+
while (currentView) {
|
|
1598
|
+
if (currentView.Tag() == initialTag)
|
|
1599
|
+
return true;
|
|
1600
|
+
currentView = currentView.Parent();
|
|
1601
|
+
}
|
|
1471
1602
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1603
|
+
// Fallback: if the pointer drifted spatially but the original target
|
|
1604
|
+
// is still structurally within the initial tree, honor the tap.
|
|
1605
|
+
// This provides touch-device tolerance for finger drift.
|
|
1606
|
+
auto targetView = viewRegistry.componentViewDescriptorWithTag(activeTouch.touch.target).view;
|
|
1607
|
+
while (targetView) {
|
|
1608
|
+
if (targetView.Tag() == initialTag)
|
|
1474
1609
|
return true;
|
|
1610
|
+
targetView = targetView.Parent();
|
|
1475
1611
|
}
|
|
1476
1612
|
|
|
1477
1613
|
return false;
|
|
1478
1614
|
}
|
|
1479
1615
|
|
|
1616
|
+
void CompositionEventHandler::DispatchSynthesizedTouchCancelForActiveTouch(
|
|
1617
|
+
const ActiveTouch &cancelledTouch,
|
|
1618
|
+
const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
|
|
1619
|
+
winrt::Windows::System::VirtualKeyModifiers keyModifiers) {
|
|
1620
|
+
if (!cancelledTouch.eventEmitter) {
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
facebook::react::PointerEvent pointerEvent =
|
|
1625
|
+
CreatePointerEventFromActiveTouch(cancelledTouch, TouchEventType::Cancel);
|
|
1626
|
+
winrt::Microsoft::ReactNative::ComponentView targetView{nullptr};
|
|
1627
|
+
facebook::react::SharedTouchEventEmitter emitter = cancelledTouch.eventEmitter;
|
|
1628
|
+
auto pointerHandler = [emitter, pointerEvent](std::vector<winrt::Microsoft::ReactNative::ComponentView> &) {
|
|
1629
|
+
emitter->onPointerCancel(pointerEvent);
|
|
1630
|
+
};
|
|
1631
|
+
HandleIncomingPointerEvent(pointerEvent, targetView, pointerPoint, keyModifiers, pointerHandler);
|
|
1632
|
+
|
|
1633
|
+
facebook::react::TouchEvent touchEvent;
|
|
1634
|
+
touchEvent.changedTouches.insert(cancelledTouch.touch);
|
|
1635
|
+
|
|
1636
|
+
for (const auto &pair : m_activeTouches) {
|
|
1637
|
+
if (!pair.second.eventEmitter) {
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
if (touchEvent.changedTouches.find(pair.second.touch) != touchEvent.changedTouches.end()) {
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
touchEvent.touches.insert(pair.second.touch);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
for (const auto &pair : m_activeTouches) {
|
|
1649
|
+
if (pair.second.eventEmitter == cancelledTouch.eventEmitter &&
|
|
1650
|
+
touchEvent.changedTouches.find(pair.second.touch) == touchEvent.changedTouches.end()) {
|
|
1651
|
+
touchEvent.targetTouches.insert(pair.second.touch);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
cancelledTouch.eventEmitter->onTouchCancel(touchEvent);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1480
1658
|
// If we have events that include multiple pointer updates, we should change arg from pointerId to vector<pointerId>
|
|
1481
1659
|
void CompositionEventHandler::DispatchTouchEvent(
|
|
1482
1660
|
TouchEventType eventType,
|
|
@@ -1501,7 +1679,7 @@ void CompositionEventHandler::DispatchTouchEvent(
|
|
|
1501
1679
|
continue;
|
|
1502
1680
|
}
|
|
1503
1681
|
|
|
1504
|
-
if (
|
|
1682
|
+
if (pair.first == pointerId) {
|
|
1505
1683
|
event.changedTouches.insert(activeTouch.touch);
|
|
1506
1684
|
}
|
|
1507
1685
|
uniqueEventEmitters.insert(activeTouch.eventEmitter);
|
|
@@ -1512,16 +1690,19 @@ void CompositionEventHandler::DispatchTouchEvent(
|
|
|
1512
1690
|
bool shouldLeave = (eventType == TouchEventType::End && activeTouch.shouldLeaveWhenReleased) ||
|
|
1513
1691
|
eventType == TouchEventType::Cancel;
|
|
1514
1692
|
if (!shouldLeave) {
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
auto
|
|
1520
|
-
|
|
1693
|
+
auto *rootViewForHit = RootComponentView();
|
|
1694
|
+
if (rootViewForHit) {
|
|
1695
|
+
const auto &viewRegistry = fabricuiManager->GetViewRegistry();
|
|
1696
|
+
facebook::react::Point ptLocal;
|
|
1697
|
+
auto targetTag = rootViewForHit->hitTest(pointerEvent.clientPoint, ptLocal);
|
|
1698
|
+
if (targetTag != -1) {
|
|
1699
|
+
auto targetComponentViewDescriptor = viewRegistry.componentViewDescriptorWithTag(targetTag);
|
|
1700
|
+
targetView = FindClosestFabricManagedTouchableView(targetComponentViewDescriptor.view);
|
|
1701
|
+
}
|
|
1521
1702
|
}
|
|
1522
1703
|
}
|
|
1523
1704
|
|
|
1524
|
-
auto handler = [&activeTouch, eventType, &pointerEvent](
|
|
1705
|
+
auto handler = [this, &activeTouch, eventType, &pointerEvent](
|
|
1525
1706
|
std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
|
|
1526
1707
|
switch (eventType) {
|
|
1527
1708
|
case TouchEventType::Start:
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <winrt/Windows.Devices.Input.h>
|
|
15
15
|
#include <optional>
|
|
16
16
|
#include <set>
|
|
17
|
+
#include <unordered_set>
|
|
17
18
|
|
|
18
19
|
namespace winrt {
|
|
19
20
|
using namespace Windows::UI;
|
|
@@ -79,7 +80,7 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE
|
|
|
79
80
|
bool releasePointerCapture(PointerId pointerId, facebook::react::Tag tag) noexcept;
|
|
80
81
|
|
|
81
82
|
facebook::react::SurfaceId SurfaceId() const noexcept;
|
|
82
|
-
winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView
|
|
83
|
+
winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *RootComponentView() const noexcept;
|
|
83
84
|
|
|
84
85
|
enum class UITouchType {
|
|
85
86
|
Mouse,
|
|
@@ -141,7 +142,7 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE
|
|
|
141
142
|
ReactTaggedView initialComponentView{nullptr};
|
|
142
143
|
};
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
bool IsPointerWithinInitialTree(const ActiveTouch &activeTouch) noexcept;
|
|
145
146
|
static bool IsEndishEventType(TouchEventType eventType) noexcept;
|
|
146
147
|
static const char *PointerTypeCStringFromUITouchType(UITouchType type) noexcept;
|
|
147
148
|
static facebook::react::PointerEvent CreatePointerEventFromActiveTouch(
|
|
@@ -150,18 +151,31 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE
|
|
|
150
151
|
static void
|
|
151
152
|
UpdateActiveTouch(ActiveTouch &activeTouch, facebook::react::Point ptScaled, facebook::react::Point ptLocal) noexcept;
|
|
152
153
|
|
|
154
|
+
void DispatchSynthesizedTouchCancelForActiveTouch(
|
|
155
|
+
const ActiveTouch &cancelledTouch,
|
|
156
|
+
const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
|
|
157
|
+
winrt::Windows::System::VirtualKeyModifiers keyModifiers);
|
|
158
|
+
|
|
159
|
+
std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPathToRoot(
|
|
160
|
+
const winrt::Microsoft::ReactNative::ComponentView &componentView);
|
|
161
|
+
|
|
153
162
|
void UpdateCursor() noexcept;
|
|
154
163
|
void SetCursor(facebook::react::Cursor cursor, HCURSOR hcur) noexcept;
|
|
155
164
|
|
|
165
|
+
// Allocates a small touch identifier (0–19) that is safe to use as a JS array index.
|
|
166
|
+
// Windows pointer IDs can be arbitrarily large, which causes React Native to warn and
|
|
167
|
+
// back-fill huge arrays, corrupting touch state after scrolling.
|
|
168
|
+
int AllocateTouchIdentifier() noexcept;
|
|
169
|
+
|
|
156
170
|
std::map<PointerId, ActiveTouch> m_activeTouches; // iOS is map of touch event args to ActiveTouch..?
|
|
157
|
-
|
|
171
|
+
int m_touchId = 0; // cycling base used by AllocateTouchIdentifier
|
|
158
172
|
|
|
159
173
|
std::map<PointerId, std::vector<ReactTaggedView>> m_currentlyHoveredViewsPerPointer;
|
|
160
174
|
winrt::weak_ref<winrt::Microsoft::ReactNative::ReactNativeIsland> m_wkRootView;
|
|
161
175
|
winrt::Microsoft::ReactNative::ReactContext m_context;
|
|
162
176
|
|
|
163
177
|
facebook::react::Tag m_pointerCapturingComponentTag{-1}; // Component that has captured input
|
|
164
|
-
std::
|
|
178
|
+
std::unordered_set<PointerId> m_capturedPointers;
|
|
165
179
|
HCURSOR m_hcursor{nullptr};
|
|
166
180
|
bool m_hcursorOwned{false}; // If we create the cursor, so we need to destroy it
|
|
167
181
|
facebook::react::Cursor m_currentCursor{facebook::react::Cursor::Auto};
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
#include "CompositionViewComponentView.h"
|
|
8
8
|
|
|
9
|
+
#include <vector>
|
|
10
|
+
|
|
9
11
|
#include <AutoDraw.h>
|
|
10
12
|
#include <Fabric/AbiState.h>
|
|
11
13
|
#include <Fabric/AbiViewProps.h>
|
|
@@ -1013,15 +1015,18 @@ bool ComponentView::anyHitTestHelper(
|
|
|
1013
1015
|
facebook::react::Tag &targetTag,
|
|
1014
1016
|
facebook::react::Point &ptContent,
|
|
1015
1017
|
facebook::react::Point &localPt) const noexcept {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1018
|
+
auto size = m_children.Size();
|
|
1019
|
+
if (size == 0) {
|
|
1020
|
+
return false;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// m_children is backed by single_threaded_vector (std::vector), so GetAt is O(1)
|
|
1024
|
+
for (uint32_t i = size; i > 0; --i) {
|
|
1025
|
+
targetTag = winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_children.GetAt(i - 1))
|
|
1026
|
+
->hitTest(ptContent, localPt);
|
|
1027
|
+
if (targetTag != -1) {
|
|
1028
|
+
return true;
|
|
1029
|
+
}
|
|
1025
1030
|
}
|
|
1026
1031
|
|
|
1027
1032
|
return false;
|