From 264ed627f1ccff5e5b7c617ff4fe524d5eab1c8d Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Mon, 4 Mar 2024 21:03:26 +0000 Subject: [PATCH] Bug 1847585 - Implement keyboardType, returnKeyType and autocapitalizationType. r=masayuki Change virtual keyboard laybout via HTML elements and attributes. Differential Revision: https://phabricator.services.mozilla.com/D185574 --- widget/IMEData.h | 14 +++--- widget/uikit/UIKitUtils.h | 23 ++++++++++ widget/uikit/UIKitUtils.mm | 89 ++++++++++++++++++++++++++++++++++++++ widget/uikit/moz.build | 1 + widget/uikit/nsWindow.mm | 45 ++++++++++++++++++- 5 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 widget/uikit/UIKitUtils.h create mode 100644 widget/uikit/UIKitUtils.mm diff --git a/widget/IMEData.h b/widget/IMEData.h index 7fb124133393..1ea33e18b9e9 100644 --- a/widget/IMEData.h +++ b/widget/IMEData.h @@ -451,18 +451,20 @@ struct InputContext final { bool IsInputAttributeChanged(const InputContext& aOldContext) const { return mIMEState.mEnabled != aOldContext.mIMEState.mEnabled || -#if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN) +#if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN) || \ + defined(XP_IOS) // input type and inputmode are supported by Windows IME API, GTK - // IME API and Android IME API + // IME API, Android IME API and iOS API. mHTMLInputType != aOldContext.mHTMLInputType || mHTMLInputMode != aOldContext.mHTMLInputMode || #endif -#if defined(ANDROID) || defined(MOZ_WIDGET_GTK) - // autocapitalize is supported by Android IME API and GTK IME API +#if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_IOS) + // autocapitalize is supported by Android IME API, GTK IME API, and + // iOS API mAutocapitalize != aOldContext.mAutocapitalize || #endif -#if defined(ANDROID) - // enterkeyhint is only supported by Android IME API. +#if defined(ANDROID) || defined(XP_IOS) + // enterkeyhint is only supported by Android IME API and iOS API. mActionHint != aOldContext.mActionHint || #endif false; diff --git a/widget/uikit/UIKitUtils.h b/widget/uikit/UIKitUtils.h new file mode 100644 index 000000000000..2e34b126fd1e --- /dev/null +++ b/widget/uikit/UIKitUtils.h @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#import + +#include "IMEData.h" + +namespace mozilla::widget { + +class UIKitUtils final { + public: + UIKitUtils() = delete; + ~UIKitUtils() = delete; + + static UIKeyboardType GetUIKeyboardType(const InputContext& aContext); + static UIReturnKeyType GetUIReturnKeyType(const InputContext& aContext); + static UITextAutocapitalizationType GetUITextAutocapitalizationType( + const InputContext& aContext); +}; + +} // namespace mozilla::widget diff --git a/widget/uikit/UIKitUtils.mm b/widget/uikit/UIKitUtils.mm new file mode 100644 index 000000000000..ddcd2312b022 --- /dev/null +++ b/widget/uikit/UIKitUtils.mm @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "UIKitUtils.h" + +namespace mozilla::widget { + +// static +UIKeyboardType UIKitUtils::GetUIKeyboardType(const InputContext& aContext) { + if (aContext.mHTMLInputMode.EqualsLiteral("email")) { + return UIKeyboardTypeEmailAddress; + } + if (aContext.mHTMLInputMode.EqualsLiteral("deciaml")) { + return UIKeyboardTypeDecimalPad; + } + if (aContext.mHTMLInputMode.EqualsLiteral("numeric")) { + return UIKeyboardTypeNumberPad; + } + if (aContext.mHTMLInputMode.EqualsLiteral("search")) { + return UIKeyboardTypeWebSearch; + } + if (aContext.mHTMLInputMode.EqualsLiteral("tel")) { + return UIKeyboardTypePhonePad; + } + if (aContext.mHTMLInputMode.EqualsLiteral("url")) { + return UIKeyboardTypeURL; + } + + if (aContext.mHTMLInputType.EqualsLiteral("email")) { + return UIKeyboardTypeEmailAddress; + } + if (aContext.mHTMLInputType.EqualsLiteral("number")) { + return UIKeyboardTypeNumberPad; + } + if (aContext.mHTMLInputType.EqualsLiteral("tel")) { + return UIKeyboardTypePhonePad; + } + if (aContext.mHTMLInputType.EqualsLiteral("url")) { + return UIKeyboardTypeURL; + } + + return UIKeyboardTypeDefault; +} + +// static +UIReturnKeyType UIKitUtils::GetUIReturnKeyType(const InputContext& aContext) { + if (aContext.mActionHint.EqualsLiteral("done")) { + return UIReturnKeyDone; + } + if (aContext.mActionHint.EqualsLiteral("go")) { + return UIReturnKeyGo; + } + if (aContext.mActionHint.EqualsLiteral("next") || + aContext.mActionHint.EqualsLiteral("maybenext")) { + return UIReturnKeyNext; + } + if (aContext.mActionHint.EqualsLiteral("search")) { + return UIReturnKeySearch; + } + if (aContext.mActionHint.EqualsLiteral("send")) { + return UIReturnKeySend; + } + + return UIReturnKeyDefault; +} + +// static +UITextAutocapitalizationType UIKitUtils::GetUITextAutocapitalizationType( + const InputContext& aContext) { + if (aContext.mAutocapitalize.EqualsLiteral("characters")) { + return UITextAutocapitalizationTypeAllCharacters; + } + if (aContext.mAutocapitalize.EqualsLiteral("none")) { + return UITextAutocapitalizationTypeNone; + } + if (aContext.mAutocapitalize.EqualsLiteral("sentences")) { + return UITextAutocapitalizationTypeSentences; + } + if (aContext.mAutocapitalize.EqualsLiteral("words")) { + return UITextAutocapitalizationTypeWords; + } + // TODO(m_kato): + // Infer autocapitalization type by input type like GeckoView. + return UITextAutocapitalizationTypeNone; +} + +} // namespace mozilla::widget diff --git a/widget/uikit/moz.build b/widget/uikit/moz.build index 45bc05db3c5a..dc511336eeb3 100644 --- a/widget/uikit/moz.build +++ b/widget/uikit/moz.build @@ -17,6 +17,7 @@ SOURCES += [ "nsWidgetFactory.mm", "nsWindow.mm", "TextInputHandler.mm", + "UIKitUtils.mm", ] include("/ipc/chromium/chromium-config.mozbuild") diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm index e56a8b99d68b..844642967d4c 100644 --- a/widget/uikit/nsWindow.mm +++ b/widget/uikit/nsWindow.mm @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #import +#import #import #import #import @@ -31,6 +32,7 @@ #include "nsRegion.h" #include "nsTArray.h" #include "TextInputHandler.h" +#include "UIKitUtils.h" #include "mozilla/BasicEvents.h" #include "mozilla/ProfilerLabels.h" @@ -43,6 +45,7 @@ using namespace mozilla::gfx; using namespace mozilla::layers; using mozilla::dom::Touch; +using mozilla::widget::UIKitUtils; #define ALOG(args...) \ fprintf(stderr, args); \ @@ -492,6 +495,40 @@ - (BOOL)hasText { return YES; } +// UITextInputTraits + +- (UIKeyboardType)keyboardType { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return UIKeyboardTypeDefault; + } + return UIKitUtils::GetUIKeyboardType(mGeckoChild->GetInputContext()); +} + +- (UIReturnKeyType)returnKeyType { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return UIReturnKeyDefault; + } + return UIKitUtils::GetUIReturnKeyType(mGeckoChild->GetInputContext()); +} + +- (UITextAutocapitalizationType)autocapitalizationType { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return UITextAutocapitalizationTypeNone; + } + return UIKitUtils::GetUITextAutocapitalizationType( + mGeckoChild->GetInputContext()); +} + +- (BOOL)isSecureTextEntry { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return NO; + } + if (mGeckoChild->GetInputContext().IsPasswordEditor()) { + return YES; + } + return NO; +} + @end nsWindow::nsWindow() @@ -783,6 +820,9 @@ - (BOOL)hasText { const InputContextAction& aAction) { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; + const bool changingEnabledState = + aContext.IsInputAttributeChanged(mInputContext); + mInputContext = aContext; if (IsVirtualKeyboardDisabled()) { @@ -792,7 +832,10 @@ - (BOOL)hasText { [mNativeView becomeFirstResponder]; - if (aAction.UserMightRequestOpenVKB()) { + if (aAction.UserMightRequestOpenVKB() || changingEnabledState) { + // TODO(m_kato): + // It is unnecessary to call reloadInputViews with changingEnabledState if + // virtual keyboard is disappeared. [mNativeView reloadInputViews]; }