Skip to content

Commit

Permalink
[Fabric] Add FocusNavigationDirection and allow overriding of default…
Browse files Browse the repository at this point in the history
… command handling (#13857)

* [Fabric] Add FocusNavigationDirection and allow overriding of default command handling

* Change files

* revert packages.lock
  • Loading branch information
acoates-ms authored Sep 13, 2024
1 parent a45509e commit 67e7442
Show file tree
Hide file tree
Showing 28 changed files with 179 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "[Fabric] Add FocusNavigationDirection and allow overriding of default command handling",
"packageName": "@react-native-windows/codegen",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "[Fabric] Add FocusNavigationDirection and allow overriding of default command handling",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -334,28 +334,27 @@ export function createComponentGenerator({
}).join('\n\n') : '';


const commandHandler = hasAnyCommands ? `void HandleCommand(const winrt::Microsoft::ReactNative::ComponentView &view, winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {
args;
const commandHandler = hasAnyCommands ? `void HandleCommand(const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::HandleCommandArgs& args) noexcept {
auto userData = view.UserData().as<TUserData>();
auto commandName = args.CommandName();
${componentShape.commands.map(command => {
const commaSeparatedCommandArgs = command.typeAnnotation.params.map(param => param.name).join(', ');
return ` if (commandName == L"${command.name}") {
${command.typeAnnotation.params.length !== 0 ? ` ${command.typeAnnotation.params.map(param => {
const commandArgType = translateCommandParamType(param.typeAnnotation, commandAliases, `${componentName}_${command.name}`, cppCodegenOptions);
return `${(param.optional && !commandArgType.alreadySupportsOptionalOrHasDefault) ? `std::optional<${commandArgType.type}>` : commandArgType.type} ${param.name};`;
}).join('\n')}
winrt::Microsoft::ReactNative::ReadArgs(args, ${commaSeparatedCommandArgs});` : ''}
winrt::Microsoft::ReactNative::ReadArgs(args.CommandArgs(), ${commaSeparatedCommandArgs});` : ''}
userData->Handle${capitalizeFirstLetter(command.name)}Command(${commaSeparatedCommandArgs});
return;
}`
}).join('\n\n')}
}` : '';

const registerCommandHandler = hasAnyCommands ? ` builder.SetCustomCommandHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
winrt::hstring commandName,
const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {
const winrt::Microsoft::ReactNative::HandleCommandArgs& args) noexcept {
auto userData = view.UserData().as<TUserData>();
userData->HandleCommand(view, commandName, args);
userData->HandleCommand(view, args);
});` : '';

const baseType = baseStructTemplate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ struct BaseMovingLight {
// You must provide an implementation of this method to handle the "setLightOn" command
virtual void HandleSetLightOnCommand(bool value) noexcept = 0;

void HandleCommand(const winrt::Microsoft::ReactNative::ComponentView &view, winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {
args;
void HandleCommand(const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::HandleCommandArgs& args) noexcept {
auto userData = view.UserData().as<TUserData>();
auto commandName = args.CommandName();
if (commandName == L"setLightOn") {
bool value;
winrt::Microsoft::ReactNative::ReadArgs(args, value);
winrt::Microsoft::ReactNative::ReadArgs(args.CommandArgs(), value);
userData->HandleSetLightOnCommand(value);
return;
}
Expand Down Expand Up @@ -175,10 +175,9 @@ void RegisterMovingLightNativeComponent(
}

builder.SetCustomCommandHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
winrt::hstring commandName,
const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {
const winrt::Microsoft::ReactNative::HandleCommandArgs& args) noexcept {
auto userData = view.UserData().as<TUserData>();
userData->HandleCommand(view, commandName, args);
userData->HandleCommand(view, args);
});

if constexpr (&TUserData::MountChildComponentView != &BaseMovingLight<TUserData>::MountChildComponentView) {
Expand Down
11 changes: 11 additions & 0 deletions vnext/Microsoft.ReactNative/ComponentView.idl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ namespace Microsoft.ReactNative
All = 0x0000000F,
};

enum FocusNavigationDirection
{
None,
Next,
Previous,
First,
Last,
};

[webhosthidden]
[experimental]
interface IComponentState
Expand All @@ -46,6 +55,7 @@ namespace Microsoft.ReactNative
[experimental]
[webhosthidden]
runtimeclass LosingFocusEventArgs : Microsoft.ReactNative.Composition.Input.RoutedEventArgs {
FocusNavigationDirection Direction { get; };
Microsoft.ReactNative.ComponentView NewFocusedComponent { get; };
Microsoft.ReactNative.ComponentView OldFocusedComponent { get; };

Expand All @@ -56,6 +66,7 @@ namespace Microsoft.ReactNative
[experimental]
[webhosthidden]
runtimeclass GettingFocusEventArgs : Microsoft.ReactNative.Composition.Input.RoutedEventArgs {
FocusNavigationDirection Direction { get; };
Microsoft.ReactNative.ComponentView NewFocusedComponent { get; };
Microsoft.ReactNative.ComponentView OldFocusedComponent { get; };

Expand Down
10 changes: 4 additions & 6 deletions vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,9 @@ void ComponentView::CustomCommandHandler(const HandleCommandDelegate &handler) n
m_customCommandHandler = handler;
}

void ComponentView::HandleCommand(
winrt::hstring commandName,
const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {
void ComponentView::HandleCommand(const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept {
if (m_customCommandHandler) {
m_customCommandHandler(*this, commandName, args);
m_customCommandHandler(*this, args);
}
}

Expand All @@ -285,7 +283,7 @@ void ComponentView::parent(const winrt::Microsoft::ReactNative::ComponentView &p
m_parent = parent;
if (!parent) {
if (oldRootView && oldRootView->GetFocusedComponent() == *this) {
oldRootView->TrySetFocusedComponent(oldParent);
oldRootView->TrySetFocusedComponent(oldParent, winrt::Microsoft::ReactNative::FocusNavigationDirection::None);
}
}
if (parent) {
Expand Down Expand Up @@ -424,7 +422,7 @@ void ComponentView::GotFocus(winrt::event_token const &token) noexcept {

bool ComponentView::TryFocus() noexcept {
if (auto root = rootComponentView()) {
return root->TrySetFocusedComponent(*get_strong());
return root->TrySetFocusedComponent(*get_strong(), winrt::Microsoft::ReactNative::FocusNavigationDirection::None);
}

return false;
Expand Down
4 changes: 1 addition & 3 deletions vnext/Microsoft.ReactNative/Fabric/ComponentView.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,7 @@ struct ComponentView : public ComponentViewT<ComponentView> {
virtual void UnmountChildComponentView(
const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
uint32_t index) noexcept;
virtual void HandleCommand(
winrt::hstring commandName,
const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept;
virtual void HandleCommand(const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept;
virtual void FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept;
virtual void OnPointerEntered(
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,23 +248,26 @@ void ComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared con
base_type::updateEventEmitter(eventEmitter);
}

void ComponentView::HandleCommand(
winrt::hstring commandName,
const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {
void ComponentView::HandleCommand(const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept {
base_type::HandleCommand(args);
if (args.Handled())
return;

auto commandName = args.CommandName();
if (commandName == L"focus") {
if (auto root = rootComponentView()) {
root->TrySetFocusedComponent(*get_strong());
root->TrySetFocusedComponent(*get_strong(), winrt::Microsoft::ReactNative::FocusNavigationDirection::None);
}
return;
}
if (commandName == L"blur") {
if (auto root = rootComponentView()) {
root->TrySetFocusedComponent(nullptr); // Todo store this component as previously focused element
root->TrySetFocusedComponent(
nullptr, winrt::Microsoft::ReactNative::FocusNavigationDirection::None); // Todo store this component as
// previously focused element
}
return;
}

base_type::HandleCommand(commandName, args);
}

bool ComponentView::CapturePointer(const winrt::Microsoft::ReactNative::Composition::Input::Pointer &pointer) noexcept {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ struct ComponentView : public ComponentViewT<
virtual winrt::Microsoft::ReactNative::Composition::Experimental::IVisual OuterVisual() const noexcept;
void updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept override;
const facebook::react::SharedViewEventEmitter &GetEventEmitter() const noexcept;
void HandleCommand(winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept
override;
void HandleCommand(const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept override;
facebook::react::Props::Shared props() noexcept override;
virtual const facebook::react::SharedViewProps &viewProps() const noexcept {
static facebook::react::SharedViewProps emptyProps;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,21 @@ struct TraceUpdate {
};

void DebuggingOverlayComponentView::HandleCommand(
winrt::hstring commandName,
const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {
const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept {
base_type::HandleCommand(args);
if (args.Handled())
return;

auto commandName = args.CommandName();
if (commandName == L"highlightTraceUpdates") {
std::vector<TraceUpdate> updates;
winrt::Microsoft::ReactNative::ReadArgs(args, updates);
winrt::Microsoft::ReactNative::ReadArgs(args.CommandArgs(), updates);
// TODO should create visuals that get removed after 2 seconds
return;
}
if (commandName == L"highlightElements") {
std::vector<ElementRectangle> elements;
winrt::Microsoft::ReactNative::ReadArgs(args, elements);
winrt::Microsoft::ReactNative::ReadArgs(args.CommandArgs(), elements);

if (auto root = rootComponentView()) {
auto rootVisual = root->OuterVisual();
Expand Down Expand Up @@ -106,8 +110,6 @@ void DebuggingOverlayComponentView::HandleCommand(
}
return;
}

base_type::HandleCommand(commandName, args);
}

} // namespace winrt::Microsoft::ReactNative::Composition::implementation
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ struct DebuggingOverlayComponentView
facebook::react::Tag tag,
winrt::Microsoft::ReactNative::ReactContext const &reactContext);

void HandleCommand(winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept
override;
void HandleCommand(const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept override;

private:
uint32_t m_activeOverlays{0};
Expand Down
26 changes: 20 additions & 6 deletions vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,26 @@ int32_t GotFocusEventArgs::OriginalSource() noexcept {

LosingFocusEventArgs::LosingFocusEventArgs(
const winrt::Microsoft::ReactNative::ComponentView &originalSource,
winrt::Microsoft::ReactNative::FocusNavigationDirection direction,
const winrt::Microsoft::ReactNative::ComponentView &oldFocusedComponent,
const winrt::Microsoft::ReactNative::ComponentView &newFocusedComponent)
: m_originalSource(originalSource ? originalSource.Tag() : -1),
m_direction(direction),
m_old(oldFocusedComponent),
m_new(newFocusedComponent) {}

int32_t LosingFocusEventArgs::OriginalSource() noexcept {
int32_t LosingFocusEventArgs::OriginalSource() const noexcept {
return m_originalSource;
}
winrt::Microsoft::ReactNative::ComponentView LosingFocusEventArgs::NewFocusedComponent() noexcept {

winrt::Microsoft::ReactNative::FocusNavigationDirection LosingFocusEventArgs::Direction() const noexcept {
return m_direction;
}

winrt::Microsoft::ReactNative::ComponentView LosingFocusEventArgs::NewFocusedComponent() const noexcept {
return m_new;
}
winrt::Microsoft::ReactNative::ComponentView LosingFocusEventArgs::OldFocusedComponent() noexcept {
winrt::Microsoft::ReactNative::ComponentView LosingFocusEventArgs::OldFocusedComponent() const noexcept {
return m_old;
}

Expand All @@ -58,19 +65,26 @@ void LosingFocusEventArgs::TrySetNewFocusedComponent(

GettingFocusEventArgs::GettingFocusEventArgs(
const winrt::Microsoft::ReactNative::ComponentView &originalSource,
winrt::Microsoft::ReactNative::FocusNavigationDirection direction,
const winrt::Microsoft::ReactNative::ComponentView &oldFocusedComponent,
const winrt::Microsoft::ReactNative::ComponentView &newFocusedComponent)
: m_originalSource(originalSource ? originalSource.Tag() : -1),
m_direction(direction),
m_old(oldFocusedComponent),
m_new(newFocusedComponent) {}

int32_t GettingFocusEventArgs::OriginalSource() noexcept {
int32_t GettingFocusEventArgs::OriginalSource() const noexcept {
return m_originalSource;
}
winrt::Microsoft::ReactNative::ComponentView GettingFocusEventArgs::NewFocusedComponent() noexcept {

winrt::Microsoft::ReactNative::FocusNavigationDirection GettingFocusEventArgs::Direction() const noexcept {
return m_direction;
}

winrt::Microsoft::ReactNative::ComponentView GettingFocusEventArgs::NewFocusedComponent() const noexcept {
return m_new;
}
winrt::Microsoft::ReactNative::ComponentView GettingFocusEventArgs::OldFocusedComponent() noexcept {
winrt::Microsoft::ReactNative::ComponentView GettingFocusEventArgs::OldFocusedComponent() const noexcept {
return m_old;
}

Expand Down
19 changes: 13 additions & 6 deletions vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,21 @@ struct LosingFocusEventArgs
: winrt::Microsoft::ReactNative::implementation::LosingFocusEventArgsT<LosingFocusEventArgs> {
LosingFocusEventArgs(
const winrt::Microsoft::ReactNative::ComponentView &originalSource,
winrt::Microsoft::ReactNative::FocusNavigationDirection direction,
const winrt::Microsoft::ReactNative::ComponentView &oldFocusedComponent,
const winrt::Microsoft::ReactNative::ComponentView &newFocusedComponent);
int32_t OriginalSource() noexcept;
winrt::Microsoft::ReactNative::ComponentView NewFocusedComponent() noexcept;
winrt::Microsoft::ReactNative::ComponentView OldFocusedComponent() noexcept;
int32_t OriginalSource() const noexcept;
winrt::Microsoft::ReactNative::FocusNavigationDirection Direction() const noexcept;

winrt::Microsoft::ReactNative::ComponentView NewFocusedComponent() const noexcept;
winrt::Microsoft::ReactNative::ComponentView OldFocusedComponent() const noexcept;

void TryCancel() noexcept;
void TrySetNewFocusedComponent(const winrt::Microsoft::ReactNative::ComponentView &newFocusedComponent) noexcept;

private:
const int32_t m_originalSource;
const winrt::Microsoft::ReactNative::FocusNavigationDirection m_direction;
winrt::Microsoft::ReactNative::ComponentView m_old{nullptr};
winrt::Microsoft::ReactNative::ComponentView m_new{nullptr};
};
Expand All @@ -51,17 +55,20 @@ struct GettingFocusEventArgs
: winrt::Microsoft::ReactNative::implementation::GettingFocusEventArgsT<GettingFocusEventArgs> {
GettingFocusEventArgs(
const winrt::Microsoft::ReactNative::ComponentView &originalSource,
winrt::Microsoft::ReactNative::FocusNavigationDirection direction,
const winrt::Microsoft::ReactNative::ComponentView &oldFocusedComponent,
const winrt::Microsoft::ReactNative::ComponentView &newFocusedComponent);
int32_t OriginalSource() noexcept;
winrt::Microsoft::ReactNative::ComponentView NewFocusedComponent() noexcept;
winrt::Microsoft::ReactNative::ComponentView OldFocusedComponent() noexcept;
int32_t OriginalSource() const noexcept;
winrt::Microsoft::ReactNative::FocusNavigationDirection Direction() const noexcept;
winrt::Microsoft::ReactNative::ComponentView NewFocusedComponent() const noexcept;
winrt::Microsoft::ReactNative::ComponentView OldFocusedComponent() const noexcept;

void TryCancel() noexcept;
void TrySetNewFocusedComponent(const winrt::Microsoft::ReactNative::ComponentView &newFocusedComponent) noexcept;

private:
const int32_t m_originalSource;
const winrt::Microsoft::ReactNative::FocusNavigationDirection m_direction;
winrt::Microsoft::ReactNative::ComponentView m_old{nullptr};
winrt::Microsoft::ReactNative::ComponentView m_new{nullptr};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,8 @@ void WindowsModalHostComponentView::UnmountChildComponentView(
}

void WindowsModalHostComponentView::HandleCommand(
winrt::hstring commandName,
const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {
Super::HandleCommand(commandName, args);
const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept {
Super::HandleCommand(args);
}

void WindowsModalHostComponentView::updateProps(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ struct WindowsModalHostComponentView
void UnmountChildComponentView(
const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
uint32_t index) noexcept override;
void HandleCommand(winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept
override;
void HandleCommand(const winrt::Microsoft::ReactNative::HandleCommandArgs &args) noexcept override;
void updateState(facebook::react::State::Shared const &state, facebook::react::State::Shared const &oldState) noexcept
override;

Expand Down
Loading

0 comments on commit 67e7442

Please sign in to comment.