summaryrefslogtreecommitdiffstats
path: root/widget/uikit/TextInputHandler.mm
diff options
context:
space:
mode:
Diffstat (limited to 'widget/uikit/TextInputHandler.mm')
-rw-r--r--widget/uikit/TextInputHandler.mm254
1 files changed, 254 insertions, 0 deletions
diff --git a/widget/uikit/TextInputHandler.mm b/widget/uikit/TextInputHandler.mm
new file mode 100644
index 0000000000..9a4a6ae226
--- /dev/null
+++ b/widget/uikit/TextInputHandler.mm
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* 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 "TextInputHandler.h"
+
+#import <UIKit/UIKit.h>
+
+#include "mozilla/EventForwards.h"
+#include "mozilla/Logging.h"
+#include "mozilla/MiscEvents.h"
+#include "mozilla/TextEventDispatcher.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/WidgetUtils.h"
+#include "nsIWidget.h"
+#include "nsObjCExceptions.h"
+#include "nsString.h"
+#include "nsWindow.h"
+
+mozilla::LazyLogModule gIMELog("TextInputHandler");
+
+namespace mozilla::widget {
+
+static void GetStringForNSString(const NSString* aSrc, nsAString& aDist) {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+
+ if (!aSrc) {
+ aDist.Truncate();
+ return;
+ }
+
+ aDist.SetLength([aSrc length]);
+ [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting())
+ range:NSMakeRange(0, [aSrc length])];
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}
+
+NS_IMPL_ISUPPORTS(TextInputHandler, TextEventDispatcherListener,
+ nsISupportsWeakReference)
+
+TextInputHandler::TextInputHandler(nsWindow* aWidget)
+ : mWidget(aWidget), mDispatcher(aWidget->GetTextEventDispatcher()) {}
+
+nsresult TextInputHandler::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
+ const IMENotification& aNotification) {
+ return NS_OK;
+}
+
+IMENotificationRequests TextInputHandler::GetIMENotificationRequests() {
+ return IMENotificationRequests();
+}
+
+void TextInputHandler::OnRemovedFrom(
+ TextEventDispatcher* aTextEventDispatcher) {}
+
+void TextInputHandler::WillDispatchKeyboardEvent(
+ TextEventDispatcher* aTextEventDispatcher,
+ WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
+ void* aData) {}
+
+bool TextInputHandler::InsertText(NSString* aText) {
+ nsString str;
+ GetStringForNSString(aText, str);
+
+ MOZ_LOG(gIMELog, LogLevel::Info,
+ ("%p TextInputHandler::InsertText(aText=%s)", this,
+ NS_ConvertUTF16toUTF8(str).get()));
+
+ if (Destroyed()) {
+ return false;
+ }
+
+ if (str.Length() == 1) {
+ char16_t charCode = str[0];
+ if (charCode == 0x0a) {
+ return EmulateKeyboardEvent(NS_VK_RETURN, KEY_NAME_INDEX_Enter, charCode);
+ }
+ if (charCode == 0x08) {
+ return EmulateKeyboardEvent(NS_VK_BACK, KEY_NAME_INDEX_Backspace,
+ charCode);
+ }
+ if (uint32_t keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode)) {
+ return EmulateKeyboardEvent(keyCode, KEY_NAME_INDEX_USE_STRING, charCode);
+ }
+ }
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ RefPtr<nsWindow> widget(mWidget);
+ if (!DispatchKeyDownEvent(NS_VK_PROCESSKEY, KEY_NAME_INDEX_Process, 0,
+ status)) {
+ return false;
+ }
+ if (Destroyed()) {
+ return false;
+ }
+
+ mDispatcher->CommitComposition(status, &str, nullptr);
+ if (widget->Destroyed()) {
+ return false;
+ }
+
+ DispatchKeyUpEvent(NS_VK_PROCESSKEY, KEY_NAME_INDEX_Process, 0, status);
+
+ return true;
+}
+
+bool TextInputHandler::HandleCommand(Command aCommand) {
+ MOZ_LOG(gIMELog, LogLevel::Info,
+ ("%p TextInputHandler::HandleCommand, aCommand=%s", this,
+ ToChar(aCommand)));
+
+ if (Destroyed()) {
+ return false;
+ }
+
+ if (aCommand != Command::DeleteCharBackward) {
+ return false;
+ }
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ if (!DispatchKeyDownEvent(NS_VK_BACK, KEY_NAME_INDEX_Backspace, 0, status)) {
+ return true;
+ }
+ if (Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
+ return true;
+ }
+
+ // TODO: Focus check
+
+ if (!DispatchKeyPressEvent(NS_VK_BACK, KEY_NAME_INDEX_Backspace, 0, status)) {
+ return true;
+ }
+ if (Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
+ return true;
+ }
+
+ // TODO: Focus check
+
+ DispatchKeyUpEvent(NS_VK_BACK, KEY_NAME_INDEX_Backspace, 0, status);
+
+ return true;
+}
+
+static uint32_t ComputeKeyModifiers(uint32_t aCharCode) {
+ if (aCharCode >= 'A' && aCharCode <= 'Z') {
+ return MODIFIER_SHIFT;
+ }
+ return 0;
+}
+
+static void InitKeyEvent(WidgetKeyboardEvent& aEvent, uint32_t aKeyCode,
+ KeyNameIndex aKeyNameIndex, char16_t aCharCode) {
+ aEvent.mKeyCode = aKeyCode;
+ aEvent.mIsRepeat = false;
+ aEvent.mKeyNameIndex = aKeyNameIndex;
+ // TODO(m_kato):
+ // How to get native key? Then, implement NativeKeyToDOM*.h for iOS
+ aEvent.mCodeNameIndex = CODE_NAME_INDEX_UNKNOWN;
+ if (aEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
+ aEvent.mKeyValue = aCharCode;
+ }
+ aEvent.mModifiers = ComputeKeyModifiers(aCharCode);
+ aEvent.mLocation = eKeyLocationStandard;
+ aEvent.mTimeStamp = TimeStamp::Now();
+}
+
+bool TextInputHandler::DispatchKeyDownEvent(uint32_t aKeyCode,
+ KeyNameIndex aKeyNameIndex,
+ char16_t aCharCode,
+ nsEventStatus& aStatus) {
+ MOZ_ASSERT(aKeyCode);
+ MOZ_ASSERT(mWidget);
+
+ WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
+ InitKeyEvent(keydownEvent, aKeyCode, aKeyNameIndex, aCharCode);
+ nsresult rv = mDispatcher->BeginNativeInputTransaction();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("BeginNativeInputTransaction is failed");
+ return false;
+ }
+ return mDispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, aStatus);
+}
+
+bool TextInputHandler::DispatchKeyUpEvent(uint32_t aKeyCode,
+ KeyNameIndex aKeyNameIndex,
+ char16_t aCharCode,
+ nsEventStatus& aStatus) {
+ MOZ_ASSERT(aKeyCode);
+ MOZ_ASSERT(mWidget);
+
+ WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
+ InitKeyEvent(keyupEvent, aKeyCode, aKeyNameIndex, aCharCode);
+ nsresult rv = mDispatcher->BeginNativeInputTransaction();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("BeginNativeInputTransaction is failed");
+ return false;
+ }
+ return mDispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, aStatus);
+}
+
+bool TextInputHandler::DispatchKeyPressEvent(uint32_t aKeyCode,
+ KeyNameIndex aKeyNameIndex,
+ char16_t aCharCode,
+ nsEventStatus& aStatus) {
+ MOZ_ASSERT(aKeyCode);
+ MOZ_ASSERT(mWidget);
+
+ WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
+ InitKeyEvent(keypressEvent, aKeyCode, aKeyNameIndex, aCharCode);
+ nsresult rv = mDispatcher->BeginNativeInputTransaction();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("BeginNativeInputTransaction is failed");
+ return false;
+ }
+ return mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, aStatus);
+}
+
+bool TextInputHandler::EmulateKeyboardEvent(uint32_t aKeyCode,
+ KeyNameIndex aKeyNameIndex,
+ char16_t aCharCode) {
+ MOZ_ASSERT(aCharCode);
+
+ MOZ_LOG(gIMELog, LogLevel::Info,
+ ("%p TextInputHandler::EmulateKeyboardEvent(aKeyCode=%x, "
+ "aKeyNameIndex=%x, aCharCode=%x)",
+ this, aKeyCode, aKeyNameIndex, aCharCode));
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ if (!DispatchKeyDownEvent(aKeyCode, aKeyNameIndex, aCharCode, status)) {
+ return true;
+ }
+ if (Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
+ return true;
+ }
+ // TODO: Focus check
+
+ if (!DispatchKeyPressEvent(aKeyCode, aKeyNameIndex, aCharCode, status)) {
+ return true;
+ }
+ if (Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
+ return true;
+ }
+ // TODO: Focus check
+
+ DispatchKeyUpEvent(aKeyCode, aKeyNameIndex, aCharCode, status);
+ return true;
+}
+
+void TextInputHandler::OnDestroyed() { mWidget = nullptr; }
+
+} // namespace mozilla::widget