react-native-windows 0.81.12 → 0.81.15

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.
Files changed (29) hide show
  1. package/Common/Common.vcxproj +1 -1
  2. package/Folly/Folly.vcxproj +1 -1
  3. package/Libraries/Modal/Modal.windows.js +1 -7
  4. package/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.cpp +53 -66
  5. package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +58 -21
  6. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +197 -54
  7. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +12 -3
  8. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +14 -9
  9. package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +4 -6
  10. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +101 -44
  11. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +4 -2
  12. package/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp +3 -3
  13. package/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.h +3 -1
  14. package/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp +0 -1
  15. package/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +1 -1
  16. package/Microsoft.ReactNative/Modules/Animated/AnimatedNode.cpp +3 -3
  17. package/Microsoft.ReactNative/Modules/Animated/AnimatedNode.h +3 -2
  18. package/Microsoft.ReactNative/Modules/Timing.h +2 -1
  19. package/Microsoft.ReactNative.Cxx/ModuleRegistration.h +10 -10
  20. package/PropertySheets/External/Microsoft.ReactNative.Composition.CppApp.targets +2 -2
  21. package/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.targets +2 -2
  22. package/PropertySheets/Generated/PackageVersion.g.props +3 -3
  23. package/ReactCommon/ReactCommon.vcxproj +1 -1
  24. package/Scripts/OfficeReact.Win32.nuspec +44 -44
  25. package/Scripts/Tfs/Layout-Desktop-Headers.ps1 +1 -15
  26. package/Shared/Shared.vcxitems.filters +0 -2
  27. package/package.json +1 -1
  28. package/Scripts/OpenSSL.nuspec +0 -39
  29. package/Scripts/OpenSSL.targets +0 -36
@@ -253,7 +253,10 @@ void CompositionEventHandler::Initialize() noexcept {
253
253
  if (strongThis->SurfaceId() == -1)
254
254
  return;
255
255
 
256
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
256
+ auto *rootView = strongThis->RootComponentView();
257
+ if (!rootView)
258
+ return;
259
+ auto focusedComponent = rootView->GetFocusedComponent();
257
260
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
258
261
  auto keyArgs =
259
262
  winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
@@ -279,7 +282,10 @@ void CompositionEventHandler::Initialize() noexcept {
279
282
  if (strongThis->SurfaceId() == -1)
280
283
  return;
281
284
 
282
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
285
+ auto *rootView = strongThis->RootComponentView();
286
+ if (!rootView)
287
+ return;
288
+ auto focusedComponent = rootView->GetFocusedComponent();
283
289
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
284
290
  auto keyArgs =
285
291
  winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
@@ -306,7 +312,10 @@ void CompositionEventHandler::Initialize() noexcept {
306
312
  if (strongThis->SurfaceId() == -1)
307
313
  return;
308
314
 
309
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
315
+ auto *rootView = strongThis->RootComponentView();
316
+ if (!rootView)
317
+ return;
318
+ auto focusedComponent = rootView->GetFocusedComponent();
310
319
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
311
320
  auto charArgs = winrt::make<
312
321
  winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>(
@@ -333,7 +342,10 @@ void CompositionEventHandler::Initialize() noexcept {
333
342
  if (strongThis->SurfaceId() == -1)
334
343
  return;
335
344
 
336
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
345
+ auto *rootView = strongThis->RootComponentView();
346
+ if (!rootView)
347
+ return;
348
+ auto focusedComponent = rootView->GetFocusedComponent();
337
349
  if (focusedComponent) {
338
350
  auto tag =
339
351
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)
@@ -363,6 +375,7 @@ CompositionEventHandler::~CompositionEventHandler() {
363
375
  pointerSource.PointerMoved(m_pointerMovedToken);
364
376
  pointerSource.PointerCaptureLost(m_pointerCaptureLostToken);
365
377
  pointerSource.PointerWheelChanged(m_pointerWheelChangedToken);
378
+ pointerSource.PointerExited(m_pointerExitedToken);
366
379
  auto keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(island);
367
380
  keyboardSource.KeyDown(m_keyDownToken);
368
381
  keyboardSource.KeyUp(m_keyUpToken);
@@ -386,10 +399,15 @@ facebook::react::SurfaceId CompositionEventHandler::SurfaceId() const noexcept {
386
399
  return -1;
387
400
  }
388
401
 
389
- winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView &
402
+ winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *
390
403
  CompositionEventHandler::RootComponentView() const noexcept {
391
404
  auto island = m_wkRootView.get();
392
- return *winrt::get_self<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>(island)->GetComponentView();
405
+ if (!island) {
406
+ return nullptr;
407
+ }
408
+ return winrt::get_self<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>(island)
409
+ ->GetComponentView()
410
+ .get();
393
411
  }
394
412
 
395
413
  void CompositionEventHandler::onPointerWheelChanged(
@@ -404,8 +422,11 @@ void CompositionEventHandler::onPointerWheelChanged(
404
422
 
405
423
  // In the case of a sub rootview, we may have a non-zero origin. hitTest takes a pt in the parent coords, so we
406
424
  // need to apply the current origin
407
- ptScaled += RootComponentView().layoutMetrics().frame.origin;
408
- auto tag = RootComponentView().hitTest(ptScaled, ptLocal);
425
+ auto *rootView = RootComponentView();
426
+ if (!rootView)
427
+ return;
428
+ ptScaled += rootView->layoutMetrics().frame.origin;
429
+ auto tag = rootView->hitTest(ptScaled, ptLocal);
409
430
 
410
431
  if (tag == -1)
411
432
  return;
@@ -559,7 +580,10 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
559
580
  case WM_CHAR:
560
581
  case WM_SYSCHAR: {
561
582
  if (auto strongRootView = m_wkRootView.get()) {
562
- auto focusedComponent = RootComponentView().GetFocusedComponent();
583
+ auto *rootView = RootComponentView();
584
+ if (!rootView)
585
+ break;
586
+ auto focusedComponent = rootView->GetFocusedComponent();
563
587
  auto keyboardSource = winrt::make<CompositionKeyboardSource>(this);
564
588
  auto args = winrt::make<
565
589
  winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>(
@@ -582,7 +606,10 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
582
606
  case WM_SYSKEYDOWN:
583
607
  case WM_SYSKEYUP: {
584
608
  if (auto strongRootView = m_wkRootView.get()) {
585
- auto focusedComponent = RootComponentView().GetFocusedComponent();
609
+ auto *rootView = RootComponentView();
610
+ if (!rootView)
611
+ break;
612
+ auto focusedComponent = rootView->GetFocusedComponent();
586
613
  auto keyboardSource = winrt::make<CompositionKeyboardSource>(this);
587
614
  auto args = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
588
615
  focusedComponent
@@ -614,9 +641,12 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
614
641
 
615
642
  void CompositionEventHandler::onKeyDown(
616
643
  const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
617
- RootComponentView().UseKeyboardForProgrammaticFocus(true);
644
+ auto *rootView = RootComponentView();
645
+ if (!rootView)
646
+ return;
647
+ rootView->UseKeyboardForProgrammaticFocus(true);
618
648
 
619
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
649
+ if (auto focusedComponent = rootView->GetFocusedComponent()) {
620
650
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)->OnKeyDown(args);
621
651
 
622
652
  if (args.Handled())
@@ -639,7 +669,7 @@ void CompositionEventHandler::onKeyDown(
639
669
  }
640
670
 
641
671
  if (!fCtrl && args.Key() == winrt::Windows::System::VirtualKey::Tab) {
642
- if (RootComponentView().TryMoveFocus(!fShift, winrt::Microsoft::ReactNative::FocusState::Keyboard)) {
672
+ if (rootView->TryMoveFocus(!fShift, winrt::Microsoft::ReactNative::FocusState::Keyboard)) {
643
673
  args.Handled(true);
644
674
  }
645
675
 
@@ -649,9 +679,12 @@ void CompositionEventHandler::onKeyDown(
649
679
 
650
680
  void CompositionEventHandler::onKeyUp(
651
681
  const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
652
- RootComponentView().UseKeyboardForProgrammaticFocus(true);
682
+ auto *rootView = RootComponentView();
683
+ if (!rootView)
684
+ return;
685
+ rootView->UseKeyboardForProgrammaticFocus(true);
653
686
 
654
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
687
+ if (auto focusedComponent = rootView->GetFocusedComponent()) {
655
688
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)->OnKeyUp(args);
656
689
 
657
690
  if (args.Handled())
@@ -661,7 +694,10 @@ void CompositionEventHandler::onKeyUp(
661
694
 
662
695
  void CompositionEventHandler::onCharacterReceived(
663
696
  const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept {
664
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
697
+ auto *rootView = RootComponentView();
698
+ if (!rootView)
699
+ return;
700
+ if (auto focusedComponent = rootView->GetFocusedComponent()) {
665
701
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)
666
702
  ->OnCharacterReceived(args);
667
703
 
@@ -670,7 +706,7 @@ void CompositionEventHandler::onCharacterReceived(
670
706
  }
671
707
  }
672
708
 
673
- std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPathToRoot(
709
+ std::vector<winrt::Microsoft::ReactNative::ComponentView> CompositionEventHandler::GetTouchableViewsInPathToRoot(
674
710
  const winrt::Microsoft::ReactNative::ComponentView &componentView) {
675
711
  std::vector<winrt::Microsoft::ReactNative::ComponentView> results;
676
712
  auto view = componentView;
@@ -680,6 +716,7 @@ std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPat
680
716
  }
681
717
  view = view.Parent();
682
718
  }
719
+
683
720
  return results;
684
721
  }
685
722
 
@@ -980,8 +1017,8 @@ void CompositionEventHandler::UpdateActiveTouch(
980
1017
  // activeTouch.touch.isEraser = false;
981
1018
  activeTouch.touch.pagePoint.x = ptScaled.x;
982
1019
  activeTouch.touch.pagePoint.y = ptScaled.y;
983
- activeTouch.touch.screenPoint.x = ptLocal.x;
984
- activeTouch.touch.screenPoint.y = ptLocal.y;
1020
+ activeTouch.touch.screenPoint.x = ptScaled.x;
1021
+ activeTouch.touch.screenPoint.y = ptScaled.y;
985
1022
  activeTouch.touch.offsetPoint.x = ptLocal.x;
986
1023
  activeTouch.touch.offsetPoint.y = ptLocal.y;
987
1024
  activeTouch.touch.timestamp = static_cast<facebook::react::Float>(
@@ -1034,9 +1071,12 @@ void CompositionEventHandler::getTargetPointerArgs(
1034
1071
 
1035
1072
  // 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
1036
1073
  // to apply the current origin
1037
- ptScaled += RootComponentView().layoutMetrics().frame.origin;
1074
+ auto *rootView = RootComponentView();
1075
+ if (!rootView)
1076
+ return;
1077
+ ptScaled += rootView->layoutMetrics().frame.origin;
1038
1078
 
1039
- if (std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId) != m_capturedPointers.end()) {
1079
+ if (m_capturedPointers.count(pointerId)) {
1040
1080
  assert(m_pointerCapturingComponentTag != -1);
1041
1081
  tag = m_pointerCapturingComponentTag;
1042
1082
 
@@ -1048,7 +1088,7 @@ void CompositionEventHandler::getTargetPointerArgs(
1048
1088
  ptLocal.y = ptScaled.y - (clientRect.top / strongRootView.ScaleFactor());
1049
1089
  }
1050
1090
  } else {
1051
- tag = RootComponentView().hitTest(ptScaled, ptLocal);
1091
+ tag = rootView->hitTest(ptScaled, ptLocal);
1052
1092
  }
1053
1093
  }
1054
1094
 
@@ -1060,7 +1100,7 @@ void CompositionEventHandler::onPointerCaptureLost(
1060
1100
 
1061
1101
  if (m_pointerCapturingComponentTag) {
1062
1102
  // copy array to avoid iterator being invalidated during deletion
1063
- std::vector<PointerId> capturedPointers = m_capturedPointers;
1103
+ std::unordered_set<PointerId> capturedPointers = m_capturedPointers;
1064
1104
 
1065
1105
  for (auto pointerId : capturedPointers) {
1066
1106
  releasePointerCapture(pointerId, m_pointerCapturingComponentTag);
@@ -1107,10 +1147,11 @@ void CompositionEventHandler::onPointerMoved(
1107
1147
 
1108
1148
  auto handler = [&, targetView, pointerEvent, isActiveTouch](
1109
1149
  std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
1150
+ auto *rootViewForEmitter = RootComponentView();
1110
1151
  const auto eventEmitter = targetView
1111
1152
  ? winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(targetView)
1112
1153
  ->eventEmitterAtPoint(pointerEvent.offsetPoint)
1113
- : RootComponentView().eventEmitterAtPoint(pointerEvent.offsetPoint);
1154
+ : (rootViewForEmitter ? rootViewForEmitter->eventEmitterAtPoint(pointerEvent.offsetPoint) : nullptr);
1114
1155
 
1115
1156
  if (eventEmitter != nullptr) {
1116
1157
  eventEmitter->onPointerMove(pointerEvent);
@@ -1136,7 +1177,10 @@ void CompositionEventHandler::ClearAllHoveredForPointer(const facebook::react::P
1136
1177
  // events. If we get null for the targetView, that means that the mouse is no over any components, so we have no
1137
1178
  // element to send the move event to. However we need to send something so that any previously hovered elements
1138
1179
  // are no longer hovered.
1139
- auto children = RootComponentView().Children();
1180
+ auto *rootView = RootComponentView();
1181
+ if (!rootView)
1182
+ return;
1183
+ auto children = rootView->Children();
1140
1184
  if (auto size = children.Size()) {
1141
1185
  auto firstChild = children.GetAt(0);
1142
1186
  if (auto childEventEmitter =
@@ -1181,23 +1225,31 @@ void CompositionEventHandler::onPointerPressed(
1181
1225
  winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
1182
1226
  namespace Composition = winrt::Microsoft::ReactNative::Composition;
1183
1227
 
1184
- RootComponentView().UseKeyboardForProgrammaticFocus(false);
1228
+ auto *rootView = RootComponentView();
1229
+ if (!rootView)
1230
+ return;
1231
+ rootView->UseKeyboardForProgrammaticFocus(false);
1185
1232
 
1186
1233
  // Clears any active text selection when left pointer is pressed
1187
1234
  if (pointerPoint.Properties().PointerUpdateKind() != Composition::Input::PointerUpdateKind::RightButtonPressed) {
1188
- RootComponentView().ClearCurrentTextSelection();
1235
+ rootView->ClearCurrentTextSelection();
1189
1236
  }
1190
1237
 
1191
1238
  PointerId pointerId = pointerPoint.PointerId();
1192
1239
 
1193
- auto staleTouch = std::find_if(m_activeTouches.begin(), m_activeTouches.end(), [pointerId](const auto &pair) {
1194
- return pair.second.touch.identifier == pointerId;
1195
- });
1240
+ auto staleTouch = m_activeTouches.find(pointerId);
1196
1241
 
1197
1242
  if (staleTouch != m_activeTouches.end()) {
1198
- // A pointer with this ID already exists - Should we fire a button cancel or something?
1199
- // assert(false);
1200
- return;
1243
+ // A previous pointer with this ID was never properly released (e.g., app lost focus,
1244
+ // pointer left window). Cancel the stale touch and clean it up so the new press can proceed.
1245
+ // Copy and erase before dispatching to avoid holding a reference into m_activeTouches
1246
+ // across DispatchSynthesizedTouchCancelForActiveTouch, which calls HandleIncomingPointerEvent
1247
+ // and iterates m_activeTouches internally.
1248
+ ActiveTouch staleTouchCopy = std::move(staleTouch->second);
1249
+ m_activeTouches.erase(staleTouch);
1250
+ if (staleTouchCopy.eventEmitter) {
1251
+ DispatchSynthesizedTouchCancelForActiveTouch(staleTouchCopy, pointerPoint, keyModifiers);
1252
+ }
1201
1253
  }
1202
1254
 
1203
1255
  const auto eventType = TouchEventType::Start;
@@ -1218,7 +1270,18 @@ void CompositionEventHandler::onPointerPressed(
1218
1270
  ->OnPointerPressed(args);
1219
1271
 
1220
1272
  ActiveTouch activeTouch{0};
1221
- activeTouch.touchType = UITouchType::Mouse;
1273
+ switch (pointerPoint.PointerDeviceType()) {
1274
+ case Composition::Input::PointerDeviceType::Touch:
1275
+ activeTouch.touchType = UITouchType::Touch;
1276
+ break;
1277
+ case Composition::Input::PointerDeviceType::Pen:
1278
+ activeTouch.touchType = UITouchType::Pen;
1279
+ break;
1280
+ case Composition::Input::PointerDeviceType::Mouse:
1281
+ default:
1282
+ activeTouch.touchType = UITouchType::Mouse;
1283
+ break;
1284
+ }
1222
1285
 
1223
1286
  // Map PointerUpdateKind to W3C button value
1224
1287
  // https://developer.mozilla.org/docs/Web/API/MouseEvent/button
@@ -1256,6 +1319,12 @@ void CompositionEventHandler::onPointerPressed(
1256
1319
  targetComponentView = targetComponentView.Parent();
1257
1320
  }
1258
1321
 
1322
+ // Don't register the touch if no eventEmitter was found — inserting a null-emitter entry
1323
+ // into m_activeTouches would block future presses with the same pointer ID.
1324
+ if (!activeTouch.eventEmitter) {
1325
+ return;
1326
+ }
1327
+
1259
1328
  UpdateActiveTouch(activeTouch, ptScaled, ptLocal);
1260
1329
 
1261
1330
  activeTouch.isPrimary = pointerId == 1;
@@ -1279,11 +1348,12 @@ void CompositionEventHandler::onPointerReleased(
1279
1348
  winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
1280
1349
  int pointerId = pointerPoint.PointerId();
1281
1350
 
1282
- RootComponentView().UseKeyboardForProgrammaticFocus(false);
1351
+ auto *rootView = RootComponentView();
1352
+ if (!rootView)
1353
+ return;
1354
+ rootView->UseKeyboardForProgrammaticFocus(false);
1283
1355
 
1284
- auto activeTouch = std::find_if(m_activeTouches.begin(), m_activeTouches.end(), [pointerId](const auto &pair) {
1285
- return pair.second.touch.identifier == pointerId;
1286
- });
1356
+ auto activeTouch = m_activeTouches.find(pointerId);
1287
1357
 
1288
1358
  if (activeTouch == m_activeTouches.end()) {
1289
1359
  return;
@@ -1295,8 +1365,13 @@ void CompositionEventHandler::onPointerReleased(
1295
1365
  facebook::react::Point ptLocal, ptScaled;
1296
1366
  getTargetPointerArgs(fabricuiManager, pointerPoint, tag, ptScaled, ptLocal);
1297
1367
 
1298
- if (tag == -1)
1368
+ if (tag == -1) {
1369
+ if (activeTouch->second.eventEmitter) {
1370
+ DispatchSynthesizedTouchCancelForActiveTouch(activeTouch->second, pointerPoint, keyModifiers);
1371
+ }
1372
+ m_activeTouches.erase(pointerId);
1299
1373
  return;
1374
+ }
1300
1375
 
1301
1376
  auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view;
1302
1377
  auto args = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerRoutedEventArgs>(
@@ -1328,7 +1403,7 @@ bool CompositionEventHandler::CapturePointer(
1328
1403
  }
1329
1404
 
1330
1405
  m_pointerCapturingComponentTag = tag;
1331
- m_capturedPointers.push_back(pointer.PointerId());
1406
+ m_capturedPointers.insert(pointer.PointerId());
1332
1407
  return true;
1333
1408
  }
1334
1409
 
@@ -1343,11 +1418,9 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo
1343
1418
  bool result = false;
1344
1419
 
1345
1420
  if (m_pointerCapturingComponentTag == tag) {
1346
- auto it = std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId);
1347
- if (it == m_capturedPointers.end()) {
1421
+ if (m_capturedPointers.erase(pointerId) == 0) {
1348
1422
  return false;
1349
1423
  }
1350
- m_capturedPointers.erase(it);
1351
1424
 
1352
1425
  if (std::shared_ptr<FabricUIManager> fabricuiManager =
1353
1426
  ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) {
@@ -1358,7 +1431,7 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo
1358
1431
  ->OnPointerCaptureLost();
1359
1432
  }
1360
1433
 
1361
- if (m_capturedPointers.size() == 0) {
1434
+ if (m_capturedPointers.empty()) {
1362
1435
  m_pointerCapturingComponentTag = -1;
1363
1436
  return true;
1364
1437
  }
@@ -1473,16 +1546,83 @@ bool CompositionEventHandler::IsPointerWithinInitialTree(const ActiveTouch &acti
1473
1546
  if (!initialComponentView)
1474
1547
  return false;
1475
1548
 
1476
- auto initialViewSet = GetTouchableViewsInPathToRoot(initialComponentView);
1549
+ auto *rootView = RootComponentView();
1550
+ if (!rootView)
1551
+ return false;
1552
+
1553
+ facebook::react::Point ptLocal;
1554
+ auto currentTag = rootView->hitTest(activeTouch.touch.pagePoint, ptLocal);
1555
+ if (currentTag == -1)
1556
+ return false;
1477
1557
 
1478
- for (const auto &view : initialViewSet) {
1479
- if (view.Tag() == activeTouch.touch.target)
1558
+ auto fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties());
1559
+ if (!fabricuiManager)
1560
+ return false;
1561
+
1562
+ auto initialTag = initialComponentView.Tag();
1563
+ auto &viewRegistry = fabricuiManager->GetViewRegistry();
1564
+ auto currentView = viewRegistry.componentViewDescriptorWithTag(currentTag).view;
1565
+ while (currentView) {
1566
+ if (currentView.Tag() == initialTag)
1567
+ return true;
1568
+ currentView = currentView.Parent();
1569
+ }
1570
+
1571
+ // Fallback: if the pointer drifted spatially but the original target
1572
+ // is still structurally within the initial tree, honor the tap.
1573
+ // This provides touch-device tolerance for finger drift.
1574
+ auto targetView = viewRegistry.componentViewDescriptorWithTag(activeTouch.touch.target).view;
1575
+ while (targetView) {
1576
+ if (targetView.Tag() == initialTag)
1480
1577
  return true;
1578
+ targetView = targetView.Parent();
1481
1579
  }
1482
1580
 
1483
1581
  return false;
1484
1582
  }
1485
1583
 
1584
+ void CompositionEventHandler::DispatchSynthesizedTouchCancelForActiveTouch(
1585
+ const ActiveTouch &cancelledTouch,
1586
+ const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
1587
+ winrt::Windows::System::VirtualKeyModifiers keyModifiers) {
1588
+ if (!cancelledTouch.eventEmitter) {
1589
+ return;
1590
+ }
1591
+
1592
+ facebook::react::PointerEvent pointerEvent =
1593
+ CreatePointerEventFromActiveTouch(cancelledTouch, TouchEventType::Cancel);
1594
+ winrt::Microsoft::ReactNative::ComponentView targetView{nullptr};
1595
+ facebook::react::SharedTouchEventEmitter emitter = cancelledTouch.eventEmitter;
1596
+ auto pointerHandler = [emitter, pointerEvent](std::vector<winrt::Microsoft::ReactNative::ComponentView> &) {
1597
+ emitter->onPointerCancel(pointerEvent);
1598
+ };
1599
+ HandleIncomingPointerEvent(pointerEvent, targetView, pointerPoint, keyModifiers, pointerHandler);
1600
+
1601
+ facebook::react::TouchEvent touchEvent;
1602
+ touchEvent.changedTouches.insert(cancelledTouch.touch);
1603
+
1604
+ for (const auto &pair : m_activeTouches) {
1605
+ if (!pair.second.eventEmitter) {
1606
+ continue;
1607
+ }
1608
+
1609
+ if (touchEvent.changedTouches.find(pair.second.touch) != touchEvent.changedTouches.end()) {
1610
+ continue;
1611
+ }
1612
+
1613
+ touchEvent.touches.insert(pair.second.touch);
1614
+ }
1615
+
1616
+ for (const auto &pair : m_activeTouches) {
1617
+ if (pair.second.eventEmitter == cancelledTouch.eventEmitter &&
1618
+ touchEvent.changedTouches.find(pair.second.touch) == touchEvent.changedTouches.end()) {
1619
+ touchEvent.targetTouches.insert(pair.second.touch);
1620
+ }
1621
+ }
1622
+
1623
+ cancelledTouch.eventEmitter->onTouchCancel(touchEvent);
1624
+ }
1625
+
1486
1626
  // If we have events that include multiple pointer updates, we should change arg from pointerId to vector<pointerId>
1487
1627
  void CompositionEventHandler::DispatchTouchEvent(
1488
1628
  TouchEventType eventType,
@@ -1518,16 +1658,19 @@ void CompositionEventHandler::DispatchTouchEvent(
1518
1658
  bool shouldLeave = (eventType == TouchEventType::End && activeTouch.shouldLeaveWhenReleased) ||
1519
1659
  eventType == TouchEventType::Cancel;
1520
1660
  if (!shouldLeave) {
1521
- const auto &viewRegistry = fabricuiManager->GetViewRegistry();
1522
- facebook::react::Point ptLocal;
1523
- auto targetTag = RootComponentView().hitTest(pointerEvent.clientPoint, ptLocal);
1524
- if (targetTag != -1) {
1525
- auto targetComponentViewDescriptor = viewRegistry.componentViewDescriptorWithTag(targetTag);
1526
- targetView = FindClosestFabricManagedTouchableView(targetComponentViewDescriptor.view);
1661
+ auto *rootViewForHit = RootComponentView();
1662
+ if (rootViewForHit) {
1663
+ const auto &viewRegistry = fabricuiManager->GetViewRegistry();
1664
+ facebook::react::Point ptLocal;
1665
+ auto targetTag = rootViewForHit->hitTest(pointerEvent.clientPoint, ptLocal);
1666
+ if (targetTag != -1) {
1667
+ auto targetComponentViewDescriptor = viewRegistry.componentViewDescriptorWithTag(targetTag);
1668
+ targetView = FindClosestFabricManagedTouchableView(targetComponentViewDescriptor.view);
1669
+ }
1527
1670
  }
1528
1671
  }
1529
1672
 
1530
- auto handler = [&activeTouch, eventType, &pointerEvent](
1673
+ auto handler = [this, &activeTouch, eventType, &pointerEvent](
1531
1674
  std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
1532
1675
  switch (eventType) {
1533
1676
  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 &RootComponentView() const noexcept;
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
- static bool IsPointerWithinInitialTree(const ActiveTouch &activeTouch) noexcept;
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,6 +151,14 @@ 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
 
@@ -161,7 +170,7 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE
161
170
  winrt::Microsoft::ReactNative::ReactContext m_context;
162
171
 
163
172
  facebook::react::Tag m_pointerCapturingComponentTag{-1}; // Component that has captured input
164
- std::vector<PointerId> m_capturedPointers;
173
+ std::unordered_set<PointerId> m_capturedPointers;
165
174
  HCURSOR m_hcursor{nullptr};
166
175
  bool m_hcursorOwned{false}; // If we create the cursor, so we need to destroy it
167
176
  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>
@@ -1015,15 +1017,18 @@ bool ComponentView::anyHitTestHelper(
1015
1017
  facebook::react::Tag &targetTag,
1016
1018
  facebook::react::Point &ptContent,
1017
1019
  facebook::react::Point &localPt) const noexcept {
1018
- if (auto index = m_children.Size()) {
1019
- do {
1020
- index--;
1021
- targetTag = winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_children.GetAt(index))
1022
- ->hitTest(ptContent, localPt);
1023
- if (targetTag != -1) {
1024
- return true;
1025
- }
1026
- } while (index != 0);
1020
+ auto size = m_children.Size();
1021
+ if (size == 0) {
1022
+ return false;
1023
+ }
1024
+
1025
+ // m_children is backed by single_threaded_vector (std::vector), so GetAt is O(1)
1026
+ for (uint32_t i = size; i > 0; --i) {
1027
+ targetTag = winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_children.GetAt(i - 1))
1028
+ ->hitTest(ptContent, localPt);
1029
+ if (targetTag != -1) {
1030
+ return true;
1031
+ }
1027
1032
  }
1028
1033
 
1029
1034
  return false;
@@ -981,8 +981,6 @@ void ScrollViewComponentView::OnPointerPressed(
981
981
  Super::OnPointerPressed(args);
982
982
 
983
983
  if (!args.Handled()) {
984
- auto f = args.Pointer();
985
- auto g = f.PointerDeviceType();
986
984
  m_scrollVisual.OnPointerPressed(args);
987
985
  }
988
986
  }
@@ -1023,16 +1021,16 @@ void ScrollViewComponentView::OnKeyDown(
1023
1021
  args.Handled(pageUp(true));
1024
1022
  break;
1025
1023
  case winrt::Windows::System::VirtualKey::Up:
1026
- args.Handled(lineUp(true));
1024
+ args.Handled(lineUp(false));
1027
1025
  break;
1028
1026
  case winrt::Windows::System::VirtualKey::Down:
1029
- args.Handled(lineDown(true));
1027
+ args.Handled(lineDown(false));
1030
1028
  break;
1031
1029
  case winrt::Windows::System::VirtualKey::Left:
1032
- args.Handled(lineLeft(true));
1030
+ args.Handled(lineLeft(false));
1033
1031
  break;
1034
1032
  case winrt::Windows::System::VirtualKey::Right:
1035
- args.Handled(lineRight(true));
1033
+ args.Handled(lineRight(false));
1036
1034
  break;
1037
1035
  }
1038
1036