react-native-windows 0.80.0-preview.1 → 0.80.0-preview.10

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 (39) hide show
  1. package/Directory.Build.props +6 -0
  2. package/Folly/TEMP_UntilFollyUpdate/json/json.cpp +1 -1
  3. package/Microsoft.ReactNative/CompositionSwitcher.idl +3 -8
  4. package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +73 -27
  5. package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +21 -156
  6. package/Microsoft.ReactNative/Fabric/Composition/CompositionTextRangeProvider.cpp +30 -9
  7. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +24 -0
  8. package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +22 -0
  9. package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h +3 -0
  10. package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +30 -65
  11. package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h +4 -6
  12. package/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp +11 -0
  13. package/Microsoft.ReactNative/Fabric/Composition/TextDrawing.cpp +37 -5
  14. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +33 -15
  15. package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp +225 -0
  16. package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h +23 -0
  17. package/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp +5 -3
  18. package/Microsoft.ReactNative/Fabric/ImageManager.cpp +1 -1
  19. package/Microsoft.ReactNative/Fabric/platform/react/renderer/graphics/PlatformColorUtils.cpp +64 -0
  20. package/Microsoft.ReactNative/Fabric/platform/react/renderer/graphics/PlatformColorUtils.h +11 -0
  21. package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp +2 -2
  22. package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.h +1 -1
  23. package/Microsoft.ReactNative/Modules/AccessibilityInfoModule.cpp +29 -0
  24. package/Microsoft.ReactNative/Utils/ThemeUtils.cpp +49 -0
  25. package/Microsoft.ReactNative/Utils/ThemeUtils.h +31 -0
  26. package/Microsoft.ReactNative.Managed.CodeGen/Microsoft.ReactNative.Managed.CodeGen.csproj +1 -1
  27. package/Microsoft.ReactNative.Managed.CodeGen/Properties/PublishProfiles/DeployAsTool-Debug.pubxml +1 -1
  28. package/Microsoft.ReactNative.Managed.CodeGen/Properties/PublishProfiles/DeployAsTool-Release.pubxml +1 -1
  29. package/PropertySheets/Generated/PackageVersion.g.props +2 -2
  30. package/PropertySheets/JSEngine.props +1 -1
  31. package/PropertySheets/Warnings.props +45 -0
  32. package/README.md +4 -0
  33. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp +1 -1
  34. package/Scripts/rnw-dependencies.ps1 +18 -5
  35. package/package.json +2 -2
  36. package/templates/cpp-app/template.config.js +2 -1
  37. package/templates/cpp-lib/example/metro.config.js +1 -1
  38. package/templates/old/generateWrapper.js +1 -1
  39. package/templates/old/uwp-cpp-app/template.config.js +1 -1
@@ -19,6 +19,12 @@
19
19
  -->
20
20
  <EnableSourceLink Condition="'$(EnableSourceLink)' == '' AND '$(BuildingInRnwRepo)' == 'true'">true</EnableSourceLink>
21
21
  <EnableSourceLink Condition="'$(EnableSourceLink)' == ''">false</EnableSourceLink>
22
+
23
+ <!-- Symbol Publishing Compliance (Work Item 59264834) - Generate PDB files for Release builds -->
24
+ <DebugSymbols Condition="'$(Configuration)' == 'Release'">true</DebugSymbols>
25
+ <DebugType Condition="'$(Configuration)' == 'Release'">pdbonly</DebugType>
26
+ <IncludeSymbols Condition="'$(Configuration)' == 'Release'">true</IncludeSymbols>
27
+ <IncludeSource Condition="'$(Configuration)' == 'Release'">false</IncludeSource>
22
28
  <!-- When bumping the Folly version, be sure to bump the git hash of that version's commit, find the matching fastfloat dependency and build Folly.vcxproj (to update its cgmanifest.json) too. -->
23
29
  <FollyVersion>2024.10.14.00</FollyVersion>
24
30
  <FastFloatVersion>6.1.4</FastFloatVersion>
@@ -908,7 +908,7 @@ void escapeStringImpl(
908
908
 
909
909
  if (encodeUnicode) {
910
910
  // note that this if condition captures utf8 chars
911
- // with value > 127, so size > 1 byte (or they are whitelisted for
911
+ // with value > 127, so size > 1 byte (or they are allowlisted for
912
912
  // Unicode encoding).
913
913
  // NOTE: char32_t / char16_t are both unsigned.
914
914
  char32_t cp = utf8ToCodePoint(p, e, opts.skip_invalid_utf8);
@@ -31,13 +31,6 @@ namespace Microsoft.ReactNative.Composition.Experimental
31
31
  SwitchThumb,
32
32
  };
33
33
 
34
- enum SnapAlignment
35
- {
36
- Start,
37
- Center,
38
- End,
39
- };
40
-
41
34
  [webhosthidden]
42
35
  [uuid("172def51-9e1a-4e3c-841a-e5a470065acc")] // uuid needed for empty interfaces
43
36
  [version(0)]
@@ -118,6 +111,8 @@ namespace Microsoft.ReactNative.Composition.Experimental
118
111
  event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollPositionChanged;
119
112
  event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollBeginDrag;
120
113
  event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollEndDrag;
114
+ event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollMomentumBegin;
115
+ event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollMomentumEnd;
121
116
  void ContentSize(Windows.Foundation.Numerics.Vector2 size);
122
117
  Windows.Foundation.Numerics.Vector3 ScrollPosition { get; };
123
118
  void ScrollBy(Windows.Foundation.Numerics.Vector3 offset, Boolean animate);
@@ -127,7 +122,7 @@ namespace Microsoft.ReactNative.Composition.Experimental
127
122
  void SetMaximumZoomScale(Single maximumZoomScale);
128
123
  void SetMinimumZoomScale(Single minimumZoomScale);
129
124
  Boolean Horizontal;
130
- void SetSnapPoints(Boolean snapToStart, Boolean snapToEnd, Windows.Foundation.Collections.IVectorView<Single> offsets, SnapAlignment snapToAlignment);
125
+ void SetSnapPoints(Boolean snapToStart, Boolean snapToEnd, Windows.Foundation.Collections.IVectorView<Single> offsets);
131
126
  }
132
127
 
133
128
  [webhosthidden]
@@ -27,8 +27,6 @@
27
27
 
28
28
  namespace Microsoft::ReactNative::Composition::Experimental {
29
29
 
30
- using namespace winrt::Microsoft::ReactNative::Composition::Experimental;
31
-
32
30
  template <typename TSpriteVisual>
33
31
  struct CompositionTypeTraits {};
34
32
 
@@ -711,8 +709,23 @@ struct CompScrollerVisual : winrt::implements<
711
709
  void IdleStateEntered(
712
710
  typename TTypeRedirects::InteractionTracker sender,
713
711
  typename TTypeRedirects::InteractionTrackerIdleStateEnteredArgs args) noexcept {
712
+ // If we were in inertia and are now idle, momentum has ended
713
+ if (m_outer->m_inertia) {
714
+ m_outer->FireScrollMomentumEnd({sender.Position().x, sender.Position().y});
715
+ }
716
+
717
+ // If we were interacting but never entered inertia (Interacting -> Idle),
718
+ // and the interaction was user-driven (requestId == 0), fire end-drag here.
719
+ // Note: if the interactionRequestId was non-zero it was caused by a Try* call
720
+ // (programmatic), so we should not fire onScrollEndDrag.
721
+ if (m_outer->m_interacting && args.RequestId() == 0) {
722
+ m_outer->FireScrollEndDrag({sender.Position().x, sender.Position().y});
723
+ }
724
+
725
+ // Clear state flags
714
726
  m_outer->m_custom = false;
715
727
  m_outer->m_inertia = false;
728
+ m_outer->m_interacting = false;
716
729
  }
717
730
  void InertiaStateEntered(
718
731
  typename TTypeRedirects::InteractionTracker sender,
@@ -720,15 +733,26 @@ struct CompScrollerVisual : winrt::implements<
720
733
  m_outer->m_custom = false;
721
734
  m_outer->m_inertia = true;
722
735
  m_outer->m_currentPosition = args.NaturalRestingPosition();
723
- // When the user stops interacting with the object, tracker can go into two paths:
724
- // 1. tracker goes into idle state immediately
725
- // 2. tracker has just started gliding into Inertia state
726
- // Fire ScrollEndDrag
727
- m_outer->FireScrollEndDrag({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});
736
+
737
+ if (!m_outer->m_interacting && args.RequestId() == 0) {
738
+ m_outer->FireScrollBeginDrag({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});
739
+ }
740
+
741
+ // If interaction was user-driven (requestId == 0),
742
+ // fire ScrollEndDrag here (Interacting -> Inertia caused by user lift).
743
+ if (m_outer->m_interacting && args.RequestId() == 0) {
744
+ m_outer->FireScrollEndDrag({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});
745
+ }
746
+
747
+ // Fire momentum scroll begin when we enter inertia (user or programmatic)
748
+ m_outer->FireScrollMomentumBegin({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});
728
749
  }
729
750
  void InteractingStateEntered(
730
751
  typename TTypeRedirects::InteractionTracker sender,
731
752
  typename TTypeRedirects::InteractionTrackerInteractingStateEnteredArgs args) noexcept {
753
+ // Mark that we're now interacting and remember the requestId (user manipulations => 0)
754
+ m_outer->m_interacting = true;
755
+
732
756
  // Fire when the user starts dragging the object
733
757
  m_outer->FireScrollBeginDrag({sender.Position().x, sender.Position().y});
734
758
  }
@@ -738,6 +762,10 @@ struct CompScrollerVisual : winrt::implements<
738
762
  void ValuesChanged(
739
763
  typename TTypeRedirects::InteractionTracker sender,
740
764
  typename TTypeRedirects::InteractionTrackerValuesChangedArgs args) noexcept {
765
+ if (!m_outer->m_interacting && args.RequestId() == 0) {
766
+ m_outer->FireScrollBeginDrag({args.Position().x, args.Position().y});
767
+ }
768
+ m_outer->m_interacting = true;
741
769
  m_outer->m_currentPosition = args.Position();
742
770
  m_outer->FireScrollPositionChanged({args.Position().x, args.Position().y});
743
771
  }
@@ -873,11 +901,9 @@ struct CompScrollerVisual : winrt::implements<
873
901
  void SetSnapPoints(
874
902
  bool snapToStart,
875
903
  bool snapToEnd,
876
- winrt::Windows::Foundation::Collections::IVectorView<float> const &offsets,
877
- SnapAlignment snapToAlignment) noexcept {
904
+ winrt::Windows::Foundation::Collections::IVectorView<float> const &offsets) noexcept {
878
905
  m_snapToStart = snapToStart;
879
906
  m_snapToEnd = snapToEnd;
880
- m_snapToAlignment = snapToAlignment;
881
907
  m_snapToOffsets.clear();
882
908
  if (offsets) {
883
909
  for (auto const &offset : offsets) {
@@ -985,6 +1011,20 @@ struct CompScrollerVisual : winrt::implements<
985
1011
  return m_scrollEndDragEvent.add(handler);
986
1012
  }
987
1013
 
1014
+ winrt::event_token ScrollMomentumBegin(
1015
+ winrt::Windows::Foundation::EventHandler<
1016
+ winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs> const
1017
+ &handler) noexcept {
1018
+ return m_scrollMomentumBeginEvent.add(handler);
1019
+ }
1020
+
1021
+ winrt::event_token ScrollMomentumEnd(
1022
+ winrt::Windows::Foundation::EventHandler<
1023
+ winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs> const
1024
+ &handler) noexcept {
1025
+ return m_scrollMomentumEndEvent.add(handler);
1026
+ }
1027
+
988
1028
  void ScrollPositionChanged(winrt::event_token const &token) noexcept {
989
1029
  m_scrollPositionChangedEvent.remove(token);
990
1030
  }
@@ -997,6 +1037,14 @@ struct CompScrollerVisual : winrt::implements<
997
1037
  m_scrollEndDragEvent.remove(token);
998
1038
  }
999
1039
 
1040
+ void ScrollMomentumBegin(winrt::event_token const &token) noexcept {
1041
+ m_scrollMomentumBeginEvent.remove(token);
1042
+ }
1043
+
1044
+ void ScrollMomentumEnd(winrt::event_token const &token) noexcept {
1045
+ m_scrollMomentumEndEvent.remove(token);
1046
+ }
1047
+
1000
1048
  void ContentSize(winrt::Windows::Foundation::Numerics::float2 const &size) noexcept {
1001
1049
  m_contentSize = size;
1002
1050
  m_contentVisual.Size(size);
@@ -1075,6 +1123,14 @@ struct CompScrollerVisual : winrt::implements<
1075
1123
  m_scrollEndDragEvent(*this, winrt::make<CompScrollPositionChangedArgs>(position));
1076
1124
  }
1077
1125
 
1126
+ void FireScrollMomentumBegin(winrt::Windows::Foundation::Numerics::float2 position) noexcept {
1127
+ m_scrollMomentumBeginEvent(*this, winrt::make<CompScrollPositionChangedArgs>(position));
1128
+ }
1129
+
1130
+ void FireScrollMomentumEnd(winrt::Windows::Foundation::Numerics::float2 position) noexcept {
1131
+ m_scrollMomentumEndEvent(*this, winrt::make<CompScrollPositionChangedArgs>(position));
1132
+ }
1133
+
1078
1134
  void UpdateMaxPosition() noexcept {
1079
1135
  m_interactionTracker.MaxPosition(
1080
1136
  {std::max<float>(m_contentSize.x - m_visualSize.x, 0),
@@ -1104,22 +1160,6 @@ struct CompScrollerVisual : winrt::implements<
1104
1160
  }
1105
1161
 
1106
1162
  snapPositions.insert(snapPositions.end(), m_snapToOffsets.begin(), m_snapToOffsets.end());
1107
-
1108
- // Adjust snap positions based on alignment
1109
- const float viewportSize = m_horizontal ? visualSize.x : visualSize.y;
1110
- if (m_snapToAlignment == SnapAlignment::Center) {
1111
- // For center alignment, offset snap positions by half the viewport size
1112
- for (auto &position : snapPositions) {
1113
- position = std::max(0.0f, position - viewportSize / 2.0f);
1114
- }
1115
- } else if (m_snapToAlignment == SnapAlignment::End) {
1116
- // For end alignment, offset snap positions by the full viewport size
1117
- for (auto &position : snapPositions) {
1118
- position = std::max(0.0f, position - viewportSize);
1119
- }
1120
- }
1121
- // For Start alignment, no adjustment needed
1122
-
1123
1163
  std::sort(snapPositions.begin(), snapPositions.end());
1124
1164
  snapPositions.erase(std::unique(snapPositions.begin(), snapPositions.end()), snapPositions.end());
1125
1165
 
@@ -1247,9 +1287,9 @@ struct CompScrollerVisual : winrt::implements<
1247
1287
  bool m_snapToStart{true};
1248
1288
  bool m_snapToEnd{true};
1249
1289
  std::vector<float> m_snapToOffsets;
1250
- SnapAlignment m_snapToAlignment{SnapAlignment::Start};
1251
1290
  bool m_inertia{false};
1252
1291
  bool m_custom{false};
1292
+ bool m_interacting{false};
1253
1293
  winrt::Windows::Foundation::Numerics::float3 m_targetPosition;
1254
1294
  winrt::Windows::Foundation::Numerics::float3 m_currentPosition;
1255
1295
  winrt::Windows::Foundation::Numerics::float2 m_contentSize{0};
@@ -1263,6 +1303,12 @@ struct CompScrollerVisual : winrt::implements<
1263
1303
  winrt::event<winrt::Windows::Foundation::EventHandler<
1264
1304
  winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs>>
1265
1305
  m_scrollEndDragEvent;
1306
+ winrt::event<winrt::Windows::Foundation::EventHandler<
1307
+ winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs>>
1308
+ m_scrollMomentumBeginEvent;
1309
+ winrt::event<winrt::Windows::Foundation::EventHandler<
1310
+ winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs>>
1311
+ m_scrollMomentumEndEvent;
1266
1312
  typename TTypeRedirects::SpriteVisual m_visual{nullptr};
1267
1313
  typename TTypeRedirects::SpriteVisual m_contentVisual{nullptr};
1268
1314
  typename TTypeRedirects::InteractionTracker m_interactionTracker{nullptr};
@@ -313,161 +313,6 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTE
313
313
  return S_OK;
314
314
  }
315
315
 
316
- long GetControlTypeFromString(const std::string &role) noexcept {
317
- if (role == "adjustable") {
318
- return UIA_SliderControlTypeId;
319
- } else if (role == "group" || role == "search" || role == "radiogroup" || role == "timer" || role.empty()) {
320
- return UIA_GroupControlTypeId;
321
- } else if (role == "button" || role == "imagebutton" || role == "switch" || role == "togglebutton") {
322
- return UIA_ButtonControlTypeId;
323
- } else if (role == "checkbox") {
324
- return UIA_CheckBoxControlTypeId;
325
- } else if (role == "combobox") {
326
- return UIA_ComboBoxControlTypeId;
327
- } else if (role == "alert" || role == "header" || role == "summary" || role == "text") {
328
- return UIA_TextControlTypeId;
329
- } else if (role == "image") {
330
- return UIA_ImageControlTypeId;
331
- } else if (role == "keyboardkey") {
332
- return UIA_CustomControlTypeId;
333
- } else if (role == "link") {
334
- return UIA_HyperlinkControlTypeId;
335
- }
336
- // list and listitem were added by RNW to better support UIA Control Types
337
- else if (role == "list") {
338
- return UIA_ListControlTypeId;
339
- } else if (role == "listitem") {
340
- return UIA_ListItemControlTypeId;
341
- } else if (role == "menu") {
342
- return UIA_MenuControlTypeId;
343
- } else if (role == "menubar") {
344
- return UIA_MenuBarControlTypeId;
345
- } else if (role == "menuitem") {
346
- return UIA_MenuItemControlTypeId;
347
- }
348
- // If role is "none", remove the element from the control tree
349
- // and expose it as a plain element would in the raw tree.
350
- else if (role == "none") {
351
- return UIA_GroupControlTypeId;
352
- } else if (role == "progressbar") {
353
- return UIA_ProgressBarControlTypeId;
354
- } else if (role == "radio") {
355
- return UIA_RadioButtonControlTypeId;
356
- } else if (role == "scrollbar") {
357
- return UIA_ScrollBarControlTypeId;
358
- } else if (role == "spinbutton") {
359
- return UIA_SpinnerControlTypeId;
360
- } else if (role == "splitbutton") {
361
- return UIA_SplitButtonControlTypeId;
362
- } else if (role == "tab") {
363
- return UIA_TabItemControlTypeId;
364
- } else if (role == "tablist") {
365
- return UIA_TabControlTypeId;
366
- } else if (role == "textinput" || role == "searchbox") {
367
- return UIA_EditControlTypeId;
368
- } else if (role == "toolbar") {
369
- return UIA_ToolBarControlTypeId;
370
- } else if (role == "tree") {
371
- return UIA_TreeControlTypeId;
372
- } else if (role == "treeitem") {
373
- return UIA_TreeItemControlTypeId;
374
- } else if (role == "pane") {
375
- return UIA_PaneControlTypeId;
376
- }
377
- assert(false);
378
- return UIA_GroupControlTypeId;
379
- }
380
-
381
- long GetControlTypeFromRole(const facebook::react::Role &role) noexcept {
382
- switch (role) {
383
- case facebook::react::Role::Alert:
384
- return UIA_TextControlTypeId;
385
- case facebook::react::Role::Application:
386
- return UIA_WindowControlTypeId;
387
- case facebook::react::Role::Button:
388
- return UIA_ButtonControlTypeId;
389
- case facebook::react::Role::Checkbox:
390
- return UIA_CheckBoxControlTypeId;
391
- case facebook::react::Role::Columnheader:
392
- return UIA_HeaderControlTypeId;
393
- case facebook::react::Role::Combobox:
394
- return UIA_ComboBoxControlTypeId;
395
- case facebook::react::Role::Document:
396
- return UIA_DocumentControlTypeId;
397
- case facebook::react::Role::Grid:
398
- return UIA_GroupControlTypeId;
399
- case facebook::react::Role::Group:
400
- return UIA_GroupControlTypeId;
401
- case facebook::react::Role::Heading:
402
- return UIA_TextControlTypeId;
403
- case facebook::react::Role::Img:
404
- return UIA_ImageControlTypeId;
405
- case facebook::react::Role::Link:
406
- return UIA_HyperlinkControlTypeId;
407
- case facebook::react::Role::List:
408
- return UIA_ListControlTypeId;
409
- case facebook::react::Role::Listitem:
410
- return UIA_ListItemControlTypeId;
411
- case facebook::react::Role::Menu:
412
- return UIA_MenuControlTypeId;
413
- case facebook::react::Role::Menubar:
414
- return UIA_MenuBarControlTypeId;
415
- case facebook::react::Role::Menuitem:
416
- return UIA_MenuItemControlTypeId;
417
- case facebook::react::Role::None:
418
- return UIA_GroupControlTypeId;
419
- case facebook::react::Role::Presentation:
420
- return UIA_GroupControlTypeId;
421
- case facebook::react::Role::Progressbar:
422
- return UIA_ProgressBarControlTypeId;
423
- case facebook::react::Role::Radio:
424
- return UIA_RadioButtonControlTypeId;
425
- case facebook::react::Role::Radiogroup:
426
- return UIA_GroupControlTypeId;
427
- case facebook::react::Role::Rowgroup:
428
- return UIA_GroupControlTypeId;
429
- case facebook::react::Role::Rowheader:
430
- return UIA_HeaderControlTypeId;
431
- case facebook::react::Role::Scrollbar:
432
- return UIA_ScrollBarControlTypeId;
433
- case facebook::react::Role::Searchbox:
434
- return UIA_EditControlTypeId;
435
- case facebook::react::Role::Separator:
436
- return UIA_SeparatorControlTypeId;
437
- case facebook::react::Role::Slider:
438
- return UIA_SliderControlTypeId;
439
- case facebook::react::Role::Spinbutton:
440
- return UIA_SpinnerControlTypeId;
441
- case facebook::react::Role::Status:
442
- return UIA_StatusBarControlTypeId;
443
- case facebook::react::Role::Summary:
444
- return UIA_GroupControlTypeId;
445
- case facebook::react::Role::Switch:
446
- return UIA_ButtonControlTypeId;
447
- case facebook::react::Role::Tab:
448
- return UIA_TabItemControlTypeId;
449
- case facebook::react::Role::Table:
450
- return UIA_TableControlTypeId;
451
- case facebook::react::Role::Tablist:
452
- return UIA_TabControlTypeId;
453
- case facebook::react::Role::Tabpanel:
454
- return UIA_TabControlTypeId;
455
- case facebook::react::Role::Timer:
456
- return UIA_ButtonControlTypeId;
457
- case facebook::react::Role::Toolbar:
458
- return UIA_ToolBarControlTypeId;
459
- case facebook::react::Role::Tooltip:
460
- return UIA_ToolTipControlTypeId;
461
- case facebook::react::Role::Tree:
462
- return UIA_TreeControlTypeId;
463
- case facebook::react::Role::Treegrid:
464
- return UIA_TreeControlTypeId;
465
- case facebook::react::Role::Treeitem:
466
- return UIA_TreeItemControlTypeId;
467
- }
468
- return UIA_GroupControlTypeId;
469
- }
470
-
471
316
  HRESULT __stdcall CompositionDynamicAutomationProvider::GetPropertyValue(PROPERTYID propertyId, VARIANT *pRetVal) {
472
317
  if (pRetVal == nullptr)
473
318
  return E_POINTER;
@@ -561,7 +406,18 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPropertyValue(PROPERT
561
406
  }
562
407
  case UIA_IsOffscreenPropertyId: {
563
408
  pRetVal->vt = VT_BOOL;
564
- pRetVal->boolVal = (compositionView->getClipState() == ClipState::FullyClipped) ? VARIANT_TRUE : VARIANT_FALSE;
409
+
410
+ // Check if element is offscreen - consider modal content special case
411
+ bool isOffscreen = (compositionView->getClipState() == ClipState::FullyClipped);
412
+
413
+ // Modal content may appear clipped but is visible in its own window
414
+ if (isOffscreen) {
415
+ if (const auto hwnd = compositionView->GetHwndForParenting()) {
416
+ isOffscreen = !(IsWindowVisible(hwnd) && !IsIconic(hwnd));
417
+ }
418
+ }
419
+
420
+ pRetVal->boolVal = isOffscreen ? VARIANT_TRUE : VARIANT_FALSE;
565
421
  break;
566
422
  }
567
423
  case UIA_HelpTextPropertyId: {
@@ -618,6 +474,11 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPropertyValue(PROPERT
618
474
  pRetVal->bstrVal = SysAllocString(desc.c_str());
619
475
  break;
620
476
  }
477
+ case UIA_HeadingLevelPropertyId: {
478
+ pRetVal->vt = VT_I4;
479
+ pRetVal->lVal = GetHeadingLevel(props->accessibilityLevel, props->accessibilityRole, props->role);
480
+ break;
481
+ }
621
482
  }
622
483
  return hr;
623
484
  }
@@ -1009,7 +870,9 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::Expand() {
1009
870
 
1010
871
  if (!strongView)
1011
872
  return UIA_E_ELEMENTNOTAVAILABLE;
873
+
1012
874
  DispatchAccessibilityAction(m_view, "expand");
875
+
1013
876
  return S_OK;
1014
877
  }
1015
878
 
@@ -1018,7 +881,9 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::Collapse() {
1018
881
 
1019
882
  if (!strongView)
1020
883
  return UIA_E_ELEMENTNOTAVAILABLE;
884
+
1021
885
  DispatchAccessibilityAction(m_view, "collapse");
886
+
1022
887
  return S_OK;
1023
888
  }
1024
889
 
@@ -18,14 +18,30 @@ CompositionTextRangeProvider::CompositionTextRangeProvider(
18
18
  }
19
19
 
20
20
  HRESULT __stdcall CompositionTextRangeProvider::Clone(ITextRangeProvider **pRetVal) {
21
- // no-op
22
- *pRetVal = nullptr;
21
+ if (pRetVal == nullptr)
22
+ return E_POINTER;
23
+
24
+ auto clone = winrt::make<winrt::Microsoft::ReactNative::implementation::CompositionTextRangeProvider>(
25
+ m_view.view().as<winrt::Microsoft::ReactNative::Composition::ComponentView>(), m_parentProvider.get());
26
+ *pRetVal = clone.detach();
23
27
  return S_OK;
24
28
  }
25
29
 
26
30
  HRESULT __stdcall CompositionTextRangeProvider::Compare(ITextRangeProvider *range, BOOL *pRetVal) {
27
- // no-op
28
- *pRetVal = false;
31
+ if (pRetVal == nullptr)
32
+ return E_POINTER;
33
+ if (range == nullptr) {
34
+ *pRetVal = FALSE;
35
+ return S_OK;
36
+ }
37
+
38
+ // Try to cast to our type , considering provider only supports a single range per view
39
+ auto other = dynamic_cast<CompositionTextRangeProvider *>(range);
40
+ if (other && other->m_view.view() == m_view.view()) {
41
+ *pRetVal = TRUE;
42
+ } else {
43
+ *pRetVal = FALSE;
44
+ }
29
45
  return S_OK;
30
46
  }
31
47
 
@@ -34,7 +50,10 @@ HRESULT __stdcall CompositionTextRangeProvider::CompareEndpoints(
34
50
  ITextRangeProvider *targetRange,
35
51
  TextPatternRangeEndpoint targetEndpoint,
36
52
  int *pRetVal) {
37
- // no-op
53
+ if (pRetVal == nullptr)
54
+ return E_POINTER;
55
+
56
+ // For a single-range provider, always equal:
38
57
  *pRetVal = 0;
39
58
  return S_OK;
40
59
  }
@@ -98,13 +117,13 @@ HRESULT __stdcall CompositionTextRangeProvider::GetAttributeValue(TEXTATTRIBUTEI
98
117
  textTransform = props->textAttributes.textTransform.value();
99
118
  }
100
119
  if (fontVariant == facebook::react::FontVariant::SmallCaps) {
101
- return CapStyle_SmallCap;
120
+ pRetVal->lVal = CapStyle_SmallCap;
102
121
  } else if (textTransform == facebook::react::TextTransform::Capitalize) {
103
- return CapStyle_Titling;
122
+ pRetVal->lVal = CapStyle_Titling;
104
123
  } else if (textTransform == facebook::react::TextTransform::Lowercase) {
105
- return CapStyle_None;
124
+ pRetVal->lVal = CapStyle_None;
106
125
  } else if (textTransform == facebook::react::TextTransform::Uppercase) {
107
- return CapStyle_AllCap;
126
+ pRetVal->lVal = CapStyle_AllCap;
108
127
  }
109
128
  } else if (attributeId == UIA_FontNameAttributeId) {
110
129
  pRetVal->vt = VT_BSTR;
@@ -282,6 +301,8 @@ HRESULT __stdcall CompositionTextRangeProvider::ScrollIntoView(BOOL alignToTop)
282
301
  return S_OK;
283
302
  }
284
303
 
304
+ // All the below methods should be implemented once the selection comes for paragraph and TextInput
305
+
285
306
  HRESULT __stdcall CompositionTextRangeProvider::AddToSelection() {
286
307
  // no-op
287
308
  return S_OK;
@@ -831,6 +831,30 @@ void ComponentView::updateAccessibilityProps(
831
831
  oldViewProps.accessibilityValue.text,
832
832
  newViewProps.accessibilityValue.text);
833
833
 
834
+ // Handle annotation properties with single call
835
+ winrt::Microsoft::ReactNative::implementation::UpdateUiaPropertiesForAnnotation(
836
+ EnsureUiaProvider(), oldViewProps.accessibilityAnnotation, newViewProps.accessibilityAnnotation);
837
+
838
+ // Handle expand/collapse state changes
839
+ if (oldViewProps.accessibilityState.has_value() != newViewProps.accessibilityState.has_value() ||
840
+ (oldViewProps.accessibilityState.has_value() && newViewProps.accessibilityState.has_value() &&
841
+ oldViewProps.accessibilityState->expanded != newViewProps.accessibilityState->expanded)) {
842
+ auto oldExpanded =
843
+ oldViewProps.accessibilityState.has_value() && oldViewProps.accessibilityState->expanded.has_value()
844
+ ? oldViewProps.accessibilityState->expanded.value()
845
+ : false;
846
+ auto newExpanded =
847
+ newViewProps.accessibilityState.has_value() && newViewProps.accessibilityState->expanded.has_value()
848
+ ? newViewProps.accessibilityState->expanded.value()
849
+ : false;
850
+
851
+ winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
852
+ EnsureUiaProvider(),
853
+ UIA_ExpandCollapseExpandCollapseStatePropertyId,
854
+ static_cast<int>(winrt::Microsoft::ReactNative::implementation::GetExpandCollapseState(oldExpanded)),
855
+ static_cast<int>(winrt::Microsoft::ReactNative::implementation::GetExpandCollapseState(newExpanded)));
856
+ }
857
+
834
858
  if ((oldViewProps.accessibilityState.has_value() && oldViewProps.accessibilityState->selected.has_value()) !=
835
859
  ((newViewProps.accessibilityState.has_value() && newViewProps.accessibilityState->selected.has_value()))) {
836
860
  auto compProvider =
@@ -40,6 +40,14 @@
40
40
 
41
41
  namespace winrt::Microsoft::ReactNative::implementation {
42
42
 
43
+ ReactPropertyId<winrt::Microsoft::ReactNative::ReactNonAbiValue<
44
+ winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>>>
45
+ ReactNativeIsland::LastFocusedReactNativeIslandProperty() noexcept {
46
+ static const ReactPropertyId<winrt::Microsoft::ReactNative::ReactNonAbiValue<
47
+ winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>>>
48
+ prop{L"ReactNative.Composition", L"ReactNativeIsland"};
49
+ return prop;
50
+ }
43
51
  constexpr float loadingActivitySize = 12.0f;
44
52
  constexpr float loadingActivityHorizontalOffset = 16.0f;
45
53
  constexpr float loadingBarHeight = 36.0f;
@@ -861,6 +869,20 @@ winrt::Microsoft::UI::Content::ContentIsland ReactNativeIsland::Island() {
861
869
  }
862
870
  }
863
871
  });
872
+ focusController.GotFocus(
873
+ [weakThis = get_weak()](const auto &sender, const winrt::Microsoft::UI::Input::FocusChangedEventArgs &args) {
874
+ if (auto pThis = weakThis.get()) {
875
+ // Set the island to React context so it can be accessed by native modules
876
+ if (pThis->m_context && pThis->m_island) {
877
+ auto properties = pThis->m_context.Properties();
878
+ properties.Set(
879
+ ReactNativeIsland::LastFocusedReactNativeIslandProperty(),
880
+ winrt::Microsoft::ReactNative::ReactNonAbiValue<
881
+ winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>>{
882
+ std::in_place, weakThis});
883
+ }
884
+ }
885
+ });
864
886
 
865
887
  // ContentIsland does not support weak_ref, so we cannot use auto_revoke for these events
866
888
  m_islandAutomationProviderRequestedToken = m_island.AutomationProviderRequested(
@@ -49,6 +49,9 @@ struct ReactNativeIsland
49
49
  ~ReactNativeIsland() noexcept;
50
50
 
51
51
  ReactNativeIsland(const winrt::Microsoft::UI::Composition::Compositor &compositor) noexcept;
52
+ static ReactPropertyId<winrt::Microsoft::ReactNative::ReactNonAbiValue<
53
+ winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>>>
54
+ LastFocusedReactNativeIslandProperty() noexcept;
52
55
  ReactNativeIsland(const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portal) noexcept;
53
56
 
54
57
  static winrt::Microsoft::ReactNative::ReactNativeIsland CreatePortal(