Skip to content

Commit 98087a7

Browse files
authored
Round Focus visuals by default, fix nudge rendering (#14312)
* Round Focus visuals by default, fix nudge rendering * Change files
1 parent ad17235 commit 98087a7

File tree

3 files changed

+89
-45
lines changed

3 files changed

+89
-45
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Round Focus visuals by default, fix nudge rendering",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp

Lines changed: 80 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
namespace winrt::Microsoft::ReactNative::Composition::implementation {
3434

3535
constexpr float FOCUS_VISUAL_WIDTH = 2.0f;
36+
constexpr float FOCUS_VISUAL_RADIUS = 3.0f;
3637

3738
// m_outerVisual
3839
// |
@@ -228,29 +229,11 @@ void ComponentView::updateFocusLayoutMetrics() noexcept {
228229
facebook::react::RectangleEdges<bool> nudgeEdges;
229230
auto scaleFactor = m_focusPrimitive->m_focusVisualComponent->m_layoutMetrics.pointScaleFactor;
230231
if (m_focusPrimitive) {
232+
auto nudgeEdges = m_focusPrimitive->m_focusVisualComponent->focusNudges();
231233
if (m_focusPrimitive->m_focusOuterPrimitive) {
232234
auto outerFocusMetrics = m_focusPrimitive->m_focusVisualComponent->focusLayoutMetrics(false /*inner*/);
233-
234-
if (outerFocusMetrics.frame.origin.x < 0) {
235-
nudgeEdges.left = true;
236-
}
237-
if (outerFocusMetrics.frame.origin.y < 0) {
238-
nudgeEdges.top = true;
239-
}
240-
if (outerFocusMetrics.frame.getMaxX() > m_layoutMetrics.frame.getMaxX()) {
241-
nudgeEdges.right = true;
242-
}
243-
if (outerFocusMetrics.frame.getMaxY() > m_layoutMetrics.frame.getMaxY()) {
244-
nudgeEdges.bottom = true;
245-
}
246-
247235
m_focusPrimitive->m_focusOuterPrimitive->RootVisual().Size(
248-
{outerFocusMetrics.frame.size.width * scaleFactor -
249-
(nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0) -
250-
(nudgeEdges.right ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0),
251-
outerFocusMetrics.frame.size.height * scaleFactor -
252-
(nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0) -
253-
(nudgeEdges.bottom ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0)});
236+
{outerFocusMetrics.frame.size.width * scaleFactor, outerFocusMetrics.frame.size.height * scaleFactor});
254237
m_focusPrimitive->m_focusOuterPrimitive->RootVisual().Offset(
255238
{nudgeEdges.left ? 0 : -(FOCUS_VISUAL_WIDTH * 2 * scaleFactor),
256239
nudgeEdges.top ? 0 : -(FOCUS_VISUAL_WIDTH * 2 * scaleFactor),
@@ -261,15 +244,10 @@ void ComponentView::updateFocusLayoutMetrics() noexcept {
261244
if (m_focusPrimitive->m_focusInnerPrimitive) {
262245
auto innerFocusMetrics = m_focusPrimitive->m_focusVisualComponent->focusLayoutMetrics(true /*inner*/);
263246
m_focusPrimitive->m_focusInnerPrimitive->RootVisual().Size(
264-
{innerFocusMetrics.frame.size.width * scaleFactor -
265-
(nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0) -
266-
(nudgeEdges.right ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0),
267-
innerFocusMetrics.frame.size.height * scaleFactor -
268-
(nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0) -
269-
(nudgeEdges.bottom ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0)});
247+
{innerFocusMetrics.frame.size.width * scaleFactor, innerFocusMetrics.frame.size.height * scaleFactor});
270248
m_focusPrimitive->m_focusInnerPrimitive->RootVisual().Offset(
271-
{nudgeEdges.left ? 0 : -FOCUS_VISUAL_WIDTH * scaleFactor,
272-
nudgeEdges.top ? 0 : -FOCUS_VISUAL_WIDTH * scaleFactor,
249+
{nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * scaleFactor) : (-FOCUS_VISUAL_WIDTH * scaleFactor),
250+
nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * scaleFactor) : (-FOCUS_VISUAL_WIDTH * scaleFactor),
273251
0.0f});
274252
m_focusPrimitive->m_focusInnerPrimitive->markNeedsUpdate();
275253
}
@@ -538,7 +516,33 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ComponentView:
538516
return m_outerVisual ? m_outerVisual : Visual();
539517
}
540518

541-
facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) const noexcept {
519+
// If the focus visual would extend past the bounds of the hosting visual,
520+
// then we will nudge the focus visual back inside the hosting visuals bounds.
521+
facebook::react::RectangleEdges<bool> ComponentView::focusNudges() const noexcept {
522+
facebook::react::RectangleEdges<bool> nudgeEdges;
523+
524+
// Always use outer focus metrics to determine if we need to nudge the focus rect over to fit
525+
facebook::react::LayoutMetrics layoutMetrics = focusLayoutMetricsNoNudge(false /*inner*/);
526+
527+
Assert(m_componentHostingFocusVisual);
528+
529+
if (layoutMetrics.frame.origin.x < 0) {
530+
nudgeEdges.left = true;
531+
}
532+
if (layoutMetrics.frame.origin.y < 0) {
533+
nudgeEdges.top = true;
534+
}
535+
if (layoutMetrics.frame.getMaxX() > m_componentHostingFocusVisual->m_layoutMetrics.frame.getMaxX()) {
536+
nudgeEdges.right = true;
537+
}
538+
if (layoutMetrics.frame.getMaxY() > m_componentHostingFocusVisual->m_layoutMetrics.frame.getMaxY()) {
539+
nudgeEdges.bottom = true;
540+
}
541+
542+
return nudgeEdges;
543+
}
544+
545+
facebook::react::LayoutMetrics ComponentView::focusLayoutMetricsNoNudge(bool inner) const noexcept {
542546
facebook::react::LayoutMetrics layoutMetrics = m_layoutMetrics;
543547
layoutMetrics.frame.origin.x -= FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
544548
layoutMetrics.frame.origin.y -= FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
@@ -547,6 +551,28 @@ facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) con
547551
return layoutMetrics;
548552
}
549553

554+
facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) const noexcept {
555+
auto nudgeEdges = focusNudges();
556+
auto layoutMetrics = focusLayoutMetricsNoNudge(inner);
557+
558+
if (nudgeEdges.left) {
559+
layoutMetrics.frame.origin.x += FOCUS_VISUAL_WIDTH * 2;
560+
layoutMetrics.frame.size.width -= FOCUS_VISUAL_WIDTH * 2;
561+
}
562+
if (nudgeEdges.top) {
563+
layoutMetrics.frame.origin.y += FOCUS_VISUAL_WIDTH * 2;
564+
layoutMetrics.frame.size.height -= FOCUS_VISUAL_WIDTH * 2;
565+
}
566+
if (nudgeEdges.right) {
567+
layoutMetrics.frame.size.width -= FOCUS_VISUAL_WIDTH * 2;
568+
}
569+
if (nudgeEdges.bottom) {
570+
layoutMetrics.frame.size.height -= FOCUS_VISUAL_WIDTH * 2;
571+
}
572+
573+
return layoutMetrics;
574+
}
575+
550576
facebook::react::BorderMetrics ComponentView::focusBorderMetrics(
551577
bool inner,
552578
const facebook::react::LayoutMetrics &layoutMetrics) const noexcept {
@@ -556,22 +582,31 @@ facebook::react::BorderMetrics ComponentView::focusBorderMetrics(
556582
innerColor.m_platformColor.push_back(inner ? "FocusVisualSecondary" : "FocusVisualPrimary");
557583
metrics.borderColors.bottom = metrics.borderColors.left = metrics.borderColors.right = metrics.borderColors.top =
558584
innerColor;
559-
if (metrics.borderRadii.bottomLeft.horizontal != 0)
560-
metrics.borderRadii.bottomLeft.horizontal += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
561-
if (metrics.borderRadii.bottomLeft.vertical != 0)
562-
metrics.borderRadii.bottomLeft.vertical += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
563-
if (metrics.borderRadii.bottomRight.horizontal != 0)
564-
metrics.borderRadii.bottomRight.horizontal += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
565-
if (metrics.borderRadii.bottomRight.vertical != 0)
566-
metrics.borderRadii.bottomRight.vertical += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
567-
if (metrics.borderRadii.topLeft.horizontal != 0)
568-
metrics.borderRadii.topLeft.horizontal += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
569-
if (metrics.borderRadii.topLeft.vertical != 0)
570-
metrics.borderRadii.topLeft.vertical += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
571-
if (metrics.borderRadii.topRight.horizontal != 0)
572-
metrics.borderRadii.topRight.horizontal += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
573-
if (metrics.borderRadii.topRight.vertical != 0)
574-
metrics.borderRadii.topRight.vertical += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
585+
586+
metrics.borderRadii.bottomLeft.horizontal =
587+
(metrics.borderRadii.bottomLeft.horizontal ? metrics.borderRadii.bottomLeft.horizontal : FOCUS_VISUAL_RADIUS) +
588+
FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
589+
metrics.borderRadii.bottomLeft.vertical =
590+
(metrics.borderRadii.bottomLeft.vertical ? metrics.borderRadii.bottomLeft.vertical : FOCUS_VISUAL_RADIUS) +
591+
FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
592+
metrics.borderRadii.bottomRight.horizontal =
593+
(metrics.borderRadii.bottomRight.horizontal ? metrics.borderRadii.bottomRight.horizontal : FOCUS_VISUAL_RADIUS) +
594+
FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
595+
metrics.borderRadii.bottomRight.vertical =
596+
(metrics.borderRadii.bottomRight.vertical ? metrics.borderRadii.bottomRight.vertical : FOCUS_VISUAL_RADIUS) +
597+
FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
598+
metrics.borderRadii.topLeft.horizontal =
599+
(metrics.borderRadii.topLeft.horizontal ? metrics.borderRadii.topLeft.horizontal : FOCUS_VISUAL_RADIUS) +
600+
FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
601+
metrics.borderRadii.topLeft.vertical =
602+
(metrics.borderRadii.topLeft.vertical ? metrics.borderRadii.topLeft.vertical : FOCUS_VISUAL_RADIUS) +
603+
FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
604+
metrics.borderRadii.topRight.horizontal =
605+
(metrics.borderRadii.topRight.horizontal ? metrics.borderRadii.topRight.horizontal : FOCUS_VISUAL_RADIUS) +
606+
FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
607+
metrics.borderRadii.topRight.vertical =
608+
(metrics.borderRadii.topRight.vertical ? metrics.borderRadii.topRight.vertical : FOCUS_VISUAL_RADIUS) +
609+
FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
575610

576611
metrics.borderStyles.bottom = metrics.borderStyles.left = metrics.borderStyles.right = metrics.borderStyles.top =
577612
facebook::react::BorderStyle::Solid;

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ struct ComponentView : public ComponentViewT<
143143
void FinalizeTransform(
144144
facebook::react::LayoutMetrics const &layoutMetrics,
145145
const facebook::react::ViewProps &viewProps) noexcept;
146+
facebook::react::RectangleEdges<bool> focusNudges() const noexcept;
147+
facebook::react::LayoutMetrics focusLayoutMetricsNoNudge(bool inner) const noexcept;
146148
facebook::react::LayoutMetrics focusLayoutMetrics(bool inner) const noexcept;
147149
facebook::react::BorderMetrics focusBorderMetrics(bool inner, const facebook::react::LayoutMetrics &layoutMetrics)
148150
const noexcept;

0 commit comments

Comments
 (0)