-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add support for React Native BackHandler API on Windows #4623
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
431cc69
da8ae9b
543ab74
6abf9c0
cf8a17c
f3f1316
05e4608
7d04303
a4362cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "type": "prerelease", | ||
| "comment": "Add support for React Native BackHandler API", | ||
| "packageName": "react-native-windows", | ||
| "email": "jahiggin@microsoft.com", | ||
| "dependentChangeType": "patch", | ||
| "date": "2020-04-16T18:09:41.547Z" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -184,6 +184,7 @@ void ReactRootControl::InitRootView( | |
| m_SIPEventHandler->AttachView(xamlRootView, /*fireKeyboradEvents:*/ true); | ||
|
|
||
| UpdateRootViewInternal(); | ||
| AttachBackHandlers(xamlRootView); | ||
|
|
||
| m_isInitialized = true; | ||
| } | ||
|
|
@@ -232,6 +233,8 @@ void ReactRootControl::UninitRootView() noexcept { | |
| m_previewKeyboardEventHandlerOnRoot->unhook(); | ||
| } | ||
|
|
||
| RemoveBackHandlers(); | ||
|
|
||
| // If the redbox error UI is shown we need to remove it, otherwise let the | ||
| // natural teardown process do this | ||
| if (m_redBoxGrid) { | ||
|
|
@@ -607,6 +610,73 @@ void ReactRootControl::ReloadViewHost() noexcept { | |
| } | ||
| } | ||
|
|
||
| void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { | ||
| auto weakThis = weak_from_this(); | ||
| m_backRequestedRevoker = winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested( | ||
| winrt::auto_revoke, | ||
| [weakThis](winrt::IInspectable const & /*sender*/, winrt::BackRequestedEventArgs const &args) { | ||
| if (auto self = weakThis.lock()) { | ||
| args.Handled(self->OnBackRequested()); | ||
| } | ||
| }); | ||
|
|
||
| // In addition to handling the BackRequested event, UWP suggests that we listen for other user inputs that should | ||
| // trigger back navigation that don't fire that event: | ||
| // https://docs.microsoft.com/en-us/windows/uwp/design/basics/navigation-history-and-backwards-navigation | ||
| auto rootElement(rootView.try_as<winrt::UIElement>()); | ||
| if (rootElement == nullptr) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Switched to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vmoroz was there a preference towards null-checking, or was pointing towards try_as more educational? It's splitting hairs but I actually slightly preferred the terminating version. |
||
| assert(false); | ||
| return; | ||
| } | ||
|
|
||
| // Handle keyboard "back" button press | ||
| winrt::KeyboardAccelerator goBack{}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this is the right way to listen for the back action. From what I can find,
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh wow, you're right. I completely missed that in the description and in code. Thanks for linking it again. It's unfortunate official reccomendations stray from the system API, but this makes sense then. I wonder a bit whether we could let developers opt into non system back events, but could see arguments both ways.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment with a link to the docs |
||
| goBack.Key(winrt::VirtualKey::GoBack); | ||
| goBack.Invoked( | ||
| [weakThis]( | ||
| winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { | ||
| if (auto self = weakThis.lock()) { | ||
| args.Handled(self->OnBackRequested()); | ||
| } | ||
| }); | ||
| rootElement.KeyboardAccelerators().Append(goBack); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It also must be reversible upon unload.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a function to remove the keyboard accelerators attached to the XAML root element on uninitialization. |
||
|
|
||
| // Handle Alt+Left keyboard shortcut | ||
| winrt::KeyboardAccelerator altLeft{}; | ||
| altLeft.Key(winrt::VirtualKey::Left); | ||
| altLeft.Invoked( | ||
| [weakThis]( | ||
| winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { | ||
| if (auto self = weakThis.lock()) { | ||
| args.Handled(self->OnBackRequested()); | ||
| } | ||
| }); | ||
| rootElement.KeyboardAccelerators().Append(altLeft); | ||
| altLeft.Modifiers(winrt::VirtualKeyModifiers::Menu); | ||
|
|
||
| // Hide keyboard accelerator tooltips | ||
| rootElement.KeyboardAcceleratorPlacementMode(winrt::KeyboardAcceleratorPlacementMode::Hidden); | ||
| } | ||
|
|
||
| void ReactRootControl::RemoveBackHandlers() noexcept { | ||
| m_backRequestedRevoker.revoke(); | ||
| if (auto rootView = m_weakRootView.get()) { | ||
| if (auto element = rootView.try_as<winrt::UIElement>()) { | ||
| element.KeyboardAccelerators().Clear(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| bool ReactRootControl::OnBackRequested() noexcept { | ||
| if (auto reactInstance = m_weakReactInstance.GetStrongPtr()) { | ||
| query_cast<Mso::React::ILegacyReactInstance &>(*reactInstance) | ||
| .CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| Mso::React::IReactViewHost *ReactRootControl::ReactViewHost() noexcept { | ||
| return m_reactViewHost.Get(); | ||
| } | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We must also detach in the UninitRootView.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a function to remove the keyboard accelerators attached to the XAML root element and the BackRequested listener on uninitialization.