Skip to content
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

Keyboard #3541

Merged
merged 6 commits into from
Oct 29, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
9 changes: 9 additions & 0 deletions change/react-native-windows-2019-10-28-10-44-55-keyboard.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "prerelease",
"comment": "Support Keyboard events",
"packageName": "react-native-windows",
"email": "[email protected]",
"commit": "45a2fb84e8cbe53ad7ea190efab3919124ffb90b",
"date": "2019-10-28T17:44:55.590Z",
"file": "D:\\react2\\react-native-windows\\change\\react-native-windows-2019-10-28-10-44-55-keyboard.json"
}
39 changes: 38 additions & 1 deletion packages/playground/Samples/textinput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,37 @@
*/

import * as React from 'react';
import {AppRegistry, Button, StyleSheet, TextInput, View} from 'react-native';
import {
AppRegistry,
Button,
StyleSheet,
TextInput,
Keyboard,
View,
KeyboardAvoidingView,
} from 'react-native';

export default class Bootstrap extends React.Component<{}, any> {
componentDidMount() {
Keyboard.addListener('keyboardDidShow', this.keyboardDidShow);

Keyboard.addListener('keyboardDidHide', this.keyboardDidHide);
}

keyboardDidShow = () => {
console.log('keyboardDidShow');
};

keyboardDidHide = () => {
console.log('keyboardDidHide');
};

componentWillUnmount() {
Keyboard.removeListener('keyboardDidShow', this.keyboardDidShow);

Keyboard.removeListener('keyboardDidHide', this.keyboardDidHide);
}

state = {
passwordHidden: true,
text: '',
Expand Down Expand Up @@ -82,6 +110,15 @@ export default class Bootstrap extends React.Component<{}, any> {
}
onPress={this.onPressShowPassword}
/>
<KeyboardAvoidingView
style={styles.container}
behavior="padding"
enabled>
<TextInput
style={styles.input}
placeholder={'KeyboardAvoidingView padding'}
/>
</KeyboardAvoidingView>
</View>
);
}
Expand Down
4 changes: 4 additions & 0 deletions vnext/ReactUWP/ReactUWP.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
</Target>
<ItemGroup>
<ClInclude Include="..\include\ReactUWP\InstanceFactory.h" />
<ClInclude Include="..\include\ReactUWP\Utils\RectUtils.h" />
<ClInclude Include="..\include\ReactUWP\Utils\ValueUtils.h" />
<ClInclude Include="..\include\ReactUWP\Utils\AccessibilityUtils.h" />
<ClInclude Include="..\include\ReactUWP\Views\ControlViewManager.h" />
Expand Down Expand Up @@ -252,6 +253,7 @@
<ClInclude Include="Views\RootViewManager.h" />
<ClInclude Include="Views\ScrollContentViewManager.h" />
<ClInclude Include="Views\ScrollViewManager.h" />
<ClInclude Include="Views\SIPEventHandler.h" />
<ClInclude Include="Views\SwitchViewManager.h" />
<ClInclude Include="Views\SliderViewManager.h" />
<ClInclude Include="Views\TextInputViewManager.h" />
Expand All @@ -276,6 +278,7 @@
<ClCompile Include="Modules\Animated\SpringAnimationDriver.cpp" />
<ClCompile Include="Modules\Animated\TrackingAnimatedNode.cpp" />
<ClCompile Include="Threading\BatchingUIMessageQueueThread.cpp" />
<ClCompile Include="Utils\RectUtils.cpp" />
<ClCompile Include="Utils\UwpPreparedScriptStore.cpp" Condition="'$(OSS_RN)' != 'true'" />
<ClCompile Include="Utils\UwpScriptStore.cpp" Condition="'$(OSS_RN)' != 'true'" />
<ClCompile Include="CxxReactUWP\JSBigString.cpp" />
Expand Down Expand Up @@ -353,6 +356,7 @@
<ClCompile Include="Views\RootViewManager.cpp" />
<ClCompile Include="Views\ScrollContentViewManager.cpp" />
<ClCompile Include="Views\ScrollViewManager.cpp" />
<ClCompile Include="Views\SIPEventHandler.cpp" />
<ClCompile Include="Views\SliderViewManager.cpp" />
<ClCompile Include="Views\ShadowNodeBase.cpp" />
<ClCompile Include="Views\SwitchViewManager.cpp" />
Expand Down
117 changes: 117 additions & 0 deletions vnext/ReactUWP/Utils/RectUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "pch.h"

#include <Utils/RectUtils.h>

namespace winrt {
using namespace Windows::Foundation;
} // namespace winrt

namespace react {
namespace uwp {

// Represents positive infinity.
const float PositiveInfinity = std::numeric_limits<float>::infinity();

// Represents negative infinity.
const float NegativeInfinity = -std::numeric_limits<float>::infinity();

bool IsPositiveInfinity(float value) {
return value == PositiveInfinity;
}

bool IsNegativeInfinity(float value) {
return value == NegativeInfinity;
}

// This is a read-only alias for X. If this is the empty rectangle,
// the value will be positive infinity.
float GetLeft(_In_ winrt::Rect rect) {
return rect.X;
}

// This is a read-only alias for Y. If this is the empty rectangle,
// the value will be positive infinity.
float GetTop(_In_ winrt::Rect rect) {
return rect.Y;
}

// This is a read-only alias for X + Width. If this is the empty
// rectangle, the value will be negative infinity.
float GetRight(_In_ winrt::Rect rect) {
return (IsEmptyRect(rect) ? NegativeInfinity : rect.X + rect.Width);
}

// This is a read-only alias for Y + Height. If this is the empty
// rectangle, the value will be negative infinity.
float GetBottom(_In_ winrt::Rect rect) {
return (IsEmptyRect(rect) ? NegativeInfinity : rect.Y + rect.Height);
}

Copy link
Member

@asklar asklar Oct 28, 2019

Choose a reason for hiding this comment

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

not used, remove? #Resolved

REACTWINDOWS_API_(bool) IsEmptyRect(winrt::Rect rect) {
return rect.Width < 0;
Copy link
Member

Choose a reason for hiding this comment

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

< [](start = 20, length = 1)

<= ?

Copy link
Member

Choose a reason for hiding this comment

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

In fact consider reusing user32's IsRectEmpty instead


In reply to: 339777475 [](ancestors = 339777475)

}

REACTWINDOWS_API_(winrt::Rect) CreateEmptyRect() {
winrt::Rect result;
result.X = std::numeric_limits<float>::infinity();
result.Y = std::numeric_limits<float>::infinity();
result.Width = -std::numeric_limits<float>::infinity();
result.Height = -std::numeric_limits<float>::infinity();
Copy link
Member

@asklar asklar Oct 28, 2019

Choose a reason for hiding this comment

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

should set these to 0? #Resolved

return result;
}

// Update this rectangle to be the union of this and rect.
REACTWINDOWS_API_(winrt::Rect) UnionRect(winrt::Rect rect1, _In_ winrt::Rect rect2) {
winrt::Rect result = rect1;
if (IsEmptyRect(rect1)) {
Copy link
Member

@asklar asklar Oct 28, 2019

Choose a reason for hiding this comment

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

can you just call the user32 UnionRect?
UnionRect(ToRECT(rect1), ToRECT(rect2)) #Resolved

result.X = rect2.X;
result.Y = rect2.Y;
result.Width = rect2.Width;
result.Height = rect2.Height;
} else {
if (IsEmptyRect(rect2)) {
float left = 0.0f;
float top = 0.0f;

left = std::min(rect1.X, rect2.X);
top = std::min(rect1.Y, rect2.Y);

// We need this check so that the math does not result in NaN
if (IsPositiveInfinity(rect2.Width) || IsPositiveInfinity(rect1.Width)) {
result.Width = std::numeric_limits<float>::infinity();
} else {
float right1 = GetRight(rect1);
float right2 = GetRight(rect2);
float maxRight = 0.0f;

maxRight = std::max(right1, right1);
result.Width = std::max(maxRight - left, 0.0f);
}

// We need this check so that the math does not result in NaN
if (IsPositiveInfinity(rect2.Height) || IsPositiveInfinity(rect1.Height)) {
result.Height = PositiveInfinity;
} else {
float bottom1 = GetBottom(rect1);
float bottom2 = GetBottom(rect2);
float maxBottom = 0.0f;

// Max with 0 to prevent double weirdness from causing us to be
// (-epsilon..0)
maxBottom = std::max(bottom1, bottom2);
result.Height = std::max(maxBottom - top, 0.0f);
}

result.X = static_cast<FLOAT>(left);
result.Y = static_cast<FLOAT>(top);
}
}

return result;
}

} // namespace uwp
} // namespace react
5 changes: 5 additions & 0 deletions vnext/ReactUWP/Views/ReactControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ void ReactControl::AttachRoot() noexcept {
if (!m_touchEventHandler)
m_touchEventHandler = std::make_shared<TouchEventHandler>(m_reactInstance);

if (!m_SIPEventHandler)
m_SIPEventHandler = std::make_shared<SIPEventHandler>(m_reactInstance);

m_previewKeyboardEventHandlerOnRoot = std::make_shared<PreviewKeyboardEventHandlerOnRoot>(m_reactInstance);

// Register callback from instance for errors
Expand Down Expand Up @@ -268,6 +271,8 @@ void ReactControl::DetachInstance() {

// Clear members with a dependency on the reactInstance
m_touchEventHandler.reset();

m_SIPEventHandler.reset();
}
}

Expand Down
2 changes: 2 additions & 0 deletions vnext/ReactUWP/Views/ReactControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <IReactInstance.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include "IXamlRootView.h"
#include "SIPEventHandler.h"
#include "TouchEventHandler.h"
#include "Views/KeyboardEventHandler.h"

Expand Down Expand Up @@ -75,6 +76,7 @@ class ReactControl : public std::enable_shared_from_this<ReactControl>, public I
std::shared_ptr<facebook::react::NativeModuleProvider> m_moduleProvider;
folly::dynamic m_initialProps;
std::shared_ptr<TouchEventHandler> m_touchEventHandler;
std::shared_ptr<SIPEventHandler> m_SIPEventHandler;
std::shared_ptr<PreviewKeyboardEventHandlerOnRoot> m_previewKeyboardEventHandlerOnRoot;

int64_t m_rootTag = -1;
Expand Down
72 changes: 72 additions & 0 deletions vnext/ReactUWP/Views/SIPEventHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include <pch.h>

#include "SIPEventHandler.h"

#include <Modules/NativeUIManager.h>
#include <Utils/RectUtils.h>

#include <winrt/Windows.ApplicationModel.Core.h>
#include <winrt/Windows.Devices.Input.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Input.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Input.h>
#include <winrt/Windows.UI.Xaml.Media.h>
Copy link
Contributor Author

@ddalp ddalp Oct 28, 2019

Choose a reason for hiding this comment

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

Remove. #Resolved

#include <winrt/Windows.UI.Xaml.h>

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

SIPEventHandler::SIPEventHandler(const std::weak_ptr<IReactInstance> &reactInstance)
: m_wkReactInstance(reactInstance) {
auto coreInputView = winrt::CoreInputView::GetForCurrentView();
if (coreInputView) {
m_occlusionsChnaged_revoker = coreInputView.OcclusionsChanged(
winrt::auto_revoke, [=](auto &&, const winrt::CoreInputViewOcclusionsChangedEventArgs &e) {
if (!e.Handled()) {
winrt::Rect finalRect = CreateEmptyRect();
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 = UnionRect(finalRect, occlusion.OccludingRect());
}
}

if (IsEmptyRect(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));
}
}
});
}
}

SIPEventHandler::~SIPEventHandler() {
m_occlusionsChnaged_revoker = {};
}

void SIPEventHandler::SendEvent(std::string &&eventName, folly::dynamic &&parameters) {
if (auto instance = m_wkReactInstance.lock()) {
instance->CallJsFunction(
"RCTDeviceEventEmitter", "emit", folly::dynamic::array(std::move(eventName), std::move(parameters)));
}
}
} // namespace uwp
} // namespace react
31 changes: 31 additions & 0 deletions vnext/ReactUWP/Views/SIPEventHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once
#include <folly/dynamic.h>

#include <IReactInstance.h>
#include <winrt/Windows.UI.ViewManagement.Core.h>

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

namespace react {
namespace uwp {

class SIPEventHandler {
public:
SIPEventHandler(const std::weak_ptr<IReactInstance> &reactInstance);
virtual ~SIPEventHandler();

private:
void SendEvent(std::string &&eventName, folly::dynamic &&parameters);
std::weak_ptr<IReactInstance> m_wkReactInstance;
winrt::CoreInputView::OcclusionsChanged_revoker m_occlusionsChnaged_revoker;
Copy link
Member

@asklar asklar Oct 28, 2019

Choose a reason for hiding this comment

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

m_occlusionsChnaged_revoker [](start = 50, length = 27)

fix typo: Ch n aged #Resolved

};

} // namespace uwp
} // namespace react
18 changes: 18 additions & 0 deletions vnext/include/ReactUWP/Utils/RectUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

#include <ReactWindowsCore/ReactWindowsAPI.h>
#include <winrt/Windows.Foundation.h>

namespace react {
namespace uwp {

REACTWINDOWS_API_(bool) IsEmptyRect(winrt::Windows::Foundation::Rect rect);
REACTWINDOWS_API_(winrt::Windows::Foundation::Rect) CreateEmptyRect();
REACTWINDOWS_API_(winrt::Windows::Foundation::Rect)
UnionRect(winrt::Windows::Foundation::Rect rect1, _In_ winrt::Windows::Foundation::Rect rect2);
Copy link
Member

@asklar asklar Oct 28, 2019

Choose a reason for hiding this comment

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

when passing Rect pass instead const Rect& #Resolved

Copy link
Contributor Author

@ddalp ddalp Oct 28, 2019

Choose a reason for hiding this comment

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

There is already RectHelper class I can use, will remove . #Resolved


} // namespace uwp
} // namespace react