in vnext/Microsoft.ReactNative/Views/TextInputViewManager.cpp [190:369]
void TextInputShadowNode::registerEvents() {
auto control = GetView().as<xaml::Controls::Control>();
auto tag = m_tag;
// TextChanged is implemented as async event in Xaml. If Javascript is like
// this:
// onChangeText={text => this.setState({text})}
// And user type 'AB' very fast, then 'B' is possible to be lost in below
// situation.
// Input 'A' -> TextChanged for 'A' -> Javascript processing 'A' -> Input
// becomes 'AB' -> Processing javascript response and set text to 'A'
// TextChanged for 'AB' but textbox.Text is 'A' -> Javascript processing
// 'A'
//
// TextChanging is used to drop the Javascript response of 'A' and expect
// another TextChanged event with correct event count.
if (m_isTextBox) {
m_passwordBoxPasswordChangedRevoker = {};
m_passwordBoxPasswordChangingRevoker = {};
auto textBox = control.as<xaml::Controls::TextBox>();
EnsureUniqueTextFlyoutForXamlIsland(textBox);
m_textBoxTextChangingRevoker = textBox.TextChanging(
winrt::auto_revoke, [=](auto &&, auto &&) { dispatchTextInputChangeEvent(textBox.Text()); });
} else {
m_textBoxTextChangingRevoker = {};
auto passwordBox = control.as<xaml::Controls::PasswordBox>();
EnsureUniqueTextFlyoutForXamlIsland(passwordBox);
if (control.try_as<xaml::Controls::IPasswordBox4>()) {
m_passwordBoxPasswordChangingRevoker = passwordBox.PasswordChanging(
winrt::auto_revoke, [=](auto &&, auto &&) { dispatchTextInputChangeEvent(passwordBox.Password()); });
} else {
m_passwordBoxPasswordChangedRevoker = passwordBox.PasswordChanged(
winrt::auto_revoke, [=](auto &&, auto &&) { dispatchTextInputChangeEvent(passwordBox.Password()); });
}
}
if (m_isTextBox) {
m_passwordBoxContextMenuOpeningRevoker = {};
auto textBox = control.as<xaml::Controls::TextBox>();
m_textBoxContextMenuOpeningRevoker =
textBox.ContextMenuOpening(winrt::auto_revoke, [=](auto &&, xaml::Controls::ContextMenuEventArgs const &e) {
if (m_contextMenuHidden) {
e.Handled(true);
}
});
} else {
m_textBoxContextMenuOpeningRevoker = {};
auto passwordBox = control.as<xaml::Controls::PasswordBox>();
m_passwordBoxContextMenuOpeningRevoker =
passwordBox.ContextMenuOpening(winrt::auto_revoke, [=](auto &&, xaml::Controls::ContextMenuEventArgs const &e) {
if (m_contextMenuHidden) {
e.Handled(true);
}
});
}
m_controlGotFocusRevoker = control.GotFocus(winrt::auto_revoke, [=](auto &&, auto &&) {
if (m_shouldClearTextOnFocus) {
if (m_isTextBox) {
control.as<xaml::Controls::TextBox>().ClearValue(xaml::Controls::TextBox::TextProperty());
} else {
control.as<xaml::Controls::PasswordBox>().ClearValue(xaml::Controls::PasswordBox::PasswordProperty());
}
}
if (m_shouldSelectTextOnFocus) {
if (m_isTextBox) {
control.as<xaml::Controls::TextBox>().SelectAll();
} else {
control.as<xaml::Controls::PasswordBox>().SelectAll();
}
}
HideCaretIfNeeded();
folly::dynamic eventData = folly::dynamic::object("target", tag);
if (!m_updating)
GetViewManager()->GetReactContext().DispatchEvent(tag, "topTextInputFocus", std::move(eventData));
});
m_controlLostFocusRevoker = control.LostFocus(winrt::auto_revoke, [=](auto &&, auto &&) {
folly::dynamic eventDataBlur = folly::dynamic::object("target", tag);
folly::dynamic eventDataEndEditing = {};
if (m_isTextBox) {
eventDataEndEditing =
folly::dynamic::object("target", tag)("text", HstringToDynamic(control.as<xaml::Controls::TextBox>().Text()));
} else {
eventDataEndEditing = folly::dynamic::object("target", tag)(
"text", HstringToDynamic(control.as<xaml::Controls::PasswordBox>().Password()));
}
if (!m_updating) {
GetViewManager()->GetReactContext().DispatchEvent(tag, "topTextInputBlur", std::move(eventDataBlur));
GetViewManager()->GetReactContext().DispatchEvent(tag, "topTextInputEndEditing", std::move(eventDataEndEditing));
}
});
registerPreviewKeyDown();
if (m_isTextBox) {
auto textBox = control.as<xaml::Controls::TextBox>();
m_textBoxSelectionChangedRevoker = textBox.SelectionChanged(winrt::auto_revoke, [=](auto &&, auto &&) {
folly::dynamic selectionData = folly::dynamic::object("start", textBox.SelectionStart())(
"end", textBox.SelectionStart() + textBox.SelectionLength());
folly::dynamic eventData = folly::dynamic::object("target", tag)("selection", std::move(selectionData));
if (!m_updating)
GetViewManager()->GetReactContext().DispatchEvent(tag, "topTextInputSelectionChange", std::move(eventData));
});
}
m_controlSizeChangedRevoker =
control.SizeChanged(winrt::auto_revoke, [=](auto &&, winrt::SizeChangedEventArgs const &args) {
if (m_isTextBox) {
if (control.as<xaml::Controls::TextBox>().TextWrapping() == xaml::TextWrapping::Wrap) {
folly::dynamic contentSizeData =
folly::dynamic::object("width", args.NewSize().Width)("height", args.NewSize().Height);
folly::dynamic eventData = folly::dynamic::object("target", tag)("contentSize", std::move(contentSizeData));
if (!m_updating)
GetViewManager()->GetReactContext().DispatchEvent(
tag, "topTextInputContentSizeChange", std::move(eventData));
}
}
});
m_controlLoadedRevoker = control.Loaded(winrt::auto_revoke, [=](auto &&, auto &&) {
if (m_autoFocus) {
control.Focus(xaml::FocusState::Keyboard);
}
auto contentElement = control.GetTemplateChild(L"ContentElement");
auto textBoxView = contentElement.as<xaml::Controls::ScrollViewer>();
if (textBoxView) {
m_scrollViewerViewChangingRevoker = textBoxView.ViewChanging(
winrt::auto_revoke, [=](auto &&, xaml::Controls::ScrollViewerViewChangingEventArgs const &args) {
if (!m_updating) {
folly::dynamic offsetData = folly::dynamic::object("x", args.FinalView().HorizontalOffset())(
"y", args.FinalView().VerticalOffset());
folly::dynamic eventData = folly::dynamic::object("target", tag)("contentOffset", std::move(offsetData));
GetViewManager()->GetReactContext().DispatchEvent(tag, "topTextInputScroll", std::move(eventData));
}
});
}
HideCaretIfNeeded();
});
if (control.try_as<xaml::IUIElement7>()) {
m_controlCharacterReceivedRevoker = control.CharacterReceived(
winrt::auto_revoke, [=](auto &&, xaml::Input::CharacterReceivedRoutedEventArgs const &args) {
std::string key;
wchar_t s[2] = L" ";
s[0] = args.Character();
key = Microsoft::Common::Unicode::Utf16ToUtf8(s, 1);
if (key.compare("\r") == 0) {
key = "Enter";
} else if (key.compare("\b") == 0) {
key = "Backspace";
}
if (!m_updating) {
folly::dynamic eventData = folly::dynamic::object("target", tag)("key", folly::dynamic(key));
GetViewManager()->GetReactContext().DispatchEvent(tag, "topTextInputKeyPress", std::move(eventData));
}
});
}
control.as<xaml::UIElement>().AddHandler(
xaml::UIElement::PointerPressedEvent(),
winrt::box_value(xaml::Input::PointerEventHandler([=](auto &&, xaml::Input::PointerRoutedEventArgs const &args) {
folly::dynamic eventData = folly::dynamic::object("target", tag);
GetViewManager()->GetReactContext().DispatchEvent(tag, "topTextInputPressIn", std::move(eventData));
})),
true);
control.as<xaml::UIElement>().AddHandler(
xaml::UIElement::PointerReleasedEvent(),
winrt::box_value(xaml::Input::PointerEventHandler([=](auto &&, xaml::Input::PointerRoutedEventArgs const &args) {
folly::dynamic eventData = folly::dynamic::object("target", tag);
GetViewManager()->GetReactContext().DispatchEvent(tag, "topTextInputPressOut", std::move(eventData));
})),
true);
}