void TextInputShadowNode::updateProperties()

in vnext/Microsoft.ReactNative/Views/TextInputViewManager.cpp [472:694]


void TextInputShadowNode::updateProperties(winrt::Microsoft::ReactNative::JSValueObject &props) {
  m_updating = true;
  auto control = GetView().as<xaml::Controls::Control>();
  auto textBox = control.try_as<xaml::Controls::TextBox>();
  auto passwordBox = control.try_as<xaml::Controls::PasswordBox>();
  auto hasKeyDownEvents = false;

  auto markDirty = false;
  for (auto &pair : props) {
    const std::string &propertyName = pair.first;
    const auto &propertyValue = pair.second;

    // Applicable properties for both TextBox and PasswordBox
    if (TryUpdateFontProperties(control, propertyName, propertyValue)) {
      markDirty = true;
      continue;
    } else if (TryUpdateCharacterSpacing(control, propertyName, propertyValue)) {
      markDirty = true;
      continue;
    } else if (propertyName == "allowFontScaling") {
      markDirty = true;
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean)
        control.IsTextScaleFactorEnabled(propertyValue.AsBoolean());
      else if (propertyValue.IsNull())
        control.ClearValue(xaml::Controls::Control::IsTextScaleFactorEnabledProperty());
    } else if (propertyName == "clearTextOnFocus") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean)
        m_shouldClearTextOnFocus = propertyValue.AsBoolean();
    } else if (propertyName == "selectTextOnFocus") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean)
        m_shouldSelectTextOnFocus = propertyValue.AsBoolean();
    } else if (propertyName == "mostRecentEventCount") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Double ||
          propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Int64) {
        m_mostRecentEventCount = propertyValue.AsInt32();
      }
    } else if (propertyName == "contextMenuHidden") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean)
        m_contextMenuHidden = propertyValue.AsBoolean();
    } else if (propertyName == "caretHidden") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean) {
        m_hideCaret = propertyValue.AsBoolean();
        HideCaretIfNeeded();
      }
    } else if (propertyName == "secureTextEntry") {
      markDirty = true;
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean) {
        if (propertyValue.AsBoolean()) {
          if (m_isTextBox) {
            xaml::Controls::PasswordBox newPasswordBox;
            ReparentView(newPasswordBox);
            m_isTextBox = false;
            registerEvents();
            control = newPasswordBox.as<xaml::Controls::Control>();
            passwordBox = newPasswordBox;
            if (!m_placeholderTextColor.IsNull()) {
              setPasswordBoxPlaceholderForeground(newPasswordBox, m_placeholderTextColor);
            }
          }
        } else {
          if (!m_isTextBox) {
            xaml::Controls::TextBox newTextBox;
            ReparentView(newTextBox);
            m_isTextBox = true;
            registerEvents();
            control = newTextBox.as<xaml::Controls::Control>();
            textBox = newTextBox;
            if (!m_placeholderTextColor.IsNull()) {
              textBox.PlaceholderForeground(SolidColorBrushFrom(m_placeholderTextColor));
            }
          }
        }
      }
    } else if (propertyName == "maxLength") {
      markDirty = true;
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Double ||
          propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Int64) {
        control.SetValue(
            m_isTextBox ? xaml::Controls::TextBox::MaxLengthProperty()
                        : xaml::Controls::PasswordBox::MaxLengthProperty(),
            winrt::PropertyValue::CreateInt32(propertyValue.AsInt32()));
      } else if (propertyValue.IsNull()) {
        control.ClearValue(
            m_isTextBox ? xaml::Controls::TextBox::MaxLengthProperty()
                        : xaml::Controls::PasswordBox::MaxLengthProperty());
      }
    } else if (propertyName == "placeholder") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::String) {
        control.SetValue(
            m_isTextBox ? xaml::Controls::TextBox::PlaceholderTextProperty()
                        : xaml::Controls::PasswordBox::PlaceholderTextProperty(),
            winrt::PropertyValue::CreateString(asHstring(propertyValue)));
      } else if (propertyValue.IsNull()) {
        control.ClearValue(
            m_isTextBox ? xaml::Controls::TextBox::PlaceholderTextProperty()
                        : xaml::Controls::PasswordBox::PlaceholderTextProperty());
      }
    } else if (propertyName == "selectionColor") {
      if (IsValidColorValue(propertyValue)) {
        control.SetValue(
            m_isTextBox ? xaml::Controls::TextBox::SelectionHighlightColorProperty()
                        : xaml::Controls::PasswordBox::SelectionHighlightColorProperty(),
            SolidColorBrushFrom(propertyValue));
      } else if (propertyValue.IsNull())
        control.ClearValue(
            m_isTextBox ? xaml::Controls::TextBox::SelectionHighlightColorProperty()
                        : xaml::Controls::PasswordBox::SelectionHighlightColorProperty());
    } else if (propertyName == "keyboardType") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::String) {
        auto inputScopeNameVaue = parseKeyboardType(propertyValue, m_isTextBox);
        auto scope = xaml::Input::InputScope();
        auto scopeName = xaml::Input::InputScopeName(inputScopeNameVaue);
        auto names = scope.Names();
        names.Append(scopeName);
        control.SetValue(
            m_isTextBox ? xaml::Controls::TextBox::InputScopeProperty()
                        : xaml::Controls::PasswordBox::InputScopeProperty(),
            scope);
      } else if (propertyValue.IsNull())
        control.ClearValue(
            m_isTextBox ? xaml::Controls::TextBox::InputScopeProperty()
                        : xaml::Controls::PasswordBox::InputScopeProperty());
    } else if (propertyName == "placeholderTextColor") {
      m_placeholderTextColor = nullptr;
      if (textBox.try_as<xaml::Controls::ITextBox6>() && m_isTextBox) {
        if (IsValidColorValue(propertyValue)) {
          m_placeholderTextColor = propertyValue.Copy();
          textBox.PlaceholderForeground(SolidColorBrushFrom(propertyValue));
        } else if (propertyValue.IsNull())
          textBox.ClearValue(xaml::Controls::TextBox::PlaceholderForegroundProperty());
      } else if (m_isTextBox != true && IsValidColorValue(propertyValue)) {
        setPasswordBoxPlaceholderForeground(passwordBox, propertyValue);
      }
    } else if (propertyName == "clearTextOnSubmit") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean)
        m_shouldClearTextOnSubmit = propertyValue.AsBoolean();
    } else if (propertyName == "submitKeyEvents") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Array)
        m_submitKeyEvents = KeyboardHelper::FromJS(propertyValue);
      else if (propertyValue.IsNull())
        m_submitKeyEvents.clear();
    } else if (propertyName == "keyDownEvents") {
      hasKeyDownEvents = propertyValue.ItemCount() > 0;
    } else if (propertyName == "autoFocus") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean)
        m_autoFocus = propertyValue.AsBoolean();
    } else if (propertyName == "editable") {
      if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean) {
        m_isTextBox ? textBox.IsReadOnly(!propertyValue.AsBoolean()) : passwordBox.IsEnabled(propertyValue.AsBoolean());
      } else if (propertyValue.IsNull()) {
        m_isTextBox ? textBox.ClearValue(xaml::Controls::TextBox::IsReadOnlyProperty())
                    : passwordBox.ClearValue(xaml::Controls::Control::IsEnabledProperty());
      }
    } else {
      if (m_isTextBox) { // Applicable properties for TextBox
        if (TryUpdateTextAlignment(textBox, propertyName, propertyValue)) {
          continue;
        } else if (propertyName == "multiline") {
          markDirty = true;
          if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean) {
            const bool isMultiline = propertyValue.AsBoolean();
            textBox.TextWrapping(isMultiline ? xaml::TextWrapping::Wrap : xaml::TextWrapping::NoWrap);
            textBox.AcceptsReturn(isMultiline);
          } else if (propertyValue.IsNull())
            textBox.ClearValue(xaml::Controls::TextBox::TextWrappingProperty());
        } else if (propertyName == "scrollEnabled") {
          if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean &&
              textBox.TextWrapping() == xaml::TextWrapping::Wrap) {
            auto scrollMode =
                propertyValue.AsBoolean() ? xaml::Controls::ScrollMode::Auto : xaml::Controls::ScrollMode::Disabled;
            xaml::Controls::ScrollViewer::SetVerticalScrollMode(textBox, scrollMode);
            xaml::Controls::ScrollViewer::SetHorizontalScrollMode(textBox, scrollMode);
          }
        } else if (propertyName == "selection") {
          if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Object) {
            auto selection = json_type_traits<Selection>::parseJson(propertyValue);
            SetSelection(selection.start, selection.end);
          }
        } else if (propertyName == "spellCheck") {
          if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean)
            textBox.IsSpellCheckEnabled(propertyValue.AsBoolean());
          else if (propertyValue.IsNull())
            textBox.ClearValue(xaml::Controls::TextBox::IsSpellCheckEnabledProperty());
        } else if (propertyName == "text") {
          markDirty = true;
          SetText(propertyValue);
        } else if (propertyName == "autoCapitalize") {
          if (textBox.try_as<xaml::Controls::ITextBox6>()) {
            if (propertyValue.Type() == winrt::Microsoft::ReactNative::JSValueType::String) {
              if (propertyValue.AsString() == "characters") {
                textBox.CharacterCasing(xaml::Controls::CharacterCasing::Upper);
              } else { // anything else turns off autoCap (should be "None" but
                       // we don't support "words"/"senetences" yet)
                textBox.CharacterCasing(xaml::Controls::CharacterCasing::Normal);
              }
            } else if (propertyValue.IsNull())
              textBox.ClearValue(xaml::Controls::TextBox::CharacterCasingProperty());
          }
        }
      } else { // Applicable properties for PasswordBox
        if (propertyName == "text" && !m_isTextBox) {
          markDirty = true;
          SetText(propertyValue);
        }
      }
    }
  }

  if (markDirty) {
    GetViewManager()->MarkDirty(m_tag);
  }

  Super::updateProperties(props);

  // We need to re-register the PreviewKeyDown handler so it is invoked after the ShadowNodeBase handler
  if (hasKeyDownEvents) {
    m_controlPreviewKeyDownRevoker.revoke();
    registerPreviewKeyDown();
  }

  m_updating = false;
  m_initialUpdateComplete = true;
}