void TextInputShadowNode::registerEvents()

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);
}