-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Support ScrollView keyboardDismissMode #3665
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
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": "Support keyboardDismissMode on-drag for ScrollView", | ||
| "packageName": "react-native-windows", | ||
| "email": "dida@ntdev.microsoft.com", | ||
| "commit": "bfbf63cc3293f15622d069778fd0283c6f84b12b", | ||
| "date": "2019-11-16T00:31:57.734Z" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
|
|
||
| #include <Modules/NativeUIManager.h> | ||
|
|
||
| #include <ReactUWP\Utils\Helpers.h> | ||
| #include <winrt/Windows.ApplicationModel.Core.h> | ||
| #include <winrt/Windows.Foundation.h> | ||
|
|
||
|
|
@@ -20,38 +21,77 @@ namespace react { | |
| namespace uwp { | ||
|
|
||
| SIPEventHandler::SIPEventHandler(const std::weak_ptr<IReactInstance> &reactInstance) | ||
| : m_wkReactInstance(reactInstance) { | ||
| auto coreInputView = winrt::CoreInputView::GetForCurrentView(); | ||
| if (coreInputView) { | ||
| m_occlusionsChanged_revoker = coreInputView.OcclusionsChanged( | ||
| winrt::auto_revoke, [=](auto &&, const winrt::CoreInputViewOcclusionsChangedEventArgs &e) { | ||
| : m_wkReactInstance(reactInstance), m_fireKeyboradEvents(false), m_finalRect(winrt::RectHelper::Empty()){}; | ||
|
|
||
| SIPEventHandler::~SIPEventHandler() { | ||
| m_occlusionsChanged_revoker = {}; | ||
| } | ||
| // keyboardDidHide and keyboardDidShow events works on >= RS3 | ||
| // TryShow and TryHide works on >= RS5 | ||
|
|
||
| void SIPEventHandler::AttachView(XamlView xamlView, bool fireKeyboardEvents) { | ||
| m_fireKeyboradEvents = fireKeyboardEvents; | ||
|
|
||
| if (!IsRS3OrHigher()) { | ||
| return; // CoreInputView is only supported on >= RS3. | ||
| } | ||
|
|
||
| if (Is19H1OrHigher()) { | ||
| // 19H1 and higher supports island scenarios | ||
| auto uiElement(xamlView.as<winrt::UIElement>()); | ||
| m_coreInputView = winrt::CoreInputView::GetForUIContext(uiElement.UIContext()); | ||
| } else { | ||
| m_coreInputView = winrt::CoreInputView::GetForCurrentView(); | ||
| } | ||
|
|
||
| if (m_coreInputView) { | ||
| auto occlusions = m_coreInputView.GetCoreInputViewOcclusions(); | ||
| m_isShowing = !IsOcclusionsEmpty(occlusions); | ||
| m_occlusionsChanged_revoker = m_coreInputView.OcclusionsChanged( | ||
|
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 think this API is introduced since 16299 but we support 15063 #Resolved |
||
| winrt::auto_revoke, [this](auto &&, const winrt::CoreInputViewOcclusionsChangedEventArgs &e) { | ||
| if (!e.Handled()) { | ||
| winrt::Rect finalRect = winrt::RectHelper::Empty(); | ||
| winrt::IVectorView<winrt::CoreInputViewOcclusion> occlusions = e.Occlusions(); | ||
| for (uint32_t i = 0; i < occlusions.Size(); i++) { | ||
| winrt::CoreInputViewOcclusion occlusion = occlusions.GetAt(i); | ||
| if (occlusion.OcclusionKind() == winrt::CoreInputViewOcclusionKind::Docked) { | ||
| finalRect = winrt::RectHelper::Union(finalRect, occlusion.OccludingRect()); | ||
| bool wasShowing = m_isShowing; | ||
| m_isShowing = !IsOcclusionsEmpty(e.Occlusions()); | ||
| if (wasShowing != m_isShowing && m_fireKeyboradEvents) { | ||
| if (!m_isShowing) { | ||
| folly::dynamic params = folly::dynamic::object("screenY", 0)("screenX", 0)("width", 0)("height", 0); | ||
| SendEvent("keyboardDidHide", std::move(params)); | ||
| } else { | ||
| folly::dynamic params = folly::dynamic::object( | ||
| "endCoordinates", | ||
| folly::dynamic::object("screenY", m_finalRect.Y)("screenX", m_finalRect.X)( | ||
| "width", m_finalRect.Width)("height", m_finalRect.Height)); | ||
| SendEvent("keyboardDidShow", std::move(params)); | ||
| } | ||
| } | ||
|
|
||
| if (winrt::RectHelper::GetIsEmpty(finalRect)) { | ||
| folly::dynamic params = folly::dynamic::object("screenY", 0)("screenX", 0)("width", 0)("height", 0); | ||
| SendEvent("keyboardDidHide", std::move(params)); | ||
| } else { | ||
| folly::dynamic params = folly::dynamic::object( | ||
| "endCoordinates", | ||
| folly::dynamic::object("screenY", finalRect.Y)("screenX", finalRect.X)("width", finalRect.Width)( | ||
| "height", finalRect.Height)); | ||
| SendEvent("keyboardDidShow", std::move(params)); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| /* | ||
| void SIPEventHandler::TryShow() { | ||
| if (IsRS5OrHigher() && m_coreInputView && !m_isShowing) { // CoreInputView.TryShow is only avaliable after RS5 | ||
| m_coreInputView.TryShow(); | ||
| } | ||
| } | ||
| */ | ||
|
|
||
| SIPEventHandler::~SIPEventHandler() { | ||
| m_occlusionsChanged_revoker = {}; | ||
| void SIPEventHandler::TryHide() { | ||
| if (IsRS5OrHigher() && m_coreInputView && m_isShowing) { // CoreInputView.TryHide is only avaliable after RS5 | ||
| m_coreInputView.TryHide(); | ||
| } | ||
| } | ||
|
|
||
| bool SIPEventHandler::IsOcclusionsEmpty(winrt::IVectorView<winrt::CoreInputViewOcclusion> occlusions) { | ||
| m_finalRect = winrt::RectHelper::Empty(); | ||
| if (occlusions) { | ||
| for (const auto &occlusion : occlusions) { | ||
| if (occlusion.OcclusionKind() == winrt::CoreInputViewOcclusionKind::Docked) { | ||
| m_finalRect = winrt::RectHelper::Union(m_finalRect, occlusion.OccludingRect()); | ||
| } | ||
|
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. nit: cppwinrt supports for like this or |
||
| } | ||
| } | ||
| return (winrt::RectHelper::GetIsEmpty(m_finalRect)); | ||
| } | ||
|
|
||
| void SIPEventHandler::SendEvent(std::string &&eventName, folly::dynamic &¶meters) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
|
|
||
| #include "pch.h" | ||
|
|
||
| #include <ReactUWP\Views\SIPEventHandler.h> | ||
| #include <Views/ShadowNodeBase.h> | ||
| #include "Impl/ScrollViewUWPImplementation.h" | ||
| #include "ScrollViewManager.h" | ||
|
|
@@ -20,6 +21,7 @@ class ScrollViewShadowNode : public ShadowNodeBase { | |
|
|
||
| public: | ||
| ScrollViewShadowNode(); | ||
| ~ScrollViewShadowNode(); | ||
| void dispatchCommand(int64_t commandId, const folly::dynamic &commandArgs) override; | ||
| void createView() override; | ||
| void updateProperties(const folly::dynamic &&props) override; | ||
|
|
@@ -44,6 +46,10 @@ class ScrollViewShadowNode : public ShadowNodeBase { | |
| bool m_isHorizontal = false; | ||
| bool m_isScrollingEnabled = true; | ||
| bool m_changeViewAfterLoaded = false; | ||
| bool m_dismissKeyboardOnDrag = false; | ||
|
|
||
| std::shared_ptr<SIPEventHandler> m_SIPEventHandler; | ||
| void RegisterSIPEventsWhenNeeded(); | ||
|
|
||
| winrt::FrameworkElement::SizeChanged_revoker m_scrollViewerSizeChangedRevoker{}; | ||
| winrt::FrameworkElement::SizeChanged_revoker m_contentSizeChangedRevoker{}; | ||
|
|
@@ -56,6 +62,10 @@ class ScrollViewShadowNode : public ShadowNodeBase { | |
|
|
||
| ScrollViewShadowNode::ScrollViewShadowNode() {} | ||
|
|
||
| ScrollViewShadowNode::~ScrollViewShadowNode() { | ||
| m_SIPEventHandler.reset(); | ||
| } | ||
|
|
||
| void ScrollViewShadowNode::dispatchCommand(int64_t commandId, const folly::dynamic &commandArgs) { | ||
| const auto scrollViewer = GetView().as<winrt::ScrollViewer>(); | ||
| if (scrollViewer == nullptr) | ||
|
|
@@ -186,6 +196,12 @@ void ScrollViewShadowNode::updateProperties(const folly::dynamic &&reactDiffMap) | |
| if (valid) { | ||
| ScrollViewUWPImplementation(scrollViewer).SnapToEnd(snapToEnd); | ||
| } | ||
| } else if (propertyName == "keyboardDismissMode") { | ||
| m_dismissKeyboardOnDrag = false; | ||
| if (propertyValue.isString()) { | ||
| m_dismissKeyboardOnDrag = (propertyValue.getString() == "on-drag"); | ||
| RegisterSIPEventsWhenNeeded(); | ||
|
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.
Do you need to unRegisterSIPEventsWhenNeeded in else? otherwise there is a callback is never be released. #Resolved
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. It's fine, we tear down event registration in the destructor of keyboardEventHandler, and we release keyboardEventHandler in ~ScrollViewShadowNode. In reply to: 348693046 [](ancestors = 348693046) |
||
| } | ||
| } else if (propertyName == "snapToAlignment") { | ||
| const auto [valid, snapToAlignment] = getPropertyAndValidity(propertyValue, winrt::SnapPointsAlignment::Near); | ||
| if (valid) { | ||
|
|
@@ -236,6 +252,11 @@ void ScrollViewShadowNode::AddHandlers(const winrt::ScrollViewer &scrollViewer) | |
| m_scrollViewerDirectManipulationStartedRevoker = | ||
| scrollViewer.DirectManipulationStarted(winrt::auto_revoke, [this](const auto &sender, const auto &) { | ||
| m_isScrolling = true; | ||
|
|
||
| if (m_dismissKeyboardOnDrag && m_SIPEventHandler) { | ||
| m_SIPEventHandler->TryHide(); | ||
| } | ||
|
|
||
| const auto scrollViewer = sender.as<winrt::ScrollViewer>(); | ||
| EmitScrollEvent( | ||
| scrollViewer, | ||
|
|
@@ -271,6 +292,8 @@ void ScrollViewShadowNode::AddHandlers(const winrt::ScrollViewer &scrollViewer) | |
| m_isScrollingFromInertia = false; | ||
| }); | ||
| m_controlLoadedRevoker = scrollViewer.Loaded(winrt::auto_revoke, [this](const auto &sender, const auto &) { | ||
| RegisterSIPEventsWhenNeeded(); | ||
|
|
||
| if (m_changeViewAfterLoaded) { | ||
| const auto scrollViewer = sender.as<winrt::ScrollViewer>(); | ||
| scrollViewer.ChangeView(nullptr, nullptr, static_cast<float>(m_zoomFactor)); | ||
|
|
@@ -279,6 +302,17 @@ void ScrollViewShadowNode::AddHandlers(const winrt::ScrollViewer &scrollViewer) | |
| }); | ||
| } | ||
|
|
||
| void ScrollViewShadowNode::RegisterSIPEventsWhenNeeded() { | ||
| if (m_dismissKeyboardOnDrag) { | ||
| auto view = GetView(); | ||
| if (winrt::VisualTreeHelper::GetParent(view)) { | ||
| auto wkinstance = GetViewManager()->GetReactInstance(); | ||
| m_SIPEventHandler = std::make_unique<SIPEventHandler>(wkinstance); | ||
| m_SIPEventHandler->AttachView(GetView(), false /*fireKeyboardEvents*/); | ||
| } | ||
| } | ||
| } // namespace uwp | ||
|
|
||
| void ScrollViewShadowNode::EmitScrollEvent( | ||
| const winrt::ScrollViewer &scrollViewer, | ||
| int64_t tag, | ||
|
|
@@ -396,7 +430,8 @@ folly::dynamic ScrollViewManager::GetNativeProps() const { | |
| props.update(folly::dynamic::object("horizontal", "boolean")("scrollEnabled", "boolean")( | ||
| "showsHorizontalScrollIndicator", "boolean")("showsVerticalScrollIndicator", "boolean")( | ||
| "minimumZoomScale", "float")("maximumZoomScale", "float")("zoomScale", "float")("snapToInterval", "float")( | ||
| "snapToOffsets", "array")("snapToAlignment", "number")("snapToStart", "boolean")("snapToEnd", "boolean")); | ||
| "snapToOffsets", "array")("snapToAlignment", "number")("snapToStart", "boolean")("snapToEnd", "boolean")( | ||
| "keyboardDismissMode", "string")); | ||
|
|
||
| return props; | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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.
Suggest you use if (auto uie = try_aswinrt::IUIElement10().
It's possible we don't need to make code change in the future when integrate with winui3. #Resolved
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.
It is there mainly for CoreInputView::GetForUIContext, last heard there is no plan to bring that down level yet.. We can always reevaluate at later time by searching the code where we call the platform check such as Is19H1orHigher..
In reply to: 347710901 [](ancestors = 347710901)
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.
Actually I realized CoreInputView.TryShow/TryHide is not availiable until RS5, so more platform check is needed. And I probably need to fall back to InputPane API on earlier OS.
In reply to: 348209889 [](ancestors = 348209889,347710901)