Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
}
25 changes: 25 additions & 0 deletions vnext/ReactUWP/Utils/Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,37 @@ bool IsAPIContractVxAvailable() {
return isAPIContractVxAvailable;
}

bool IsAPIContractV5Available() {
return IsAPIContractVxAvailable<5>();
}

bool IsAPIContractV6Available() {
return IsAPIContractVxAvailable<6>();
}

bool IsAPIContractV7Available() {
return IsAPIContractVxAvailable<7>();
}

bool IsAPIContractV8Available() {
return IsAPIContractVxAvailable<8>();
}

bool IsRS3OrHigher() {
return IsAPIContractV5Available();
}

bool IsRS4OrHigher() {
return IsAPIContractV6Available();
}

bool IsRS5OrHigher() {
return IsAPIContractV7Available();
}

bool Is19H1OrHigher() {
return IsAPIContractV8Available();
}

} // namespace uwp
}; // namespace react
3 changes: 3 additions & 0 deletions vnext/ReactUWP/Utils/Helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ inline typename T asEnum(folly::dynamic const &obj) {
ReactId getViewId(_In_ IReactInstance *instance, winrt::FrameworkElement const &fe);
std::int32_t CountOpenPopups();

bool IsRS3OrHigher();
bool IsRS4OrHigher();
bool IsRS5OrHigher();
bool Is19H1OrHigher();
} // namespace uwp
} // namespace react
10 changes: 6 additions & 4 deletions vnext/ReactUWP/Views/KeyboardEventHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,13 @@ PreviewKeyboardEventHandler::PreviewKeyboardEventHandler(KeyboardEventCallback &

void PreviewKeyboardEventHandler::hook(XamlView xamlView) {
auto uiElement = xamlView.as<winrt::UIElement>();
if (m_keyDownCallback)
m_previewKeyDownRevoker = uiElement.PreviewKeyDown(winrt::auto_revoke, m_keyDownCallback);
if (uiElement.try_as<winrt::IUIElement7>()) {
if (m_keyDownCallback)
m_previewKeyDownRevoker = uiElement.PreviewKeyDown(winrt::auto_revoke, m_keyDownCallback);

if (m_keyUpCallback)
m_previewKeyUpRevoker = uiElement.PreviewKeyUp(winrt::auto_revoke, m_keyUpCallback);
if (m_keyUpCallback)
m_previewKeyUpRevoker = uiElement.PreviewKeyUp(winrt::auto_revoke, m_keyUpCallback);
}
}

void PreviewKeyboardEventHandler::unhook() {
Expand Down
1 change: 1 addition & 0 deletions vnext/ReactUWP/Views/ReactControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ void ReactControl::AttachRoot() noexcept {

m_touchEventHandler->AddTouchHandlers(m_xamlRootView);
m_previewKeyboardEventHandlerOnRoot->hook(m_xamlRootView);
m_SIPEventHandler->AttachView(m_xamlRootView, true /*fireKeyboradEvents*/);

auto initialProps = m_initialProps;
m_reactInstance->AttachMeasuredRootView(m_pParent, std::move(initialProps));
Expand Down
88 changes: 64 additions & 24 deletions vnext/ReactUWP/Views/SIPEventHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <Modules/NativeUIManager.h>

#include <ReactUWP\Utils\Helpers.h>
#include <winrt/Windows.ApplicationModel.Core.h>
#include <winrt/Windows.Foundation.h>

Expand All @@ -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()) {
Copy link
Contributor

@licanhua licanhua Nov 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is19H1OrHigher [](start = 6, length = 14)

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

Copy link
Contributor Author

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)

Copy link
Contributor Author

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)

// 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(
Copy link
Contributor

@licanhua licanhua Nov 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OcclusionsChanged [](start = 50, length = 17)

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());
}
Copy link
Contributor

@licanhua licanhua Nov 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: cppwinrt supports for like this
for (auto const& item: occlusions) {
}

or
for (auto item: occlusions) {
}
I don't know if the first one will work or not #Resolved

}
}
return (winrt::RectHelper::GetIsEmpty(m_finalRect));
}

void SIPEventHandler::SendEvent(std::string &&eventName, folly::dynamic &&parameters) {
Expand Down
15 changes: 14 additions & 1 deletion vnext/ReactUWP/Views/SIPEventHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <winrt/Windows.UI.ViewManagement.Core.h>

namespace winrt {
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::ViewManagement::Core;
} // namespace winrt

Expand All @@ -20,10 +20,23 @@ class SIPEventHandler {
SIPEventHandler(const std::weak_ptr<IReactInstance> &reactInstance);
virtual ~SIPEventHandler();

bool IsSIPShowing() {
return m_isShowing;
}

void AttachView(XamlView xamlView, bool fireKeyboardEvents);
// void TryShow();
void TryHide();

private:
bool IsOcclusionsEmpty(winrt::IVectorView<winrt::CoreInputViewOcclusion> occlusions);
void SendEvent(std::string &&eventName, folly::dynamic &&parameters);
std::weak_ptr<IReactInstance> m_wkReactInstance;
winrt::CoreInputView::OcclusionsChanged_revoker m_occlusionsChanged_revoker;
winrt::Rect m_finalRect;
winrt::CoreInputView m_coreInputView{nullptr};
bool m_isShowing{false};
bool m_fireKeyboradEvents;
};

} // namespace uwp
Expand Down
37 changes: 36 additions & 1 deletion vnext/ReactUWP/Views/ScrollViewManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "pch.h"

#include <ReactUWP\Views\SIPEventHandler.h>
#include <Views/ShadowNodeBase.h>
#include "Impl/ScrollViewUWPImplementation.h"
#include "ScrollViewManager.h"
Expand All @@ -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;
Expand All @@ -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{};
Expand All @@ -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)
Expand Down Expand Up @@ -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();
Copy link
Contributor

@licanhua licanhua Nov 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RegisterSIPEventsWhenNeeded [](start = 8, length = 27)

Do you need to unRegisterSIPEventsWhenNeeded in else? otherwise there is a callback is never be released. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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) {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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));
Expand All @@ -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,
Expand Down Expand Up @@ -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;
}
Expand Down