react-native-windows 0.84.0-preview.5 → 0.84.0-preview.6

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.
@@ -41,13 +41,7 @@ export type PublicModalInstance = HostInstance;
41
41
  const ModalEventEmitter =
42
42
  (Platform.OS === 'ios' || Platform.OS === 'windows') && // [Windows]
43
43
  NativeModalManager != null
44
- ? new NativeEventEmitter<ModalEventDefinitions>(
45
- // T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
46
- // If you want to use the native module on other platforms, please remove this condition and test its behavior
47
- Platform.OS !== 'ios' && Platform.OS !== 'windows' // [Windows]
48
- ? null
49
- : NativeModalManager,
50
- )
44
+ ? new NativeEventEmitter<ModalEventDefinitions>(NativeModalManager)
51
45
  : null;
52
46
 
53
47
  /**
@@ -2,6 +2,9 @@
2
2
  #include "pch.h"
3
3
  #include "CompositionContextHelper.h"
4
4
  #include <algorithm>
5
+ #include <cassert>
6
+ #include <exception>
7
+ #include <vector>
5
8
  #if __has_include("Composition.Experimental.SystemCompositionContextHelper.g.cpp")
6
9
  #include "Composition.Experimental.SystemCompositionContextHelper.g.cpp"
7
10
  #endif
@@ -431,30 +434,43 @@ struct CompVisualImpl {
431
434
  void InsertAt(
432
435
  const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual,
433
436
  uint32_t index) noexcept {
437
+ if (index > m_childrenCache.size()) {
438
+ std::terminate();
439
+ }
434
440
  auto containerChildren = InnerVisual().as<typename TTypeRedirects::ContainerVisual>().Children();
435
441
  auto compVisual = TTypeRedirects::CompositionContextHelper::InnerVisual(visual);
436
442
  if (index == 0) {
437
443
  containerChildren.InsertAtBottom(compVisual);
438
- return;
444
+ } else {
445
+ auto insertAfter = containerChildren.First();
446
+ for (uint32_t i = 1; i < index; i++)
447
+ insertAfter.MoveNext();
448
+ containerChildren.InsertAbove(compVisual, insertAfter.Current());
449
+ }
450
+ if (index >= m_childrenCache.size()) {
451
+ m_childrenCache.push_back(visual);
452
+ } else {
453
+ m_childrenCache.insert(m_childrenCache.begin() + index, visual);
439
454
  }
440
- auto insertAfter = containerChildren.First();
441
- for (uint32_t i = 1; i < index; i++)
442
- insertAfter.MoveNext();
443
- containerChildren.InsertAbove(compVisual, insertAfter.Current());
444
455
  }
445
456
 
446
457
  void Remove(const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual) noexcept {
447
458
  auto compVisual = TTypeRedirects::CompositionContextHelper::InnerVisual(visual);
448
459
  auto containerChildren = InnerVisual().as<typename TTypeRedirects::ContainerVisual>().Children();
449
460
  containerChildren.Remove(compVisual);
461
+ auto it = std::find_if(
462
+ m_childrenCache.begin(), m_childrenCache.end(), [&visual](const auto &cached) { return cached == visual; });
463
+ if (it != m_childrenCache.end()) {
464
+ m_childrenCache.erase(it);
465
+ }
450
466
  }
451
467
 
452
468
  winrt::Microsoft::ReactNative::Composition::Experimental::IVisual GetAt(uint32_t index) noexcept {
453
- auto containerChildren = m_visual.as<typename TTypeRedirects::ContainerVisual>().Children();
454
- auto it = containerChildren.First();
455
- for (uint32_t i = 0; i < index; i++)
456
- it.MoveNext();
457
- return TTypeRedirects::CompositionContextHelper::CreateVisual(it.Current());
469
+ if (index < m_childrenCache.size()) {
470
+ return m_childrenCache[index];
471
+ }
472
+ assert(false && "GetAt called with out-of-range index");
473
+ return nullptr;
458
474
  }
459
475
 
460
476
  void SetClippingPath(ID2D1Geometry *clippingPath) noexcept {
@@ -534,6 +550,7 @@ struct CompVisualImpl {
534
550
 
535
551
  protected:
536
552
  TVisual m_visual;
553
+ std::vector<winrt::Microsoft::ReactNative::Composition::Experimental::IVisual> m_childrenCache;
537
554
  };
538
555
 
539
556
  template <typename TTypeRedirects>
@@ -848,30 +865,43 @@ struct CompScrollerVisual : winrt::implements<
848
865
  void InsertAt(
849
866
  const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual,
850
867
  uint32_t index) noexcept {
868
+ if (index > m_childrenCache.size()) {
869
+ std::terminate();
870
+ }
851
871
  auto containerChildren = m_contentVisual.Children();
852
872
  auto compVisual = TTypeRedirects::CompositionContextHelper::InnerVisual(visual);
853
873
  if (index == 0) {
854
874
  containerChildren.InsertAtBottom(compVisual);
855
- return;
875
+ } else {
876
+ auto insertAfter = containerChildren.First();
877
+ for (uint32_t i = 1; i < index; i++)
878
+ insertAfter.MoveNext();
879
+ containerChildren.InsertAbove(compVisual, insertAfter.Current());
880
+ }
881
+ if (index >= m_childrenCache.size()) {
882
+ m_childrenCache.push_back(visual);
883
+ } else {
884
+ m_childrenCache.insert(m_childrenCache.begin() + index, visual);
856
885
  }
857
- auto insertAfter = containerChildren.First();
858
- for (uint32_t i = 1; i < index; i++)
859
- insertAfter.MoveNext();
860
- containerChildren.InsertAbove(compVisual, insertAfter.Current());
861
886
  }
862
887
 
863
888
  void Remove(const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual) noexcept {
864
889
  auto compVisual = TTypeRedirects::CompositionContextHelper::InnerVisual(visual);
865
890
  auto containerChildren = m_contentVisual.Children();
866
891
  containerChildren.Remove(compVisual);
892
+ auto it = std::find_if(
893
+ m_childrenCache.begin(), m_childrenCache.end(), [&visual](const auto &cached) { return cached == visual; });
894
+ if (it != m_childrenCache.end()) {
895
+ m_childrenCache.erase(it);
896
+ }
867
897
  }
868
898
 
869
899
  winrt::Microsoft::ReactNative::Composition::Experimental::IVisual GetAt(uint32_t index) noexcept {
870
- auto containerChildren = m_visual.as<typename TTypeRedirects::ContainerVisual>().Children();
871
- auto it = containerChildren.First();
872
- for (uint32_t i = 0; i < index; i++)
873
- it.MoveNext();
874
- return TTypeRedirects::CompositionContextHelper::CreateVisual(it.Current());
900
+ if (index < m_childrenCache.size()) {
901
+ return m_childrenCache[index];
902
+ }
903
+ assert(false && "GetAt called with out-of-range index");
904
+ return nullptr;
875
905
  }
876
906
 
877
907
  void Brush(const winrt::Microsoft::ReactNative::Composition::Experimental::IBrush &brush) noexcept {
@@ -1255,6 +1285,12 @@ struct CompScrollerVisual : winrt::implements<
1255
1285
  std::sort(snapPositions.begin(), snapPositions.end());
1256
1286
  snapPositions.erase(std::unique(snapPositions.begin(), snapPositions.end()), snapPositions.end());
1257
1287
 
1288
+ // Skip reconfiguration if snap points haven't changed
1289
+ if (snapPositions == m_previousSnapPositions) {
1290
+ return;
1291
+ }
1292
+ m_previousSnapPositions = snapPositions;
1293
+
1258
1294
  std::vector<typename TTypeRedirects::InteractionTrackerInertiaRestingValue> restingValues;
1259
1295
 
1260
1296
  for (size_t i = 0; i < snapPositions.size(); ++i) {
@@ -1384,6 +1420,7 @@ struct CompScrollerVisual : winrt::implements<
1384
1420
  winrt::Microsoft::ReactNative::Composition::Experimental::SnapPointsAlignment m_snapToAlignment{
1385
1421
  winrt::Microsoft::ReactNative::Composition::Experimental::SnapPointsAlignment::Near};
1386
1422
  std::vector<float> m_snapToOffsets;
1423
+ std::vector<float> m_previousSnapPositions;
1387
1424
  bool m_inertia{false};
1388
1425
  bool m_custom{false};
1389
1426
  bool m_interacting{false};
@@ -1410,6 +1447,7 @@ struct CompScrollerVisual : winrt::implements<
1410
1447
  typename TTypeRedirects::SpriteVisual m_contentVisual{nullptr};
1411
1448
  typename TTypeRedirects::InteractionTracker m_interactionTracker{nullptr};
1412
1449
  typename TTypeRedirects::VisualInteractionSource m_visualInteractionSource{nullptr};
1450
+ std::vector<winrt::Microsoft::ReactNative::Composition::Experimental::IVisual> m_childrenCache;
1413
1451
  };
1414
1452
  using WindowsCompScrollerVisual = CompScrollerVisual<WindowsTypeRedirects>;
1415
1453
  using MicrosoftCompScrollerVisual = CompScrollerVisual<MicrosoftTypeRedirects>;
@@ -250,7 +250,10 @@ void CompositionEventHandler::Initialize() noexcept {
250
250
  if (strongThis->SurfaceId() == -1)
251
251
  return;
252
252
 
253
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
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 focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
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 focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
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 focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
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
- return *winrt::get_self<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>(island)->GetComponentView();
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
- ptScaled += RootComponentView().layoutMetrics().frame.origin;
402
- auto tag = RootComponentView().hitTest(ptScaled, ptLocal);
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 focusedComponent = RootComponentView().GetFocusedComponent();
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 focusedComponent = RootComponentView().GetFocusedComponent();
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().UseKeyboardForProgrammaticFocus(true);
638
+ auto *rootView = RootComponentView();
639
+ if (!rootView)
640
+ return;
641
+ rootView->UseKeyboardForProgrammaticFocus(true);
612
642
 
613
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
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 (RootComponentView().TryMoveFocus(!fShift, winrt::Microsoft::ReactNative::FocusState::Keyboard)) {
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().UseKeyboardForProgrammaticFocus(true);
676
+ auto *rootView = RootComponentView();
677
+ if (!rootView)
678
+ return;
679
+ rootView->UseKeyboardForProgrammaticFocus(true);
647
680
 
648
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
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
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
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 = ptLocal.x;
978
- activeTouch.touch.screenPoint.y = ptLocal.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
- ptScaled += RootComponentView().layoutMetrics().frame.origin;
1068
+ auto *rootView = RootComponentView();
1069
+ if (!rootView)
1070
+ return;
1071
+ ptScaled += rootView->layoutMetrics().frame.origin;
1032
1072
 
1033
- if (std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId) != m_capturedPointers.end()) {
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 = RootComponentView().hitTest(ptScaled, ptLocal);
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::vector<PointerId> capturedPointers = m_capturedPointers;
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
- : RootComponentView().eventEmitterAtPoint(pointerEvent.offsetPoint);
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 children = RootComponentView().Children();
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 =
@@ -1175,23 +1219,31 @@ void CompositionEventHandler::onPointerPressed(
1175
1219
  winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
1176
1220
  namespace Composition = winrt::Microsoft::ReactNative::Composition;
1177
1221
 
1178
- RootComponentView().UseKeyboardForProgrammaticFocus(false);
1222
+ auto *rootView = RootComponentView();
1223
+ if (!rootView)
1224
+ return;
1225
+ rootView->UseKeyboardForProgrammaticFocus(false);
1179
1226
 
1180
1227
  // Clears any active text selection when left pointer is pressed
1181
1228
  if (pointerPoint.Properties().PointerUpdateKind() != Composition::Input::PointerUpdateKind::RightButtonPressed) {
1182
- RootComponentView().ClearCurrentTextSelection();
1229
+ rootView->ClearCurrentTextSelection();
1183
1230
  }
1184
1231
 
1185
1232
  PointerId pointerId = pointerPoint.PointerId();
1186
1233
 
1187
- auto staleTouch = std::find_if(m_activeTouches.begin(), m_activeTouches.end(), [pointerId](const auto &pair) {
1188
- return pair.second.touch.identifier == pointerId;
1189
- });
1234
+ auto staleTouch = m_activeTouches.find(pointerId);
1190
1235
 
1191
1236
  if (staleTouch != m_activeTouches.end()) {
1192
- // A pointer with this ID already exists - Should we fire a button cancel or something?
1193
- // assert(false);
1194
- return;
1237
+ // A previous pointer with this ID was never properly released (e.g., app lost focus,
1238
+ // pointer left window). Cancel the stale touch and clean it up so the new press can proceed.
1239
+ // Copy and erase before dispatching to avoid holding a reference into m_activeTouches
1240
+ // across DispatchSynthesizedTouchCancelForActiveTouch, which calls HandleIncomingPointerEvent
1241
+ // and iterates m_activeTouches internally.
1242
+ ActiveTouch staleTouchCopy = std::move(staleTouch->second);
1243
+ m_activeTouches.erase(staleTouch);
1244
+ if (staleTouchCopy.eventEmitter) {
1245
+ DispatchSynthesizedTouchCancelForActiveTouch(staleTouchCopy, pointerPoint, keyModifiers);
1246
+ }
1195
1247
  }
1196
1248
 
1197
1249
  const auto eventType = TouchEventType::Start;
@@ -1212,7 +1264,18 @@ void CompositionEventHandler::onPointerPressed(
1212
1264
  ->OnPointerPressed(args);
1213
1265
 
1214
1266
  ActiveTouch activeTouch{0};
1215
- activeTouch.touchType = UITouchType::Mouse;
1267
+ switch (pointerPoint.PointerDeviceType()) {
1268
+ case Composition::Input::PointerDeviceType::Touch:
1269
+ activeTouch.touchType = UITouchType::Touch;
1270
+ break;
1271
+ case Composition::Input::PointerDeviceType::Pen:
1272
+ activeTouch.touchType = UITouchType::Pen;
1273
+ break;
1274
+ case Composition::Input::PointerDeviceType::Mouse:
1275
+ default:
1276
+ activeTouch.touchType = UITouchType::Mouse;
1277
+ break;
1278
+ }
1216
1279
 
1217
1280
  // Map PointerUpdateKind to W3C button value
1218
1281
  // https://developer.mozilla.org/docs/Web/API/MouseEvent/button
@@ -1250,6 +1313,12 @@ void CompositionEventHandler::onPointerPressed(
1250
1313
  targetComponentView = targetComponentView.Parent();
1251
1314
  }
1252
1315
 
1316
+ // Don't register the touch if no eventEmitter was found — inserting a null-emitter entry
1317
+ // into m_activeTouches would block future presses with the same pointer ID.
1318
+ if (!activeTouch.eventEmitter) {
1319
+ return;
1320
+ }
1321
+
1253
1322
  UpdateActiveTouch(activeTouch, ptScaled, ptLocal);
1254
1323
 
1255
1324
  activeTouch.isPrimary = pointerId == 1;
@@ -1273,11 +1342,12 @@ void CompositionEventHandler::onPointerReleased(
1273
1342
  winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
1274
1343
  int pointerId = pointerPoint.PointerId();
1275
1344
 
1276
- RootComponentView().UseKeyboardForProgrammaticFocus(false);
1345
+ auto *rootView = RootComponentView();
1346
+ if (!rootView)
1347
+ return;
1348
+ rootView->UseKeyboardForProgrammaticFocus(false);
1277
1349
 
1278
- auto activeTouch = std::find_if(m_activeTouches.begin(), m_activeTouches.end(), [pointerId](const auto &pair) {
1279
- return pair.second.touch.identifier == pointerId;
1280
- });
1350
+ auto activeTouch = m_activeTouches.find(pointerId);
1281
1351
 
1282
1352
  if (activeTouch == m_activeTouches.end()) {
1283
1353
  return;
@@ -1289,8 +1359,13 @@ void CompositionEventHandler::onPointerReleased(
1289
1359
  facebook::react::Point ptLocal, ptScaled;
1290
1360
  getTargetPointerArgs(fabricuiManager, pointerPoint, tag, ptScaled, ptLocal);
1291
1361
 
1292
- if (tag == -1)
1362
+ if (tag == -1) {
1363
+ if (activeTouch->second.eventEmitter) {
1364
+ DispatchSynthesizedTouchCancelForActiveTouch(activeTouch->second, pointerPoint, keyModifiers);
1365
+ }
1366
+ m_activeTouches.erase(pointerId);
1293
1367
  return;
1368
+ }
1294
1369
 
1295
1370
  auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view;
1296
1371
  auto args = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerRoutedEventArgs>(
@@ -1322,7 +1397,7 @@ bool CompositionEventHandler::CapturePointer(
1322
1397
  }
1323
1398
 
1324
1399
  m_pointerCapturingComponentTag = tag;
1325
- m_capturedPointers.push_back(pointer.PointerId());
1400
+ m_capturedPointers.insert(pointer.PointerId());
1326
1401
  return true;
1327
1402
  }
1328
1403
 
@@ -1337,11 +1412,9 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo
1337
1412
  bool result = false;
1338
1413
 
1339
1414
  if (m_pointerCapturingComponentTag == tag) {
1340
- auto it = std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId);
1341
- if (it == m_capturedPointers.end()) {
1415
+ if (m_capturedPointers.erase(pointerId) == 0) {
1342
1416
  return false;
1343
1417
  }
1344
- m_capturedPointers.erase(it);
1345
1418
 
1346
1419
  if (std::shared_ptr<FabricUIManager> fabricuiManager =
1347
1420
  ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) {
@@ -1352,7 +1425,7 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo
1352
1425
  ->OnPointerCaptureLost();
1353
1426
  }
1354
1427
 
1355
- if (m_capturedPointers.size() == 0) {
1428
+ if (m_capturedPointers.empty()) {
1356
1429
  m_pointerCapturingComponentTag = -1;
1357
1430
  return true;
1358
1431
  }
@@ -1467,16 +1540,83 @@ bool CompositionEventHandler::IsPointerWithinInitialTree(const ActiveTouch &acti
1467
1540
  if (!initialComponentView)
1468
1541
  return false;
1469
1542
 
1470
- auto initialViewSet = GetTouchableViewsInPathToRoot(initialComponentView);
1543
+ auto *rootView = RootComponentView();
1544
+ if (!rootView)
1545
+ return false;
1546
+
1547
+ facebook::react::Point ptLocal;
1548
+ auto currentTag = rootView->hitTest(activeTouch.touch.pagePoint, ptLocal);
1549
+ if (currentTag == -1)
1550
+ return false;
1471
1551
 
1472
- for (const auto &view : initialViewSet) {
1473
- if (view.Tag() == activeTouch.touch.target)
1552
+ auto fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties());
1553
+ if (!fabricuiManager)
1554
+ return false;
1555
+
1556
+ auto initialTag = initialComponentView.Tag();
1557
+ auto &viewRegistry = fabricuiManager->GetViewRegistry();
1558
+ auto currentView = viewRegistry.componentViewDescriptorWithTag(currentTag).view;
1559
+ while (currentView) {
1560
+ if (currentView.Tag() == initialTag)
1561
+ return true;
1562
+ currentView = currentView.Parent();
1563
+ }
1564
+
1565
+ // Fallback: if the pointer drifted spatially but the original target
1566
+ // is still structurally within the initial tree, honor the tap.
1567
+ // This provides touch-device tolerance for finger drift.
1568
+ auto targetView = viewRegistry.componentViewDescriptorWithTag(activeTouch.touch.target).view;
1569
+ while (targetView) {
1570
+ if (targetView.Tag() == initialTag)
1474
1571
  return true;
1572
+ targetView = targetView.Parent();
1475
1573
  }
1476
1574
 
1477
1575
  return false;
1478
1576
  }
1479
1577
 
1578
+ void CompositionEventHandler::DispatchSynthesizedTouchCancelForActiveTouch(
1579
+ const ActiveTouch &cancelledTouch,
1580
+ const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
1581
+ winrt::Windows::System::VirtualKeyModifiers keyModifiers) {
1582
+ if (!cancelledTouch.eventEmitter) {
1583
+ return;
1584
+ }
1585
+
1586
+ facebook::react::PointerEvent pointerEvent =
1587
+ CreatePointerEventFromActiveTouch(cancelledTouch, TouchEventType::Cancel);
1588
+ winrt::Microsoft::ReactNative::ComponentView targetView{nullptr};
1589
+ facebook::react::SharedTouchEventEmitter emitter = cancelledTouch.eventEmitter;
1590
+ auto pointerHandler = [emitter, pointerEvent](std::vector<winrt::Microsoft::ReactNative::ComponentView> &) {
1591
+ emitter->onPointerCancel(pointerEvent);
1592
+ };
1593
+ HandleIncomingPointerEvent(pointerEvent, targetView, pointerPoint, keyModifiers, pointerHandler);
1594
+
1595
+ facebook::react::TouchEvent touchEvent;
1596
+ touchEvent.changedTouches.insert(cancelledTouch.touch);
1597
+
1598
+ for (const auto &pair : m_activeTouches) {
1599
+ if (!pair.second.eventEmitter) {
1600
+ continue;
1601
+ }
1602
+
1603
+ if (touchEvent.changedTouches.find(pair.second.touch) != touchEvent.changedTouches.end()) {
1604
+ continue;
1605
+ }
1606
+
1607
+ touchEvent.touches.insert(pair.second.touch);
1608
+ }
1609
+
1610
+ for (const auto &pair : m_activeTouches) {
1611
+ if (pair.second.eventEmitter == cancelledTouch.eventEmitter &&
1612
+ touchEvent.changedTouches.find(pair.second.touch) == touchEvent.changedTouches.end()) {
1613
+ touchEvent.targetTouches.insert(pair.second.touch);
1614
+ }
1615
+ }
1616
+
1617
+ cancelledTouch.eventEmitter->onTouchCancel(touchEvent);
1618
+ }
1619
+
1480
1620
  // If we have events that include multiple pointer updates, we should change arg from pointerId to vector<pointerId>
1481
1621
  void CompositionEventHandler::DispatchTouchEvent(
1482
1622
  TouchEventType eventType,
@@ -1512,16 +1652,19 @@ void CompositionEventHandler::DispatchTouchEvent(
1512
1652
  bool shouldLeave = (eventType == TouchEventType::End && activeTouch.shouldLeaveWhenReleased) ||
1513
1653
  eventType == TouchEventType::Cancel;
1514
1654
  if (!shouldLeave) {
1515
- const auto &viewRegistry = fabricuiManager->GetViewRegistry();
1516
- facebook::react::Point ptLocal;
1517
- auto targetTag = RootComponentView().hitTest(pointerEvent.clientPoint, ptLocal);
1518
- if (targetTag != -1) {
1519
- auto targetComponentViewDescriptor = viewRegistry.componentViewDescriptorWithTag(targetTag);
1520
- targetView = FindClosestFabricManagedTouchableView(targetComponentViewDescriptor.view);
1655
+ auto *rootViewForHit = RootComponentView();
1656
+ if (rootViewForHit) {
1657
+ const auto &viewRegistry = fabricuiManager->GetViewRegistry();
1658
+ facebook::react::Point ptLocal;
1659
+ auto targetTag = rootViewForHit->hitTest(pointerEvent.clientPoint, ptLocal);
1660
+ if (targetTag != -1) {
1661
+ auto targetComponentViewDescriptor = viewRegistry.componentViewDescriptorWithTag(targetTag);
1662
+ targetView = FindClosestFabricManagedTouchableView(targetComponentViewDescriptor.view);
1663
+ }
1521
1664
  }
1522
1665
  }
1523
1666
 
1524
- auto handler = [&activeTouch, eventType, &pointerEvent](
1667
+ auto handler = [this, &activeTouch, eventType, &pointerEvent](
1525
1668
  std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
1526
1669
  switch (eventType) {
1527
1670
  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>
@@ -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
- if (auto index = m_children.Size()) {
1017
- do {
1018
- index--;
1019
- targetTag = winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_children.GetAt(index))
1020
- ->hitTest(ptContent, localPt);
1021
- if (targetTag != -1) {
1022
- return true;
1023
- }
1024
- } while (index != 0);
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;
@@ -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
  }
@@ -18,6 +18,7 @@
18
18
  #include <winrt/Microsoft.UI.Input.h>
19
19
  #include <winrt/Windows.System.h>
20
20
  #include <winrt/Windows.UI.h>
21
+ #include <mutex>
21
22
  #include "../Composition.Input.h"
22
23
  #include "../CompositionHelpers.h"
23
24
  #include "../RootComponentView.h"
@@ -75,37 +76,38 @@ WindowsTextInputComponentView::DrawBlock::~DrawBlock() {
75
76
 
76
77
  // Msftedit.dll vs "Riched20.dll"?
77
78
 
79
+ static std::once_flag g_richEditLoadedFlag;
78
80
  static HINSTANCE g_hInstRichEdit = nullptr;
79
81
  static PCreateTextServices g_pfnCreateTextServices;
80
82
 
81
83
  HRESULT HrEnsureRichEd20Loaded() noexcept {
82
- if (g_hInstRichEdit == nullptr) {
84
+ HRESULT hr = S_OK;
85
+ std::call_once(g_richEditLoadedFlag, [&hr]() {
83
86
  g_hInstRichEdit = LoadLibrary(L"Msftedit.dll");
84
- if (!g_hInstRichEdit)
85
- return E_FAIL;
87
+ if (!g_hInstRichEdit) {
88
+ hr = E_FAIL;
89
+ return;
90
+ }
86
91
 
87
92
  // Create the windowless control (text services object)
88
93
  g_pfnCreateTextServices = (PCreateTextServices)GetProcAddress(g_hInstRichEdit, "CreateTextServices");
89
- if (!g_pfnCreateTextServices)
90
- return E_FAIL;
91
-
92
- /*
93
- // Calling the REExtendedRegisterClass() function is required for
94
- // registering the REComboboxW and REListBoxW window classes.
95
- PFNREGISTER pfnRegister = (PFNREGISTER)GetProcAddress(g_hInstRichEdit, "REExtendedRegisterClass");
96
- if (pfnRegister) {
97
- pfnRegister();
98
- return S_OK;
99
- } else
100
- return E_FAIL;
101
- */
102
- }
103
- return NOERROR;
94
+ if (!g_pfnCreateTextServices) {
95
+ hr = E_FAIL;
96
+ return;
97
+ }
98
+ });
99
+ if (!g_hInstRichEdit || !g_pfnCreateTextServices)
100
+ return E_FAIL;
101
+ return hr;
104
102
  }
105
103
 
106
104
  struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
107
105
  CompTextHost(WindowsTextInputComponentView *outer) : m_outer(outer) {}
108
106
 
107
+ void Detach() {
108
+ m_outer = nullptr;
109
+ }
110
+
109
111
  //@cmember Get the DC for the host
110
112
  HDC TxGetDC() override {
111
113
  assert(false);
@@ -144,6 +146,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
144
146
 
145
147
  //@cmember InvalidateRect
146
148
  void TxInvalidateRect(LPCRECT prc, BOOL fMode) override {
149
+ if (!m_outer)
150
+ return;
147
151
  if (m_outer->m_drawing)
148
152
  return;
149
153
  m_outer->DrawText();
@@ -151,6 +155,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
151
155
 
152
156
  //@cmember Send a WM_PAINT to the window
153
157
  void TxViewChange(BOOL fUpdate) override {
158
+ if (!m_outer)
159
+ return;
154
160
  // When keyboard scrolling without scrollbar, TxInvalidateRect is not called.
155
161
  // Instead TxViewChange is called with fUpdate = true
156
162
  // if (fUpdate && !OnInnerViewerExtentChanged())
@@ -163,12 +169,16 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
163
169
 
164
170
  //@cmember Create the caret
165
171
  BOOL TxCreateCaret(HBITMAP hbmp, INT xWidth, INT yHeight) override {
172
+ if (!m_outer)
173
+ return false;
166
174
  m_outer->m_caretVisual.Size({static_cast<float>(xWidth), static_cast<float>(yHeight)});
167
175
  return true;
168
176
  }
169
177
 
170
178
  //@cmember Show the caret
171
179
  BOOL TxShowCaret(BOOL fShow) override {
180
+ if (!m_outer)
181
+ return false;
172
182
  // Only show the caret if we have focus
173
183
  if (fShow && !m_outer->m_hasFocus) {
174
184
  return false;
@@ -179,6 +189,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
179
189
 
180
190
  //@cmember Set the caret position
181
191
  BOOL TxSetCaretPos(INT x, INT y) override {
192
+ if (!m_outer)
193
+ return false;
182
194
  if (x < 0 && y < 0) {
183
195
  // RichEdit sends (-32000,-32000) when the caret is not currently visible.
184
196
  return false;
@@ -217,6 +229,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
217
229
 
218
230
  //@cmember Get mouse capture
219
231
  void TxSetCapture(BOOL fCapture) override {
232
+ if (!m_outer)
233
+ return;
220
234
  auto mousePointer = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::Pointer>(
221
235
  winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse, 1 /* 1 is Mouse PointerId*/);
222
236
 
@@ -229,6 +243,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
229
243
 
230
244
  //@cmember Set the focus to the text window
231
245
  void TxSetFocus() override {
246
+ if (!m_outer)
247
+ return;
232
248
  winrt::Microsoft::ReactNative::ComponentView view{nullptr};
233
249
  winrt::check_hresult(
234
250
  m_outer->QueryInterface(winrt::guid_of<winrt::Microsoft::ReactNative::ComponentView>(), winrt::put_abi(view)));
@@ -242,11 +258,15 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
242
258
 
243
259
  //@cmember Establish a new cursor shape
244
260
  void TxSetCursor(HCURSOR hcur, BOOL fText) override {
261
+ if (!m_outer)
262
+ return;
245
263
  m_outer->m_hcursor = hcur;
246
264
  }
247
265
 
248
266
  //@cmember Converts screen coordinates of a specified point to the client coordinates
249
267
  BOOL TxScreenToClient(LPPOINT lppt) override {
268
+ if (!m_outer)
269
+ return false;
250
270
  winrt::Windows::Foundation::Point pt{static_cast<float>(lppt->x), static_cast<float>(lppt->y)};
251
271
  pt.X -= m_outer->m_contentOffsetPx.x;
252
272
  pt.Y -= m_outer->m_contentOffsetPx.y;
@@ -258,6 +278,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
258
278
 
259
279
  //@cmember Converts the client coordinates of a specified point to screen coordinates
260
280
  BOOL TxClientToScreen(LPPOINT lppt) override {
281
+ if (!m_outer)
282
+ return false;
261
283
  winrt::Windows::Foundation::Point pt{static_cast<float>(lppt->x), static_cast<float>(lppt->y)};
262
284
 
263
285
  if (!m_outer->m_parent) {
@@ -284,6 +306,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
284
306
 
285
307
  //@cmember Retrieves the coordinates of a window's client area
286
308
  HRESULT TxGetClientRect(LPRECT prc) override {
309
+ if (!m_outer)
310
+ return E_FAIL;
287
311
  *prc = m_outer->getClientRect();
288
312
 
289
313
  prc->top += m_outer->m_contentOffsetPx.y;
@@ -310,6 +334,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
310
334
 
311
335
  //@cmember Get the default character format for the text
312
336
  HRESULT TxGetCharFormat(const CHARFORMATW **ppCF) override {
337
+ if (!m_outer)
338
+ return E_FAIL;
313
339
  m_outer->UpdateCharFormat();
314
340
 
315
341
  *ppCF = &(m_outer->m_cf);
@@ -318,6 +344,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
318
344
 
319
345
  //@cmember Get the default paragraph format for the text
320
346
  HRESULT TxGetParaFormat(const PARAFORMAT **ppPF) override {
347
+ if (!m_outer)
348
+ return E_FAIL;
321
349
  m_outer->UpdateParaFormat();
322
350
 
323
351
  *ppPF = &(m_outer->m_pf);
@@ -326,6 +354,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
326
354
 
327
355
  //@cmember Get the background color for the window
328
356
  COLORREF TxGetSysColor(int nIndex) override {
357
+ if (!m_outer)
358
+ return GetSysColor(nIndex);
329
359
  // if (/* !m_isDisabled || */ nIndex != COLOR_WINDOW && nIndex != COLOR_WINDOWTEXT && nIndex != COLOR_GRAYTEXT) {
330
360
  // This window is either not disabled or the color isn't interesting
331
361
  // in the disabled case.
@@ -400,6 +430,10 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
400
430
 
401
431
  //@cmember Get the maximum length for the text
402
432
  HRESULT TxGetMaxLength(DWORD *plength) override {
433
+ if (!m_outer) {
434
+ *plength = std::numeric_limits<DWORD>::max();
435
+ return S_OK;
436
+ }
403
437
  auto length = m_outer->windowsTextInputProps().maxLength;
404
438
  if (length > static_cast<decltype(m_outer->windowsTextInputProps().maxLength)>(std::numeric_limits<DWORD>::max())) {
405
439
  length = std::numeric_limits<DWORD>::max();
@@ -410,6 +444,10 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
410
444
 
411
445
  //@cmember Get the bits representing requested scroll bars for the window
412
446
  HRESULT TxGetScrollBars(DWORD *pdwScrollBar) override {
447
+ if (!m_outer) {
448
+ *pdwScrollBar = WS_HSCROLL | ES_AUTOHSCROLL;
449
+ return S_OK;
450
+ }
413
451
  if (m_outer->m_multiline) {
414
452
  *pdwScrollBar = WS_VSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | ES_AUTOHSCROLL;
415
453
  } else {
@@ -457,7 +495,8 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
457
495
 
458
496
  //@cmember Notify host of events
459
497
  HRESULT TxNotify(DWORD iNotify, void *pv) override {
460
- // TODO
498
+ if (!m_outer)
499
+ return S_OK;
461
500
 
462
501
  switch (iNotify) {
463
502
  case EN_UPDATE:
@@ -542,6 +581,16 @@ WindowsTextInputComponentView::WindowsTextInputComponentView(
542
581
  reactContext,
543
582
  ComponentViewFeatures::Default & ~ComponentViewFeatures::Background) {}
544
583
 
584
+ WindowsTextInputComponentView::~WindowsTextInputComponentView() {
585
+ // Release text services first to prevent further callbacks into CompTextHost.
586
+ m_textServices = nullptr;
587
+
588
+ // Detach the CompTextHost so any lingering references cannot use a dangling pointer.
589
+ if (m_textHost) {
590
+ winrt::get_self<CompTextHost>(m_textHost)->Detach();
591
+ }
592
+ }
593
+
545
594
  void WindowsTextInputComponentView::HandleCommand(
546
595
  const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept {
547
596
  Super::HandleCommand(args);
@@ -649,17 +698,22 @@ WPARAM PointerRoutedEventArgsToMouseWParam(
649
698
  return wParam;
650
699
  }
651
700
 
652
- bool WindowsTextInputComponentView::IsDoubleClick() {
701
+ bool WindowsTextInputComponentView::IsDoubleClick(uint32_t pointerId) {
653
702
  using namespace std::chrono;
654
703
 
655
704
  auto now = steady_clock::now();
656
- auto duration = duration_cast<milliseconds>(now - m_lastClickTime).count();
705
+ auto it = m_lastClickTimeByPointer.find(pointerId);
706
+ bool isDouble = false;
657
707
 
658
- const int DOUBLE_CLICK_TIME_MS = ::GetDoubleClickTime();
659
-
660
- m_lastClickTime = now;
708
+ if (it != m_lastClickTimeByPointer.end()) {
709
+ auto duration = duration_cast<milliseconds>(now - it->second).count();
710
+ isDouble = (duration < ::GetDoubleClickTime());
711
+ it->second = now;
712
+ } else {
713
+ m_lastClickTimeByPointer[pointerId] = now;
714
+ }
661
715
 
662
- return (duration < DOUBLE_CLICK_TIME_MS);
716
+ return isDouble;
663
717
  }
664
718
 
665
719
  void WindowsTextInputComponentView::OnPointerPressed(
@@ -678,7 +732,7 @@ void WindowsTextInputComponentView::OnPointerPressed(
678
732
  if (pp.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse) {
679
733
  switch (pp.Properties().PointerUpdateKind()) {
680
734
  case winrt::Microsoft::ReactNative::Composition::Input::PointerUpdateKind::LeftButtonPressed:
681
- if (IsDoubleClick()) {
735
+ if (IsDoubleClick(pp.PointerId())) {
682
736
  msg = WM_LBUTTONDBLCLK;
683
737
  } else {
684
738
  msg = WM_LBUTTONDOWN;
@@ -699,10 +753,14 @@ void WindowsTextInputComponentView::OnPointerPressed(
699
753
  wParam |= (XBUTTON2 << 16);
700
754
  break;
701
755
  }
702
- wParam = PointerRoutedEventArgsToMouseWParam(args);
756
+ wParam |= PointerRoutedEventArgsToMouseWParam(args);
703
757
  } else {
704
- msg = WM_POINTERDOWN;
705
- wParam = PointerPointToPointerWParam(pp);
758
+ if (IsDoubleClick(pp.PointerId())) {
759
+ msg = WM_LBUTTONDBLCLK;
760
+ } else {
761
+ msg = WM_LBUTTONDOWN;
762
+ }
763
+ wParam = PointerRoutedEventArgsToMouseWParam(args);
706
764
  }
707
765
 
708
766
  if (m_textServices && msg) {
@@ -764,10 +822,10 @@ void WindowsTextInputComponentView::OnPointerReleased(
764
822
  wParam |= (XBUTTON2 << 16);
765
823
  break;
766
824
  }
767
- wParam = PointerRoutedEventArgsToMouseWParam(args);
825
+ wParam |= PointerRoutedEventArgsToMouseWParam(args);
768
826
  } else {
769
- msg = WM_POINTERUP;
770
- wParam = PointerPointToPointerWParam(pp);
827
+ msg = WM_LBUTTONUP;
828
+ wParam = PointerRoutedEventArgsToMouseWParam(args);
771
829
  }
772
830
 
773
831
  if (m_textServices && msg) {
@@ -817,23 +875,18 @@ void WindowsTextInputComponentView::OnPointerMoved(
817
875
  static_cast<LONG>(position.Y * m_layoutMetrics.pointScaleFactor)};
818
876
  lParam = static_cast<LPARAM>(POINTTOPOINTS(ptContainer));
819
877
 
820
- if (pp.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse) {
821
- msg = WM_MOUSEMOVE;
822
- wParam = PointerRoutedEventArgsToMouseWParam(args);
823
- } else {
824
- msg = WM_POINTERUPDATE;
825
- wParam = PointerPointToPointerWParam(pp);
826
- }
878
+ msg = WM_MOUSEMOVE;
879
+ wParam = PointerRoutedEventArgsToMouseWParam(args);
827
880
 
828
881
  if (m_textServices) {
829
882
  LRESULT lresult;
830
883
  DrawBlock db(*this);
831
884
  auto hr = m_textServices->TxSendMessage(msg, static_cast<WPARAM>(wParam), static_cast<LPARAM>(lParam), &lresult);
832
885
  args.Handled(hr != S_FALSE);
833
- }
834
886
 
835
- m_textServices->OnTxSetCursor(
836
- DVASPECT_CONTENT, -1, nullptr, nullptr, nullptr, nullptr, nullptr, ptContainer.x, ptContainer.y);
887
+ m_textServices->OnTxSetCursor(
888
+ DVASPECT_CONTENT, -1, nullptr, nullptr, nullptr, nullptr, nullptr, ptContainer.x, ptContainer.y);
889
+ }
837
890
  }
838
891
 
839
892
  void WindowsTextInputComponentView::OnPointerWheelChanged(
@@ -935,7 +988,7 @@ bool WindowsTextInputComponentView::ShouldSubmit(
935
988
  bool ctrlDown = (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) &
936
989
  winrt::Microsoft::UI::Input::VirtualKeyStates::Down) ==
937
990
  winrt::Microsoft::UI::Input::VirtualKeyStates::Down;
938
- bool altDown = (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) &
991
+ bool altDown = (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Menu) &
939
992
  winrt::Microsoft::UI::Input::VirtualKeyStates::Down) ==
940
993
  winrt::Microsoft::UI::Input::VirtualKeyStates::Down;
941
994
  bool metaDown = (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::LeftWindows) &
@@ -946,7 +999,7 @@ bool WindowsTextInputComponentView::ShouldSubmit(
946
999
  winrt::Microsoft::UI::Input::VirtualKeyStates::Down;
947
1000
  return (submitKeyEvent.shiftKey && shiftDown) || (submitKeyEvent.ctrlKey && ctrlDown) ||
948
1001
  (submitKeyEvent.altKey && altDown) || (submitKeyEvent.metaKey && metaDown) ||
949
- (!submitKeyEvent.shiftKey && !submitKeyEvent.altKey && !submitKeyEvent.metaKey && !submitKeyEvent.altKey &&
1002
+ (!submitKeyEvent.shiftKey && !submitKeyEvent.ctrlKey && !submitKeyEvent.altKey && !submitKeyEvent.metaKey &&
950
1003
  !shiftDown && !ctrlDown && !altDown && !metaDown);
951
1004
  } else {
952
1005
  shouldSubmit = false;
@@ -1836,6 +1889,7 @@ WindowsTextInputComponentView::createVisual() noexcept {
1836
1889
  winrt::com_ptr<::IUnknown> spUnk;
1837
1890
  winrt::check_hresult(g_pfnCreateTextServices(nullptr, m_textHost.get(), spUnk.put()));
1838
1891
  spUnk.as(m_textServices);
1892
+ winrt::check_bool(m_textServices != nullptr);
1839
1893
 
1840
1894
  LRESULT res;
1841
1895
  winrt::check_hresult(m_textServices->TxSendMessage(EM_SETTEXTMODE, TM_PLAINTEXT, 0, &res));
@@ -11,6 +11,7 @@
11
11
  #include <textserv.h>
12
12
  #include <windows.ui.composition.interop.h>
13
13
  #include <winrt/Windows.UI.Composition.h>
14
+ #include <unordered_map>
14
15
  #include "../ComponentView.h"
15
16
  #include "../CompositionHelpers.h"
16
17
  #include "../CompositionViewComponentView.h"
@@ -74,12 +75,13 @@ struct WindowsTextInputComponentView
74
75
  std::optional<std::string> getAccessiblityValue() noexcept override;
75
76
  void setAcccessiblityValue(std::string &&value) noexcept override;
76
77
  bool getAcccessiblityIsReadOnly() noexcept override;
77
- bool IsDoubleClick();
78
+ bool IsDoubleClick(uint32_t pointerId);
78
79
 
79
80
  WindowsTextInputComponentView(
80
81
  const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
81
82
  facebook::react::Tag tag,
82
83
  winrt::Microsoft::ReactNative::ReactContext const &reactContext);
84
+ ~WindowsTextInputComponentView();
83
85
 
84
86
  winrt::Microsoft::ReactNative::Composition::Experimental::IVisual createVisual() noexcept;
85
87
 
@@ -152,7 +154,7 @@ struct WindowsTextInputComponentView
152
154
  DWORD m_propBits{0};
153
155
  HCURSOR m_hcursor{nullptr};
154
156
  POINT m_caretPosition{0, 0};
155
- std::chrono::steady_clock::time_point m_lastClickTime{};
157
+ std::unordered_map<uint32_t, std::chrono::steady_clock::time_point> m_lastClickTimeByPointer;
156
158
  std::vector<facebook::react::CompWindowsTextInputSubmitKeyEventsStruct> m_submitKeyEvents;
157
159
  };
158
160
 
@@ -46,12 +46,12 @@ WindowsComponentDescriptorRegistry::WindowsComponentDescriptorRegistry()
46
46
  }
47
47
 
48
48
  void WindowsComponentDescriptorRegistry::add(const facebook::react::ComponentDescriptorProvider &provider) noexcept {
49
- m_componentNames.push_back(provider.name);
49
+ m_componentNames.insert(provider.name);
50
50
  m_componentDescriptorRegistry->add(provider);
51
51
  }
52
52
 
53
53
  bool WindowsComponentDescriptorRegistry::hasComponentProvider(const std::string &name) noexcept {
54
- return std::find(m_componentNames.begin(), m_componentNames.end(), name) != m_componentNames.end();
54
+ return m_componentNames.count(name) != 0;
55
55
  }
56
56
 
57
57
  bool WindowsComponentDescriptorRegistry::isXamlSupportRequired() const noexcept {
@@ -68,7 +68,7 @@ void WindowsComponentDescriptorRegistry::Add(
68
68
  auto builder = winrt::make<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>();
69
69
  provider(builder);
70
70
 
71
- m_componentNames.push_back(winrt::to_string(componentName));
71
+ m_componentNames.insert(winrt::to_string(componentName));
72
72
  m_descriptorFlavors.emplace_back(std::make_shared<std::string>(winrt::to_string(componentName)));
73
73
  auto handle = reinterpret_cast<facebook::react::ComponentHandle>(
74
74
  facebook::react::ComponentName(m_descriptorFlavors.back()->c_str()));
@@ -10,6 +10,8 @@
10
10
  #include <ReactPropertyBag.h>
11
11
  #include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
12
12
 
13
+ #include <unordered_set>
14
+
13
15
  namespace Microsoft::ReactNative {
14
16
 
15
17
  struct WindowsComponentDescriptorRegistry {
@@ -41,7 +43,7 @@ struct WindowsComponentDescriptorRegistry {
41
43
  void add(const facebook::react::ComponentDescriptorProvider &provider) noexcept;
42
44
 
43
45
  std::vector<std::shared_ptr<const std::string>> m_descriptorFlavors;
44
- std::vector<std::string> m_componentNames;
46
+ std::unordered_set<std::string> m_componentNames;
45
47
  std::shared_ptr<facebook::react::ComponentDescriptorProviderRegistry> m_componentDescriptorRegistry;
46
48
 
47
49
  std::map<std::shared_ptr<const std::string>, winrt::Microsoft::ReactNative::IReactViewComponentBuilder const>
@@ -72,7 +72,6 @@ wicBitmapSourceFromStream(const winrt::Windows::Storage::Streams::IRandomAccessS
72
72
  return {decodedFrame, imagingFactory, nullptr};
73
73
  } catch (winrt::hresult_error const &ex) {
74
74
  auto errorInfo = std::make_shared<facebook::react::ImageErrorInfo>();
75
- errorInfo = std::make_shared<facebook::react::ImageErrorInfo>();
76
75
  errorInfo->error = ::Microsoft::ReactNative::FormatHResultError(winrt::hresult_error(ex));
77
76
  return {nullptr, nullptr, errorInfo};
78
77
  }
@@ -22,19 +22,19 @@ int64_t AnimatedNode::Tag() {
22
22
  }
23
23
 
24
24
  void AnimatedNode::AddChild(const int64_t animatedNodeTag) {
25
- m_children.push_back(animatedNodeTag);
25
+ m_children.insert(animatedNodeTag);
26
26
  GetChildNode(animatedNodeTag)->OnAttachToNode(m_tag);
27
27
  }
28
28
 
29
29
  void AnimatedNode::RemoveChild(const int64_t tag) {
30
30
  if (const auto childNode = GetChildNode(tag)) {
31
31
  childNode->OnDetachedFromNode(m_tag);
32
- m_children.erase(std::find(m_children.begin(), m_children.end(), tag));
32
+ m_children.erase(tag);
33
33
  }
34
34
  }
35
35
 
36
36
  AnimatedNode *AnimatedNode::GetChildNode(int64_t tag) {
37
- if (std::find(m_children.begin(), m_children.end(), tag) != m_children.end()) {
37
+ if (m_children.count(tag)) {
38
38
  if (const auto manager = m_manager.lock()) {
39
39
  return manager->GetAnimatedNode(tag);
40
40
  }
@@ -6,6 +6,7 @@
6
6
  #include <JSValue.h>
7
7
  #include <cstdint>
8
8
  #include <memory>
9
+ #include <unordered_set>
9
10
  #include <vector>
10
11
 
11
12
  namespace Microsoft::ReactNative {
@@ -20,7 +21,7 @@ class AnimatedNode {
20
21
  void AddChild(int64_t animatedNode);
21
22
  void RemoveChild(int64_t animatedNode);
22
23
 
23
- std::vector<int64_t> &Children() {
24
+ std::unordered_set<int64_t> &Children() {
24
25
  return m_children;
25
26
  }
26
27
 
@@ -37,7 +38,7 @@ class AnimatedNode {
37
38
  AnimatedNode *GetChildNode(int64_t tag);
38
39
  int64_t m_tag{0};
39
40
  const std::weak_ptr<NativeAnimatedNodeManager> m_manager;
40
- std::vector<int64_t> m_children{};
41
+ std::unordered_set<int64_t> m_children{};
41
42
  bool m_useComposition{false};
42
43
 
43
44
  bool HasCompatibleAnimationDriver(int64_t tag);
@@ -9,6 +9,7 @@
9
9
  #include <ReactCoreInjection.h>
10
10
  #include <react/runtime/PlatformTimerRegistry.h>
11
11
  #include <react/runtime/TimerManager.h>
12
+ #include <atomic>
12
13
 
13
14
  namespace Microsoft::ReactNative {
14
15
 
@@ -114,7 +115,7 @@ struct Timing : public std::enable_shared_from_this<Timing> {
114
115
  xaml::Media::CompositionTarget::Rendering_revoker m_rendering;
115
116
  winrt::Microsoft::ReactNative::ITimer m_dispatcherQueueTimer{nullptr};
116
117
  winrt::weak_ref<winrt::Microsoft::ReactNative::IReactDispatcher> m_uiDispatcher;
117
- bool m_usingRendering{false};
118
+ std::atomic<bool> m_usingRendering{false};
118
119
  bool m_usePostForRendering{false};
119
120
  };
120
121
 
@@ -10,11 +10,11 @@
10
10
  -->
11
11
  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
12
12
  <PropertyGroup>
13
- <ReactNativeWindowsVersion>0.84.0-preview.5</ReactNativeWindowsVersion>
13
+ <ReactNativeWindowsVersion>0.84.0-preview.6</ReactNativeWindowsVersion>
14
14
  <ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
15
15
  <ReactNativeWindowsMinor>84</ReactNativeWindowsMinor>
16
16
  <ReactNativeWindowsPatch>0</ReactNativeWindowsPatch>
17
17
  <ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
18
- <ReactNativeWindowsCommitId>4065de3a56f0db924a7b8849b5946af2472ee050</ReactNativeWindowsCommitId>
18
+ <ReactNativeWindowsCommitId>91ceaa8418a39db986927aaaff0cdd07dbf9ba2e</ReactNativeWindowsCommitId>
19
19
  </PropertyGroup>
20
20
  </Project>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-windows",
3
- "version": "0.84.0-preview.5",
3
+ "version": "0.84.0-preview.6",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",