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,7 @@
{
"type": "none",
"comment": "Implemented OnPressIn event for textinput",
"packageName": "react-native-windows",
"email": "hmalothu@microsoft.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,17 @@ class PressInOutEvents extends React.Component<
super(props);
this.state = {text: 'PressIn/PressOut message'};
}

render() {
return (
<View>
<Text>{this.state.text}</Text>
<Text testID="textinput-state-display">{this.state.text}</Text>
<ExampleTextInput
placeholder="Click inside the box to observe events being fired."
style={[styles.singleLineWithHeightTextInput]}
onPressIn={() =>
this.setState({text: 'Holding down the click/touch'})
}
onPressIn={() => {
this.setState({text: 'Holding down the click/touch'});
}}
onPressOut={() => this.setState({text: 'Released click/touch'})}
testID="textinput-press"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ afterEach(async () => {
await verifyNoErrorLogs();
});

const searchBox = async (input: string) => {
const searchBox = await app.findElementByTestID('example_search');
await app.waitUntil(
async () => {
await searchBox.setValue(input);
return (await searchBox.getText()) === input;
},
{
interval: 1500,
timeout: 5000,
timeoutMsg: `Unable to enter correct search text into test searchbox.`,
},
);
};

describe('TextInput Tests', () => {
test('TextInputs can rewrite characters: Replace Space with Underscore', async () => {
const component = await app.findElementByTestID(
Expand Down Expand Up @@ -179,6 +194,34 @@ describe('TextInput Tests', () => {
},
);
});
test('TextInput triggers onPressIn and updates state text', async () => {
// Scroll the example into view
await searchBox('onPressIn');
const component = await app.findElementByTestID('textinput-press');
await component.waitForDisplayed({timeout: 5000});
const dump = await dumpVisualTree('textinput-press');
expect(dump).toMatchSnapshot();

// Trigger onPressIn (click only)
await component.click();
const stateText = await app.findElementByTestID('textinput-state-display');

await app.waitUntil(
async () => {
const currentText = await stateText.getText();
return currentText === 'Holding down the click/touch';
},
{
timeout: 5000,
timeoutMsg: 'State text not updated after onPressIn.',
},
);
// Assertion
expect(await stateText.getText()).toBe('Holding down the click/touch');
// This step helps avoid UI lock by unfocusing the input
const search = await app.findElementByTestID('example_search');
await search.setValue('');
});
test('TextInputs can have attributed text', async () => {
const component = await app.findElementByTestID('text-input');
await component.waitForDisplayed({timeout: 5000});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,84 @@ exports[`TextInput Tests Text have cursorColor 1`] = `
}
`;

exports[`TextInput Tests TextInput triggers onPressIn and updates state text 1`] = `
{
"Automation Tree": {
"AutomationId": "textinput-press",
"ControlType": 50004,
"HelpText": "Click inside the box to observe events being fired.",
"IsKeyboardFocusable": true,
"LocalizedControlType": "edit",
"Name": "Click inside the box to observe events being fired.",
"TextRangePattern.GetText": "Click inside the box to observe events being fired.",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView",
"_Props": {
"TestId": "textinput-press",
},
},
"Visual Tree": {
"Comment": "textinput-press",
"Offset": "0, 0, 0",
"Size": "916, 30",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
"Size": "1, 1",
"Visual Type": "SpriteVisual",
},
{
"Offset": "1, 0, 0",
"Size": "-2, 1",
"Visual Type": "SpriteVisual",
},
{
"Offset": "-1, 0, 0",
"Size": "1, 1",
"Visual Type": "SpriteVisual",
},
{
"Offset": "-1, 1, 0",
"Size": "1, -2",
"Visual Type": "SpriteVisual",
},
{
"Offset": "-1, -1, 0",
"Size": "1, 1",
"Visual Type": "SpriteVisual",
},
{
"Offset": "1, -1, 0",
"Size": "-2, 1",
"Visual Type": "SpriteVisual",
},
{
"Offset": "0, -1, 0",
"Size": "1, 1",
"Visual Type": "SpriteVisual",
},
{
"Offset": "0, 1, 0",
"Size": "1, -2",
"Visual Type": "SpriteVisual",
},
{
"Brush": {
"Brush Type": "ColorBrush",
"Color": "rgba(0, 0, 0, 255)",
},
"Offset": "0, 0, 0",
"Opacity": 0,
"Size": "0, 0",
"Visual Type": "SpriteVisual",
},
],
},
}
`;

exports[`TextInput Tests TextInputs can autocapitalize: Autocapitalize Characters 1`] = `
{
"Automation Tree": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79941,7 +79941,9 @@ exports[`snapshotAllPages TextInput 37`] = `

exports[`snapshotAllPages TextInput 38`] = `
<View>
<Text>
<Text
testID="textinput-state-display"
>
PressIn/PressOut message
</Text>
<TextInput
Expand Down
8 changes: 8 additions & 0 deletions packages/playground/Samples/textinput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
KeyboardAvoidingView,
ScrollView,
TouchableWithoutFeedback,
Alert,
} from 'react-native';

import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
Expand Down Expand Up @@ -219,6 +220,13 @@ export default class Bootstrap extends React.Component<{}, any> {
{backgroundColor: 'black', color: 'white', marginBottom: 4},
]}
/>
<TextInput
style={styles.input}
placeholder="OnPressIn..."
onPressIn={event => {
Alert.alert('Pressed!');
}}
/>
<TextInput
placeholder="Single line with selection color"
cursorColor="#00FF00"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,24 @@ void WindowsTextInputComponentView::OnPointerPressed(
auto hr = m_textServices->TxSendMessage(msg, static_cast<WPARAM>(wParam), static_cast<LPARAM>(lParam), &lresult);
args.Handled(hr != S_FALSE);
}

// Emits the OnPressIn event
if (m_eventEmitter && !m_comingFromJS) {
auto emitter = std::static_pointer_cast<const facebook::react::WindowsTextInputEventEmitter>(m_eventEmitter);
float offsetX = position.X - m_layoutMetrics.frame.origin.x;
float offsetY = position.Y - m_layoutMetrics.frame.origin.y;
float neutralX = m_layoutMetrics.frame.origin.x;
float neutralY = m_layoutMetrics.frame.origin.y;

facebook::react::PressEvent pressInArgs;
pressInArgs.target = m_tag;
pressInArgs.pagePoint = {position.X, position.Y};
pressInArgs.offsetPoint = {offsetX, offsetY}; //{LocationX,LocationY}
pressInArgs.timestamp = static_cast<double>(pp.Timestamp()) / 1000.0;
pressInArgs.identifier = pp.PointerId();

emitter->onPressIn(pressInArgs);
}
}

void WindowsTextInputComponentView::OnPointerReleased(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ void WindowsTextInputEventEmitter::onContentSizeChange(OnContentSizeChange event
});
}

void WindowsTextInputEventEmitter::onPressIn(PressEvent event) const {
dispatchEvent("textInputPressIn", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
auto nativeEvent = jsi::Object(runtime);
nativeEvent.setProperty(runtime, "target", static_cast<double>(event.target));
nativeEvent.setProperty(runtime, "pageX", event.pagePoint.x);
nativeEvent.setProperty(runtime, "pageY", event.pagePoint.y);
nativeEvent.setProperty(runtime, "locationX", event.offsetPoint.x);
nativeEvent.setProperty(runtime, "locationY", event.offsetPoint.y);
nativeEvent.setProperty(runtime, "timestamp", event.timestamp);
nativeEvent.setProperty(runtime, "identifier", static_cast<double>(event.identifier));
payload.setProperty(runtime, "nativeEvent", nativeEvent);
return payload;
});
}

void WindowsTextInputEventEmitter::onEndEditing(OnEndEditing event) const {
dispatchEvent("textInputEndEditing", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class WindowsTextInputEventEmitter : public ViewEventEmitter {
void onSubmitEditing(OnSubmitEditing value) const;
void onKeyPress(OnKeyPress value) const;
void onContentSizeChange(OnContentSizeChange value) const;
void onPressIn(PressEvent event) const override;
void onEndEditing(OnEndEditing value) const;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,22 @@ void HostPlatformViewEventEmitter::onMouseLeave(PointerEvent const &pointerEvent
dispatchEvent("mouseLeave", std::make_shared<PointerEvent>(pointerEvent), RawEvent::Category::ContinuousStart);
}

#pragma mark - Touch Events

void HostPlatformViewEventEmitter::onPressIn(PressEvent event) const {
dispatchEvent("pressIn", [event](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
auto nativeEvent = jsi::Object(runtime);
nativeEvent.setProperty(runtime, "target", static_cast<double>(event.target));
nativeEvent.setProperty(runtime, "pageX", event.pagePoint.x);
nativeEvent.setProperty(runtime, "pageY", event.pagePoint.y);
nativeEvent.setProperty(runtime, "locationX", event.offsetPoint.x);
nativeEvent.setProperty(runtime, "locationY", event.offsetPoint.y);
nativeEvent.setProperty(runtime, "timestamp", event.timestamp);
nativeEvent.setProperty(runtime, "identifier", event.identifier);
payload.setProperty(runtime, "nativeEvent", nativeEvent);
return payload;
});
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@

namespace facebook::react {

struct PressEvent {
Tag target;
Point pagePoint;
Point offsetPoint;
double timestamp;
int identifier;
};

class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
public:
using BaseViewEventEmitter::BaseViewEventEmitter;
Expand All @@ -26,6 +34,10 @@ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {

void onMouseEnter(PointerEvent const &pointerEvent) const;
void onMouseLeave(PointerEvent const &pointerEvent) const;

#pragma mark - Touch Events

virtual void onPressIn(PressEvent event) const;
};

} // namespace facebook::react
Loading