react-native-windows 0.74.25 → 0.74.26

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.
@@ -818,6 +818,11 @@ uint8_t BorderPrimitive::numberOfVisuals() const noexcept {
818
818
  return m_numBorderVisuals;
819
819
  }
820
820
 
821
+ void BorderPrimitive::setOuter(
822
+ winrt::Microsoft::ReactNative::Composition::implementation::ComponentView *outer) noexcept {
823
+ m_outer = outer;
824
+ }
825
+
821
826
  bool BorderPrimitive::TryUpdateSpecialBorderLayers(
822
827
  winrt::Microsoft::ReactNative::Composition::implementation::Theme *theme,
823
828
  std::array<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual, SpecialBorderLayerCount>
@@ -29,6 +29,10 @@ struct BorderPrimitive {
29
29
 
30
30
  void markNeedsUpdate() noexcept;
31
31
 
32
+ // We hoist focus visuals up the tree to allow them to be higher in the z-order.
33
+ // This means a single BorderPrimitive may change the owning ComponentView as focus moves around
34
+ void setOuter(winrt::Microsoft::ReactNative::Composition::implementation::ComponentView *outer) noexcept;
35
+
32
36
  void updateProps(
33
37
  const facebook::react::ViewProps &oldViewProps,
34
38
  const facebook::react::ViewProps &newViewProps) noexcept;
@@ -40,7 +40,7 @@ constexpr float FOCUS_VISUAL_WIDTH = 2.0f;
40
40
  // ----- m_visual <-- Background / clip - Can be a custom visual depending on Component type
41
41
  // |
42
42
  // ----- Border Visuals x N (BorderPrimitive attached to m_visual)
43
- // ------Focus Visual Container
43
+ // ------Focus Visual Container (created when hosting focus visuals)
44
44
  // |
45
45
  // |------Inner Focus Visual
46
46
  // |
@@ -57,9 +57,6 @@ ComponentView::ComponentView(
57
57
  : base_type(tag, reactContext), m_compContext(compContext), m_flags(flags) {
58
58
  m_outerVisual = compContext.CreateSpriteVisual(); // TODO could be a raw ContainerVisual if we had a
59
59
  // CreateContainerVisual in ICompositionContext
60
- m_focusVisual = compContext.CreateSpriteVisual(); // TODO could be a raw ContainerVisual if we had a
61
- // CreateContainerVisual in ICompositionContext
62
- m_outerVisual.InsertAt(m_focusVisual, 0);
63
60
  }
64
61
 
65
62
  ComponentView::~ComponentView() {
@@ -90,13 +87,17 @@ void ComponentView::onThemeChanged() noexcept {
90
87
  m_borderPrimitive->onThemeChanged(
91
88
  m_layoutMetrics, BorderPrimitive::resolveAndAlignBorderMetrics(m_layoutMetrics, *viewProps()));
92
89
  }
93
- if (m_focusInnerPrimitive) {
94
- auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
95
- m_focusInnerPrimitive->onThemeChanged(innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
96
- }
97
- if (m_focusOuterPrimitive) {
98
- auto outerFocusMetrics = focusLayoutMetrics(true /*inner*/);
99
- m_focusOuterPrimitive->onThemeChanged(outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
90
+ if (m_componentHostingFocusVisual) {
91
+ if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive) {
92
+ auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
93
+ m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive->onThemeChanged(
94
+ innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
95
+ }
96
+ if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive) {
97
+ auto outerFocusMetrics = focusLayoutMetrics(true /*inner*/);
98
+ m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive->onThemeChanged(
99
+ outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
100
+ }
100
101
  }
101
102
 
102
103
  if ((m_flags & ComponentViewFeatures::ShadowProps) == ComponentViewFeatures::ShadowProps) {
@@ -160,14 +161,18 @@ void ComponentView::updateProps(
160
161
  if (m_borderPrimitive) {
161
162
  m_borderPrimitive->updateProps(oldViewProps, newViewProps);
162
163
  }
163
- if (!newViewProps.enableFocusRing) {
164
- showFocusVisual(false);
165
- }
166
- if (m_focusInnerPrimitive) {
167
- m_focusInnerPrimitive->updateProps(oldViewProps, newViewProps);
168
- }
169
- if (m_focusOuterPrimitive) {
170
- m_focusOuterPrimitive->updateProps(oldViewProps, newViewProps);
164
+
165
+ if (m_componentHostingFocusVisual) {
166
+ if (!newViewProps.enableFocusRing) {
167
+ m_componentHostingFocusVisual->hostFocusVisual(false, get_strong());
168
+ }
169
+
170
+ if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive) {
171
+ m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive->updateProps(oldViewProps, newViewProps);
172
+ }
173
+ if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive) {
174
+ m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive->updateProps(oldViewProps, newViewProps);
175
+ }
171
176
  }
172
177
  if ((m_flags & ComponentViewFeatures::ShadowProps) == ComponentViewFeatures::ShadowProps) {
173
178
  updateShadowProps(oldViewProps, newViewProps);
@@ -200,44 +205,70 @@ void ComponentView::updateLayoutMetrics(
200
205
  });
201
206
  }
202
207
 
203
- updateFocusLayoutMetrics(layoutMetrics);
208
+ base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
204
209
 
205
210
  if (layoutMetrics != oldLayoutMetrics) {
206
211
  if (m_borderPrimitive) {
207
212
  m_borderPrimitive->markNeedsUpdate();
208
213
  }
209
- if (m_focusInnerPrimitive) {
210
- m_focusInnerPrimitive->markNeedsUpdate();
211
- }
212
- if (m_focusOuterPrimitive) {
213
- m_focusOuterPrimitive->markNeedsUpdate();
214
+
215
+ if (m_componentHostingFocusVisual) {
216
+ m_componentHostingFocusVisual->updateFocusLayoutMetrics();
214
217
  }
215
218
  }
216
219
 
217
- base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
218
220
  UpdateCenterPropertySet();
219
221
  }
220
222
 
221
- void ComponentView::updateFocusLayoutMetrics(facebook::react::LayoutMetrics const &layoutMetrics) noexcept {
222
- if (m_focusInnerPrimitive) {
223
- auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
224
- m_focusInnerPrimitive->RootVisual().Size(
225
- {innerFocusMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
226
- innerFocusMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
227
- m_focusInnerPrimitive->RootVisual().Offset(
228
- {-FOCUS_VISUAL_WIDTH * layoutMetrics.pointScaleFactor,
229
- -FOCUS_VISUAL_WIDTH * layoutMetrics.pointScaleFactor,
230
- 0.0f});
231
- }
232
- if (m_focusOuterPrimitive) {
233
- auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
234
- m_focusOuterPrimitive->RootVisual().Size(
235
- {outerFocusMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
236
- outerFocusMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
237
- m_focusOuterPrimitive->RootVisual().Offset(
238
- {-(FOCUS_VISUAL_WIDTH * 2 * m_layoutMetrics.pointScaleFactor),
239
- -(FOCUS_VISUAL_WIDTH * 2 * m_layoutMetrics.pointScaleFactor),
240
- 0.0f});
223
+ void ComponentView::updateFocusLayoutMetrics() noexcept {
224
+ facebook::react::RectangleEdges<bool> nudgeEdges;
225
+ auto scaleFactor = m_focusPrimitive->m_focusVisualComponent->m_layoutMetrics.pointScaleFactor;
226
+ if (m_focusPrimitive) {
227
+ if (m_focusPrimitive->m_focusOuterPrimitive) {
228
+ auto outerFocusMetrics = m_focusPrimitive->m_focusVisualComponent->focusLayoutMetrics(false /*inner*/);
229
+
230
+ if (outerFocusMetrics.frame.origin.x < 0) {
231
+ nudgeEdges.left = true;
232
+ }
233
+ if (outerFocusMetrics.frame.origin.y < 0) {
234
+ nudgeEdges.top = true;
235
+ }
236
+ if (outerFocusMetrics.frame.getMaxX() > m_layoutMetrics.frame.getMaxX()) {
237
+ nudgeEdges.right = true;
238
+ }
239
+ if (outerFocusMetrics.frame.getMaxY() > m_layoutMetrics.frame.getMaxY()) {
240
+ nudgeEdges.bottom = true;
241
+ }
242
+
243
+ m_focusPrimitive->m_focusOuterPrimitive->RootVisual().Size(
244
+ {outerFocusMetrics.frame.size.width * scaleFactor -
245
+ (nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0) -
246
+ (nudgeEdges.right ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0),
247
+ outerFocusMetrics.frame.size.height * scaleFactor -
248
+ (nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0) -
249
+ (nudgeEdges.bottom ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0)});
250
+ m_focusPrimitive->m_focusOuterPrimitive->RootVisual().Offset(
251
+ {nudgeEdges.left ? 0 : -(FOCUS_VISUAL_WIDTH * 2 * scaleFactor),
252
+ nudgeEdges.top ? 0 : -(FOCUS_VISUAL_WIDTH * 2 * scaleFactor),
253
+ 0.0f});
254
+ m_focusPrimitive->m_focusOuterPrimitive->markNeedsUpdate();
255
+ }
256
+
257
+ if (m_focusPrimitive->m_focusInnerPrimitive) {
258
+ auto innerFocusMetrics = m_focusPrimitive->m_focusVisualComponent->focusLayoutMetrics(true /*inner*/);
259
+ m_focusPrimitive->m_focusInnerPrimitive->RootVisual().Size(
260
+ {innerFocusMetrics.frame.size.width * scaleFactor -
261
+ (nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0) -
262
+ (nudgeEdges.right ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0),
263
+ innerFocusMetrics.frame.size.height * scaleFactor -
264
+ (nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0) -
265
+ (nudgeEdges.bottom ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0)});
266
+ m_focusPrimitive->m_focusInnerPrimitive->RootVisual().Offset(
267
+ {nudgeEdges.left ? 0 : -FOCUS_VISUAL_WIDTH * scaleFactor,
268
+ nudgeEdges.top ? 0 : -FOCUS_VISUAL_WIDTH * scaleFactor,
269
+ 0.0f});
270
+ m_focusPrimitive->m_focusInnerPrimitive->markNeedsUpdate();
271
+ }
241
272
  }
242
273
  }
243
274
 
@@ -288,13 +319,17 @@ void ComponentView::FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentView
288
319
  }
289
320
  }
290
321
 
291
- if (m_focusInnerPrimitive) {
292
- auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
293
- m_focusInnerPrimitive->finalize(innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
294
- }
295
- if (m_focusOuterPrimitive) {
296
- auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
297
- m_focusOuterPrimitive->finalize(outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
322
+ if (m_componentHostingFocusVisual) {
323
+ if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive) {
324
+ auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
325
+ m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive->finalize(
326
+ innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
327
+ }
328
+ if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive) {
329
+ auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
330
+ m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive->finalize(
331
+ outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
332
+ }
298
333
  }
299
334
 
300
335
  if (m_FinalizeTransform) {
@@ -308,7 +343,12 @@ void ComponentView::onLostFocus(
308
343
  const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
309
344
  if (args.OriginalSource() == Tag()) {
310
345
  m_eventEmitter->onBlur();
311
- showFocusVisual(false);
346
+
347
+ if (m_componentHostingFocusVisual) {
348
+ auto s = get_strong();
349
+
350
+ m_componentHostingFocusVisual->hostFocusVisual(false, get_strong());
351
+ }
312
352
  if (m_uiaProvider) {
313
353
  winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
314
354
  m_uiaProvider, UIA_HasKeyboardFocusPropertyId, true, false);
@@ -317,12 +357,47 @@ void ComponentView::onLostFocus(
317
357
  base_type::onLostFocus(args);
318
358
  }
319
359
 
360
+ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ComponentView::visualToHostFocus() noexcept {
361
+ return OuterVisual();
362
+ }
363
+
364
+ // We want to host focus visuals as close to the focused component as possible. However since the focus visuals extend
365
+ // past the bounds of the component, in cases where additional components are positioned directly next to this one, we'd
366
+ // get zorder issues causing most of the focus rect to be obscured. So we go up the tree until we find a component who's
367
+ // bounds will fix the entire focus rect.
368
+ winrt::com_ptr<ComponentView> ComponentView::focusVisualRoot(const facebook::react::Rect &focusRect) noexcept {
369
+ auto compVisual =
370
+ winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(OuterVisual());
371
+ if (!compVisual) {
372
+ return get_strong();
373
+ // When not using lifted composition, force the focus visual to host within its own component, as we do not support
374
+ // ParentForTransform
375
+ }
376
+
377
+ if (facebook::react::Rect::intersect(focusRect, m_layoutMetrics.frame) == focusRect) {
378
+ return get_strong();
379
+ }
380
+
381
+ if (!m_parent) {
382
+ return get_strong();
383
+ }
384
+
385
+ return m_parent.as<ComponentView>()->focusVisualRoot(
386
+ {{focusRect.origin.x + m_layoutMetrics.frame.origin.x, focusRect.origin.y + m_layoutMetrics.frame.origin.y},
387
+ focusRect.size});
388
+ }
389
+
320
390
  void ComponentView::onGotFocus(
321
391
  const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
322
392
  if (args.OriginalSource() == Tag()) {
323
393
  m_eventEmitter->onFocus();
324
394
  if (viewProps()->enableFocusRing) {
325
- showFocusVisual(true);
395
+ facebook::react::Rect focusRect = m_layoutMetrics.frame;
396
+ focusRect.origin.x -= (FOCUS_VISUAL_WIDTH * 2);
397
+ focusRect.origin.y -= (FOCUS_VISUAL_WIDTH * 2);
398
+ focusRect.size.width += (FOCUS_VISUAL_WIDTH * 2);
399
+ focusRect.size.height += (FOCUS_VISUAL_WIDTH * 2);
400
+ focusVisualRoot(focusRect)->hostFocusVisual(true, get_strong());
326
401
  }
327
402
  if (m_uiaProvider) {
328
403
  auto spProviderSimple = m_uiaProvider.try_as<IRawElementProviderSimple>();
@@ -493,39 +568,70 @@ facebook::react::BorderMetrics ComponentView::focusBorderMetrics(
493
568
  return metrics;
494
569
  }
495
570
 
496
- void ComponentView::showFocusVisual(bool show) noexcept {
497
- if ((m_flags & ComponentViewFeatures::FocusVisual) == ComponentViewFeatures::FocusVisual) {
498
- if (show && !m_showingFocusVisual) {
499
- m_showingFocusVisual = true;
500
-
501
- m_focusVisual.IsVisible(true);
502
- assert(viewProps()->enableFocusRing);
503
- if (!m_focusInnerPrimitive) {
504
- m_focusInnerPrimitive = std::make_shared<BorderPrimitive>(*this);
505
- m_focusVisual.InsertAt(m_focusInnerPrimitive->RootVisual(), 0);
571
+ void ComponentView::hostFocusVisual(bool show, winrt::com_ptr<ComponentView> view) noexcept {
572
+ if ((view->m_flags & ComponentViewFeatures::FocusVisual) == ComponentViewFeatures::FocusVisual) {
573
+ // Any previous view showing focus visuals should have removed them before another shows it
574
+ assert(
575
+ !m_focusPrimitive || !m_focusPrimitive->m_focusVisualComponent ||
576
+ m_focusPrimitive->m_focusVisualComponent == view);
577
+ assert(
578
+ !m_focusPrimitive || !m_focusPrimitive->m_focusVisualComponent ||
579
+ view->m_componentHostingFocusVisual.get() == this);
580
+ if (show && !view->m_componentHostingFocusVisual) {
581
+ view->m_componentHostingFocusVisual = get_strong();
582
+
583
+ if (!m_focusPrimitive) {
584
+ m_focusPrimitive = std::make_unique<FocusPrimitive>();
506
585
  }
507
- if (!m_focusOuterPrimitive) {
508
- m_focusOuterPrimitive = std::make_shared<BorderPrimitive>(*this);
509
- m_focusVisual.InsertAt(m_focusOuterPrimitive->RootVisual(), 0);
586
+ m_focusPrimitive->m_focusVisualComponent = view;
587
+
588
+ if (!m_focusPrimitive->m_focusVisual) {
589
+ m_focusPrimitive->m_focusVisual = m_compContext.CreateSpriteVisual();
590
+ auto hostingVisual =
591
+ winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(
592
+ visualToHostFocus())
593
+ .as<winrt::Microsoft::UI::Composition::ContainerVisual>();
594
+ if (hostingVisual) {
595
+ hostingVisual.Children().InsertAtTop(
596
+ winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(
597
+ m_focusPrimitive->m_focusVisual));
598
+ } else {
599
+ assert(
600
+ view.get() ==
601
+ this); // When not using lifted comp, focus visuals should always host within their own component
602
+ OuterVisual().InsertAt(m_focusPrimitive->m_focusVisual, 1);
603
+ }
510
604
  }
511
- updateFocusLayoutMetrics(m_layoutMetrics);
512
- auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
513
- m_focusInnerPrimitive->finalize(innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
514
- auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
515
- m_focusOuterPrimitive->finalize(outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
516
- } else if (!show && m_showingFocusVisual) {
517
- m_showingFocusVisual = false;
518
- m_focusVisual.IsVisible(false);
519
- if (m_focusInnerPrimitive) {
520
- m_focusInnerPrimitive->markNeedsUpdate();
521
- auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
522
- m_focusInnerPrimitive->finalize(innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
605
+
606
+ m_focusPrimitive->m_focusVisual.IsVisible(true);
607
+ assert(view->viewProps()->enableFocusRing);
608
+ if (!m_focusPrimitive->m_focusInnerPrimitive) {
609
+ m_focusPrimitive->m_focusInnerPrimitive = std::make_shared<BorderPrimitive>(*this);
610
+ m_focusPrimitive->m_focusVisual.InsertAt(m_focusPrimitive->m_focusInnerPrimitive->RootVisual(), 0);
611
+ }
612
+ if (!m_focusPrimitive->m_focusOuterPrimitive) {
613
+ m_focusPrimitive->m_focusOuterPrimitive = std::make_shared<BorderPrimitive>(*this);
614
+ m_focusPrimitive->m_focusVisual.InsertAt(m_focusPrimitive->m_focusOuterPrimitive->RootVisual(), 0);
523
615
  }
524
- if (m_focusOuterPrimitive) {
525
- m_focusOuterPrimitive->markNeedsUpdate();
526
- auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
527
- m_focusOuterPrimitive->finalize(outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
616
+ if (auto focusVisual =
617
+ winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(
618
+ m_focusPrimitive->m_focusVisual)) {
619
+ auto outerVisual =
620
+ winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(
621
+ view->OuterVisual());
622
+ focusVisual.ParentForTransform(outerVisual);
528
623
  }
624
+ updateFocusLayoutMetrics();
625
+ auto innerFocusMetrics = view->focusLayoutMetrics(true /*inner*/);
626
+ m_focusPrimitive->m_focusInnerPrimitive->finalize(
627
+ innerFocusMetrics, view->focusBorderMetrics(true /*inner*/, innerFocusMetrics));
628
+ auto outerFocusMetrics = view->focusLayoutMetrics(false /*inner*/);
629
+ m_focusPrimitive->m_focusOuterPrimitive->finalize(
630
+ outerFocusMetrics, view->focusBorderMetrics(false /*inner*/, outerFocusMetrics));
631
+ } else if (!show && view->m_componentHostingFocusVisual && m_focusPrimitive) {
632
+ m_focusPrimitive->m_focusVisualComponent = nullptr;
633
+ m_focusPrimitive->m_focusVisual.IsVisible(false);
634
+ view->m_componentHostingFocusVisual = nullptr;
529
635
  }
530
636
  }
531
637
  }
@@ -20,6 +20,13 @@ struct CompContext;
20
20
 
21
21
  namespace winrt::Microsoft::ReactNative::Composition::implementation {
22
22
 
23
+ struct FocusPrimitive {
24
+ std::shared_ptr<BorderPrimitive> m_focusInnerPrimitive;
25
+ std::shared_ptr<BorderPrimitive> m_focusOuterPrimitive;
26
+ winrt::com_ptr<ComponentView> m_focusVisualComponent{nullptr}; // The owning component of focus visuals being hosted
27
+ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_focusVisual{nullptr};
28
+ };
29
+
23
30
  struct ComponentView : public ComponentViewT<
24
31
  ComponentView,
25
32
  winrt::Microsoft::ReactNative::implementation::ComponentView,
@@ -125,13 +132,10 @@ struct ComponentView : public ComponentViewT<
125
132
  facebook::react::SharedViewEventEmitter m_eventEmitter;
126
133
 
127
134
  private:
128
- void updateFocusLayoutMetrics(facebook::react::LayoutMetrics const &layoutMetrics) noexcept;
135
+ void updateFocusLayoutMetrics() noexcept;
129
136
  void updateClippingPath(
130
137
  facebook::react::LayoutMetrics const &layoutMetrics,
131
138
  const facebook::react::ViewProps &viewProps) noexcept;
132
- void finalizeFocusVisual(
133
- facebook::react::LayoutMetrics const &layoutMetrics,
134
- const facebook::react::ViewProps &viewProps) noexcept;
135
139
  void UpdateCenterPropertySet() noexcept;
136
140
  void FinalizeTransform(
137
141
  facebook::react::LayoutMetrics const &layoutMetrics,
@@ -140,16 +144,18 @@ struct ComponentView : public ComponentViewT<
140
144
  facebook::react::BorderMetrics focusBorderMetrics(bool inner, const facebook::react::LayoutMetrics &layoutMetrics)
141
145
  const noexcept;
142
146
 
147
+ virtual winrt::Microsoft::ReactNative::Composition::Experimental::IVisual visualToHostFocus() noexcept;
148
+ virtual winrt::com_ptr<ComponentView> focusVisualRoot(const facebook::react::Rect &focusRect) noexcept;
149
+
143
150
  bool m_hasTransformMatrixFacade : 1 {false};
144
- bool m_showingFocusVisual : 1 {false};
145
151
  bool m_FinalizeTransform : 1 {false};
146
152
  bool m_tooltipTracked : 1 {false};
147
153
  ComponentViewFeatures m_flags;
148
- void showFocusVisual(bool show) noexcept;
154
+ void hostFocusVisual(bool show, winrt::com_ptr<ComponentView> view) noexcept;
155
+ winrt::com_ptr<ComponentView>
156
+ m_componentHostingFocusVisual; // The component that we are showing our focus visuals within
149
157
  std::shared_ptr<BorderPrimitive> m_borderPrimitive;
150
- std::shared_ptr<BorderPrimitive> m_focusInnerPrimitive;
151
- std::shared_ptr<BorderPrimitive> m_focusOuterPrimitive;
152
- winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_focusVisual{nullptr};
158
+ std::unique_ptr<FocusPrimitive> m_focusPrimitive{nullptr};
153
159
  winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_outerVisual{nullptr};
154
160
  winrt::event<winrt::Windows::Foundation::EventHandler<winrt::IInspectable>> m_themeChangedEvent;
155
161
  };
@@ -176,7 +182,6 @@ struct ViewComponentView : public ViewComponentViewT<
176
182
  facebook::react::LayoutMetrics const &layoutMetrics,
177
183
  facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept override;
178
184
  void prepareForRecycle() noexcept override;
179
- bool TryFocus() noexcept;
180
185
  bool focusable() const noexcept override;
181
186
  void OnKeyDown(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept override;
182
187
  void OnKeyUp(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept override;
@@ -1258,4 +1258,14 @@ std::string ScrollViewComponentView::DefaultControlType() const noexcept {
1258
1258
  return "scrollbar";
1259
1259
  }
1260
1260
 
1261
+ winrt::com_ptr<ComponentView> ScrollViewComponentView::focusVisualRoot(
1262
+ const facebook::react::Rect &focusRect) noexcept {
1263
+ return get_strong();
1264
+ }
1265
+
1266
+ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual
1267
+ ScrollViewComponentView::visualToHostFocus() noexcept {
1268
+ return m_scrollVisual;
1269
+ }
1270
+
1261
1271
  } // namespace winrt::Microsoft::ReactNative::Composition::implementation
@@ -110,6 +110,9 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
110
110
  bool lineRight(bool animate) noexcept;
111
111
  winrt::Microsoft::ReactNative::Composition::Experimental::IVisual createVisual() noexcept override;
112
112
 
113
+ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual visualToHostFocus() noexcept override;
114
+ winrt::com_ptr<ComponentView> focusVisualRoot(const facebook::react::Rect &focusRect) noexcept override;
115
+
113
116
  private:
114
117
  void updateContentVisualSize() noexcept;
115
118
  bool scrollToEnd(bool animate) noexcept;
@@ -165,6 +165,10 @@ struct CompTextHost : public winrt::implements<CompTextHost, ITextHost> {
165
165
 
166
166
  //@cmember Show the caret
167
167
  BOOL TxShowCaret(BOOL fShow) override {
168
+ // Only show the caret if we have focus
169
+ if (fShow && !m_outer->m_hasFocus) {
170
+ return false;
171
+ }
168
172
  m_outer->ShowCaret(m_outer->windowsTextInputProps().caretHidden ? false : fShow);
169
173
  return true;
170
174
  }
@@ -915,6 +919,7 @@ void WindowsTextInputComponentView::UnmountChildComponentView(
915
919
 
916
920
  void WindowsTextInputComponentView::onLostFocus(
917
921
  const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
922
+ m_hasFocus = false;
918
923
  Super::onLostFocus(args);
919
924
  if (m_textServices) {
920
925
  LRESULT lresult;
@@ -926,6 +931,7 @@ void WindowsTextInputComponentView::onLostFocus(
926
931
 
927
932
  void WindowsTextInputComponentView::onGotFocus(
928
933
  const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
934
+ m_hasFocus = true;
929
935
  Super::onGotFocus(args);
930
936
  if (m_textServices) {
931
937
  LRESULT lresult;
@@ -128,6 +128,7 @@ struct WindowsTextInputComponentView
128
128
  int m_cDrawBlock{0};
129
129
  bool m_needsRedraw{false};
130
130
  bool m_drawing{false};
131
+ bool m_hasFocus{false};
131
132
  bool m_clearTextOnSubmit{false};
132
133
  bool m_multiline{false};
133
134
  DWORD m_propBitsMask{0};
@@ -26,7 +26,7 @@ UnimplementedNativeViewComponentView::UnimplementedNativeViewComponentView(
26
26
  ~(ComponentViewFeatures::Background | ComponentViewFeatures::ShadowProps |
27
27
  ComponentViewFeatures::NativeBorder)) {
28
28
  m_labelVisual = compContext.CreateSpriteVisual();
29
- OuterVisual().InsertAt(m_labelVisual, 1);
29
+ OuterVisual().InsertAt(m_labelVisual, 0);
30
30
  }
31
31
 
32
32
  winrt::Microsoft::ReactNative::ComponentView UnimplementedNativeViewComponentView::Create(
@@ -10,11 +10,11 @@
10
10
  -->
11
11
  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
12
12
  <PropertyGroup>
13
- <ReactNativeWindowsVersion>0.74.25</ReactNativeWindowsVersion>
13
+ <ReactNativeWindowsVersion>0.74.26</ReactNativeWindowsVersion>
14
14
  <ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
15
15
  <ReactNativeWindowsMinor>74</ReactNativeWindowsMinor>
16
- <ReactNativeWindowsPatch>25</ReactNativeWindowsPatch>
16
+ <ReactNativeWindowsPatch>26</ReactNativeWindowsPatch>
17
17
  <ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
18
- <ReactNativeWindowsCommitId>15286b6547fe52ab5ae6d98e442a52ca1627d0e9</ReactNativeWindowsCommitId>
18
+ <ReactNativeWindowsCommitId>242effb4b70c5c442b0568203fb2de1818b9f8a3</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.74.25",
3
+ "version": "0.74.26",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",