Skip to content

Commit d89154d

Browse files
authored
[0.83] Focus fixes (#15871)
* Enable imperative focus (#15863) * Fix view.focus function * Change files * snapshots * Only show focus visuals when using keyboard to move focus (#15864) * Only show focus visuals when using keyboard to move focus * Change files * fix change files * Revert "fix change files" This reverts commit f99ebba.
1 parent 13c867d commit d89154d

16 files changed

Lines changed: 135 additions & 25 deletions
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Fix view.focus function",
4+
"packageName": "react-native-windows",
5+
"email": "30809111+acoates-ms@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Only show focus visuals when using keyboard to move focus",
4+
"packageName": "react-native-windows",
5+
"email": "30809111+acoates-ms@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

packages/e2e-test-app-fabric/test/__snapshots__/PressableComponentTest.test.ts.snap

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,13 @@ exports[`Pressable Tests Pressables can have event handlers, hover and click 2`]
846846
"Name": "pressOut",
847847
"TextRangePattern.GetText": "pressOut",
848848
},
849+
{
850+
"AutomationId": "",
851+
"ControlType": 50020,
852+
"LocalizedControlType": "text",
853+
"Name": "focus",
854+
"TextRangePattern.GetText": "focus",
855+
},
849856
{
850857
"AutomationId": "",
851858
"ControlType": 50020,
@@ -891,6 +898,10 @@ exports[`Pressable Tests Pressables can have event handlers, hover and click 2`]
891898
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
892899
"_Props": {},
893900
},
901+
{
902+
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
903+
"_Props": {},
904+
},
894905
],
895906
},
896907
"Visual Tree": {
@@ -991,6 +1002,18 @@ exports[`Pressable Tests Pressables can have event handlers, hover and click 2`]
9911002
},
9921003
],
9931004
},
1005+
{
1006+
"Offset": "11, 85, 0",
1007+
"Size": "874, 20",
1008+
"Visual Type": "SpriteVisual",
1009+
"__Children": [
1010+
{
1011+
"Offset": "0, 0, 0",
1012+
"Size": "874, 20",
1013+
"Visual Type": "SpriteVisual",
1014+
},
1015+
],
1016+
},
9941017
],
9951018
},
9961019
}

vnext/Microsoft.ReactNative/ComponentView.idl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ namespace Microsoft.ReactNative
4343
Last,
4444
};
4545

46+
enum FocusState
47+
{
48+
Unfocused = 0,
49+
Pointer,
50+
Keyboard,
51+
Programmatic,
52+
};
53+
4654
[webhosthidden]
4755
[experimental]
4856
interface IComponentState
@@ -99,7 +107,7 @@ namespace Microsoft.ReactNative
99107
LayoutMetrics LayoutMetrics { get; };
100108
IInspectable UserData;
101109

102-
Boolean TryFocus();
110+
Boolean TryFocus(FocusState focusState);
103111

104112
DOC_STRING("Used to handle key down events when this component is focused, or if a child component did not handle the key down")
105113
event Windows.Foundation.EventHandler<Microsoft.ReactNative.Composition.Input.KeyRoutedEventArgs> KeyDown;

vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ void ComponentView::parent(const winrt::Microsoft::ReactNative::ComponentView &p
283283
oldRootView->TrySetFocusedComponent(
284284
oldParent,
285285
winrt::Microsoft::ReactNative::FocusNavigationDirection::None,
286+
winrt::Microsoft::ReactNative::FocusState::Programmatic,
286287
true /*forceNoSelectionIfCannotMove*/);
287288
}
288289
}
@@ -431,9 +432,10 @@ void ComponentView::GotFocus(winrt::event_token const &token) noexcept {
431432
m_gotFocusEvent.remove(token);
432433
}
433434

434-
bool ComponentView::TryFocus() noexcept {
435+
bool ComponentView::TryFocus(winrt::Microsoft::ReactNative::FocusState focusState) noexcept {
435436
if (auto root = rootComponentView()) {
436-
return root->TrySetFocusedComponent(*get_strong(), winrt::Microsoft::ReactNative::FocusNavigationDirection::None);
437+
return root->TrySetFocusedComponent(
438+
*get_strong(), winrt::Microsoft::ReactNative::FocusNavigationDirection::None, focusState);
437439
}
438440

439441
return false;

vnext/Microsoft.ReactNative/Fabric/ComponentView.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ struct ComponentView
201201

202202
LayoutMetrics LayoutMetrics() const noexcept;
203203

204-
bool TryFocus() noexcept;
204+
bool TryFocus(winrt::Microsoft::ReactNative::FocusState focusState) noexcept;
205205

206206
virtual bool focusable() const noexcept;
207207
virtual facebook::react::SharedViewEventEmitter eventEmitterAtPoint(facebook::react::Point pt) noexcept;

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,8 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
608608

609609
void CompositionEventHandler::onKeyDown(
610610
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
611+
RootComponentView().UseKeyboardForProgrammaticFocus(true);
612+
611613
if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
612614
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)->OnKeyDown(args);
613615

@@ -631,7 +633,7 @@ void CompositionEventHandler::onKeyDown(
631633
}
632634

633635
if (!fCtrl && args.Key() == winrt::Windows::System::VirtualKey::Tab) {
634-
if (RootComponentView().TryMoveFocus(!fShift)) {
636+
if (RootComponentView().TryMoveFocus(!fShift, winrt::Microsoft::ReactNative::FocusState::Keyboard)) {
635637
args.Handled(true);
636638
}
637639

@@ -641,6 +643,8 @@ void CompositionEventHandler::onKeyDown(
641643

642644
void CompositionEventHandler::onKeyUp(
643645
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
646+
RootComponentView().UseKeyboardForProgrammaticFocus(true);
647+
644648
if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
645649
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)->OnKeyUp(args);
646650

@@ -1173,6 +1177,8 @@ void CompositionEventHandler::onPointerPressed(
11731177
winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
11741178
namespace Composition = winrt::Microsoft::ReactNative::Composition;
11751179

1180+
RootComponentView().UseKeyboardForProgrammaticFocus(false);
1181+
11761182
// Clears any active text selection when left pointer is pressed
11771183
if (pointerPoint.Properties().PointerUpdateKind() != Composition::Input::PointerUpdateKind::RightButtonPressed) {
11781184
RootComponentView().ClearCurrentTextSelection();
@@ -1269,6 +1275,8 @@ void CompositionEventHandler::onPointerReleased(
12691275
winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
12701276
int pointerId = pointerPoint.PointerId();
12711277

1278+
RootComponentView().UseKeyboardForProgrammaticFocus(false);
1279+
12721280
auto activeTouch = std::find_if(m_activeTouches.begin(), m_activeTouches.end(), [pointerId](const auto &pair) {
12731281
return pair.second.touch.identifier == pointerId;
12741282
});

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,8 @@ void ComponentView::onGotFocus(
373373
const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
374374
if (args.OriginalSource() == Tag()) {
375375
m_eventEmitter->onFocus();
376-
if (viewProps()->enableFocusRing) {
376+
if (viewProps()->enableFocusRing &&
377+
rootComponentView()->focusState() == winrt::Microsoft::ReactNative::FocusState::Keyboard) {
377378
facebook::react::Rect focusRect = m_layoutMetrics.frame;
378379
focusRect.origin.x -= (FOCUS_VISUAL_WIDTH * 2);
379380
focusRect.origin.y -= (FOCUS_VISUAL_WIDTH * 2);
@@ -426,15 +427,20 @@ void ComponentView::HandleCommand(const winrt::Microsoft::ReactNative::HandleCom
426427
auto commandName = args.CommandName();
427428
if (commandName == L"focus") {
428429
if (auto root = rootComponentView()) {
429-
root->TrySetFocusedComponent(*get_strong(), winrt::Microsoft::ReactNative::FocusNavigationDirection::None);
430+
root->TrySetFocusedComponent(
431+
*get_strong(),
432+
winrt::Microsoft::ReactNative::FocusNavigationDirection::None,
433+
winrt::Microsoft::ReactNative::FocusState::Programmatic);
430434
}
431435
return;
432436
}
433437
if (commandName == L"blur") {
434438
if (auto root = rootComponentView()) {
435439
root->TrySetFocusedComponent(
436-
nullptr, winrt::Microsoft::ReactNative::FocusNavigationDirection::None); // Todo store this component as
437-
// previously focused element
440+
nullptr,
441+
winrt::Microsoft::ReactNative::FocusNavigationDirection::None,
442+
winrt::Microsoft::ReactNative::FocusState::Programmatic); // Todo store this component as
443+
// previously focused element
438444
}
439445
return;
440446
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void ContentIslandComponentView::ConnectInternal() noexcept {
8282
m_navigationHost.DepartFocusRequested([wkThis = get_weak()](const auto &, const auto &args) {
8383
if (auto strongThis = wkThis.get()) {
8484
const bool next = (args.Request().Reason() != winrt::Microsoft::UI::Input::FocusNavigationReason::Last);
85-
strongThis->rootComponentView()->TryMoveFocus(next);
85+
strongThis->rootComponentView()->TryMoveFocus(next, winrt::Microsoft::ReactNative::FocusState::Programmatic);
8686
args.Result(winrt::Microsoft::UI::Input::FocusNavigationResult::Moved);
8787
}
8888
});

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,10 @@ void ParagraphComponentView::OnPointerPressed(
622622

623623
// Focuses so we receive onLostFocus when clicking elsewhere
624624
if (auto root = rootComponentView()) {
625-
root->TrySetFocusedComponent(*get_strong(), winrt::Microsoft::ReactNative::FocusNavigationDirection::None);
625+
root->TrySetFocusedComponent(
626+
*get_strong(),
627+
winrt::Microsoft::ReactNative::FocusNavigationDirection::None,
628+
winrt::Microsoft::ReactNative::FocusState::Pointer);
626629
}
627630

628631
args.Handled(true);

0 commit comments

Comments
 (0)