From c8ca7e206a3a89caa18f8bb8c123aa535db280e2 Mon Sep 17 00:00:00 2001 From: ZhangYu Date: Sun, 24 Apr 2022 23:50:49 +0800 Subject: [PATCH] Make the input components in the previewer support keyboard input. Signed-off-by: ZhangYu Change-Id: I2a383579a92abd8266b4c71f022120a1cf844434 --- adapter/preview/entrance/BUILD.gn | 16 +- adapter/preview/entrance/ace_ability.cpp | 59 +++++ adapter/preview/entrance/ace_ability.h | 14 +- adapter/preview/entrance/ace_run_args.h | 11 +- .../entrance/clipboard/clipboard_impl.cpp | 58 +++++ .../entrance/clipboard/clipboard_impl.h | 51 ++++ .../clipboard/clipboard_proxy_impl.cpp | 32 +++ .../entrance/clipboard/clipboard_proxy_impl.h | 39 +++ .../editing/text_input_client_mgr.cpp | 238 ++++++++++++++++++ .../entrance/editing/text_input_client_mgr.h | 55 ++++ .../editing/text_input_connection_impl.cpp | 53 ++++ .../editing/text_input_connection_impl.h | 47 ++++ .../entrance/editing/text_input_impl.cpp | 37 +++ .../entrance/editing/text_input_impl.h | 39 +++ .../entrance/editing/text_input_plugin.cpp | 135 ++++++++++ .../entrance/editing/text_input_plugin.h | 53 ++++ adapter/preview/entrance/flutter_ace_view.cpp | 9 + adapter/preview/entrance/flutter_ace_view.h | 3 + frameworks/core/common/ace_view.h | 5 + .../core/common/clipboard/clipboard_proxy.h | 2 +- .../text_field/render_text_field.cpp | 123 ++++++++- .../components/text_field/render_text_field.h | 7 +- frameworks/core/event/key_event.h | 2 +- frameworks/core/pipeline/pipeline_context.cpp | 1 - 24 files changed, 1061 insertions(+), 28 deletions(-) create mode 100644 adapter/preview/entrance/clipboard/clipboard_impl.cpp create mode 100644 adapter/preview/entrance/clipboard/clipboard_impl.h create mode 100644 adapter/preview/entrance/clipboard/clipboard_proxy_impl.cpp create mode 100644 adapter/preview/entrance/clipboard/clipboard_proxy_impl.h create mode 100644 adapter/preview/entrance/editing/text_input_client_mgr.cpp create mode 100644 adapter/preview/entrance/editing/text_input_client_mgr.h create mode 100644 adapter/preview/entrance/editing/text_input_connection_impl.cpp create mode 100644 adapter/preview/entrance/editing/text_input_connection_impl.h create mode 100644 adapter/preview/entrance/editing/text_input_impl.cpp create mode 100644 adapter/preview/entrance/editing/text_input_impl.h create mode 100644 adapter/preview/entrance/editing/text_input_plugin.cpp create mode 100644 adapter/preview/entrance/editing/text_input_plugin.h diff --git a/adapter/preview/entrance/BUILD.gn b/adapter/preview/entrance/BUILD.gn index 07270bc8..c3270fd3 100644 --- a/adapter/preview/entrance/BUILD.gn +++ b/adapter/preview/entrance/BUILD.gn @@ -34,6 +34,12 @@ template("preview_entrance_source") { "ace_application_info.cpp", "ace_container.cpp", "ace_resource_register.cpp", + "clipboard/clipboard_impl.cpp", + "clipboard/clipboard_proxy_impl.cpp", + "editing/text_input_client_mgr.cpp", + "editing/text_input_connection_impl.cpp", + "editing/text_input_impl.cpp", + "editing/text_input_plugin.cpp", "flutter_ace_view.cpp", "subwindow_preview.cpp", ] @@ -48,13 +54,19 @@ template("preview_entrance_source") { if (!is_cross_platform_build) { if (platform == "windows") { defines -= [ "UNICODE" ] - include_dirs = [ "//utils/native/base/include" ] + include_dirs = [ + "//utils/native/base/include", + "//third_party/glfw/include", + ] deps += [ "//base/global/resmgr_standard/frameworks/resmgr:global_resmgr_win(${current_toolchain})" ] cflags_cc += [ "-DNONLS" ] } if (platform == "mac") { - include_dirs = [ "//utils/native/base/include" ] + include_dirs = [ + "//utils/native/base/include", + "//third_party/glfw/include", + ] deps += [ "//base/global/resmgr_standard/frameworks/resmgr:global_resmgr_mac(${current_toolchain})" ] } } diff --git a/adapter/preview/entrance/ace_ability.cpp b/adapter/preview/entrance/ace_ability.cpp index 4a5bd948..3d040599 100644 --- a/adapter/preview/entrance/ace_ability.cpp +++ b/adapter/preview/entrance/ace_ability.cpp @@ -28,6 +28,14 @@ #include "frameworks/core/components/common/painter/flutter_svg_painter.h" #include "third_party/skia/include/core/SkFontMgr.h" #include "third_party/skia/src/ports/SkFontMgr_config_parser.h" +#include "adapter/preview/entrance/editing/text_input_client_mgr.h" +#include "adapter/preview/entrance/clipboard/clipboard_impl.h" +#include "adapter/preview/entrance/clipboard/clipboard_proxy_impl.h" +#include "core/common/clipboard/clipboard_proxy.h" + +#ifdef USE_GLFW_WINDOW +#include "adapter/preview/entrance/editing/text_input_plugin.h" +#endif namespace OHOS::Ace::Platform { @@ -171,6 +179,24 @@ std::unique_ptr AceAbility::CreateInstance(AceRunArgs& runArgs) auto controller = FlutterDesktopCreateWindow( runArgs.deviceWidth, runArgs.deviceHeight, runArgs.windowTitle.c_str(), runArgs.onRender); +#ifdef USE_GLFW_WINDOW + std::unique_ptr textInputPlugin = std::make_unique(); + textInputPlugin->RegisterKeyboardHookCallback((KeyboardHookCallback) AceAbility::DispatchKeyEvent); + textInputPlugin->RegisterCharHookCallback((CharHookCallback) AceAbility::DispatchInputMethodEvent); + FlutterDesktopAddKeyboardHookHandler(controller, std::move(textInputPlugin)); + + auto callbackSetClipboardData = [controller](const std::string& data) { + FlutterDesktopSetClipboardData(controller, data.c_str()); + }; + auto callbackGetClipboardData = [controller]() { + return FlutterDesktopGetClipboardData(controller); + }; + ClipboardProxy::GetInstance()->SetDelegate( + std::make_unique(callbackSetClipboardData, callbackGetClipboardData)); +#endif + // Initial the proxy of Input method + TextInputClientMgr::GetInstance().InitTextInputProxy(); + auto aceAbility = std::make_unique(runArgs); aceAbility->SetGlfwWindowController(controller); return aceAbility; @@ -344,6 +370,39 @@ bool AceAbility::DispatchBackPressedEvent() return backFuture.get(); } +bool AceAbility::DispatchInputMethodEvent(unsigned int code_point) +{ + return TextInputClientMgr::GetInstance().AddCharacter(static_cast(code_point)); +} + +bool AceAbility::DispatchKeyEvent(const KeyEvent& event) +{ + if (TextInputClientMgr::GetInstance().HandleKeyEvent(event)) { + LOGI("The event is related to the input component and has been handled successfully."); + return true; + } + auto container = AceContainer::GetContainerInstance(ACE_INSTANCE_ID); + if (!container) { + LOGE("container is null"); + return false; + } + + auto aceView = container->GetAceView(); + if (!aceView) { + LOGE("aceView is null"); + return false; + } + + bool isHandled; + container->GetTaskExecutor()->PostTask( + [aceView, event, &isHandled]() { + isHandled = aceView->HandleKeyEvent(event); + }, + TaskExecutor::TaskType::UI); + + return isHandled; +} + void AceAbility::SetConfigChanges(const std::string& configChanges) { if (configChanges == "") { diff --git a/adapter/preview/entrance/ace_ability.h b/adapter/preview/entrance/ace_ability.h index 598fa649..45e3e6d0 100644 --- a/adapter/preview/entrance/ace_ability.h +++ b/adapter/preview/entrance/ace_ability.h @@ -22,14 +22,8 @@ #include "adapter/preview/entrance/ace_run_args.h" #include "core/event/touch_event.h" - -#ifndef ACE_PREVIEW_EXPORT -#ifdef _WIN32 -#define ACE_PREVIEW_EXPORT __declspec(dllexport) -#elif defined(__APPLE__) -#define ACE_PREVIEW_EXPORT __attribute__((visibility("default"))) -#endif -#endif // ACE_PREVIEW_EXPORT +#include "core/event/key_event.h" +#include "base/utils/macros.h" namespace OHOS::Ace::Platform { @@ -54,7 +48,7 @@ struct SystemParams { OHOS::Ace::DeviceOrientation orientation { DeviceOrientation::PORTRAIT }; }; -class ACE_PREVIEW_EXPORT AceAbility { +class ACE_FORCE_EXPORT_WITH_PREVIEW AceAbility { public: explicit AceAbility(const AceRunArgs& runArgs); ~AceAbility(); @@ -67,6 +61,8 @@ public: static bool DispatchTouchEvent(const TouchEvent& event); static bool DispatchBackPressedEvent(); + static bool DispatchInputMethodEvent(unsigned int code_point); + static bool DispatchKeyEvent(const KeyEvent& keyEvent); void OnConfigurationChanged(const DeviceConfig& newConfig); void SurfaceChanged( diff --git a/adapter/preview/entrance/ace_run_args.h b/adapter/preview/entrance/ace_run_args.h index 6dd6f430..5ff5ee4b 100644 --- a/adapter/preview/entrance/ace_run_args.h +++ b/adapter/preview/entrance/ace_run_args.h @@ -19,17 +19,10 @@ #include #include +#include "base/utils/macros.h" #include "base/utils/device_type.h" #include "base/utils/device_config.h" -#ifndef ACE_PREVIEW_EXPORT -#ifdef _WIN32 -#define ACE_PREVIEW_EXPORT __declspec(dllexport) -#elif defined(__APPLE__) -#define ACE_PREVIEW_EXPORT __attribute__((visibility("default"))) -#endif -#endif // ACE_PREVIEW_EXPORT - namespace OHOS::Ace::Platform { using SendRenderDataCallback = bool (*)(const void*, const size_t, const int32_t, const int32_t); @@ -49,7 +42,7 @@ enum class ProjectModel { STAGE, }; -struct ACE_PREVIEW_EXPORT AceRunArgs { +struct ACE_FORCE_EXPORT_WITH_PREVIEW AceRunArgs { // the adopted project model ProjectModel projectModel = ProjectModel::FA; // stores routing information diff --git a/adapter/preview/entrance/clipboard/clipboard_impl.cpp b/adapter/preview/entrance/clipboard/clipboard_impl.cpp new file mode 100644 index 00000000..54e5871e --- /dev/null +++ b/adapter/preview/entrance/clipboard/clipboard_impl.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adapter/preview/entrance/clipboard/clipboard_impl.h" + +namespace OHOS::Ace::Platform { + +void ClipboardImpl::SetData(const std::string& data) +{ + if (!taskExecutor_ || !callbackSetClipboardData_) { + LOGE("Failed to set the data to clipboard."); + return; + } + taskExecutor_->PostTask( + [callbackSetClipboardData = callbackSetClipboardData_, data] { + callbackSetClipboardData(data); + }, + TaskExecutor::TaskType::PLATFORM); +} + +void ClipboardImpl::GetData(const std::function& callback, bool syncMode) +{ + if (!taskExecutor_ || !callbackGetClipboardData_ || !callback) { + LOGE("Failed to get the data from clipboard."); + return; + } + taskExecutor_->PostTask( + [callbackGetClipboardData = callbackGetClipboardData_, callback] { + callback(callbackGetClipboardData()); + }, + TaskExecutor::TaskType::PLATFORM); +} + +void ClipboardImpl::Clear() {} + +void ClipboardImpl::RegisterCallbackSetClipboardData(CallbackSetClipboardData callback) +{ + callbackSetClipboardData_ = callback; +} + +void ClipboardImpl::RegisterCallbackGetClipboardData(CallbackGetClipboardData callback) +{ + callbackGetClipboardData_ = callback; +} + +} // namespace OHOS::Ace::Platform diff --git a/adapter/preview/entrance/clipboard/clipboard_impl.h b/adapter/preview/entrance/clipboard/clipboard_impl.h new file mode 100644 index 00000000..17d4e8da --- /dev/null +++ b/adapter/preview/entrance/clipboard/clipboard_impl.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_ACE_ENGINE_ADAPTER_PREVIEW_ENTRANCE_CLIPBOARD_CLIPBOARD_IMPL_H +#define FOUNDATION_ACE_ACE_ENGINE_ADAPTER_PREVIEW_ENTRANCE_CLIPBOARD_CLIPBOARD_IMPL_H + +#include +#include + +#include "base/utils/macros.h" +#include "base/memory/referenced.h" +#include "base/thread/task_executor.h" +#include "core/common/clipboard/clipboard.h" +#include "core/common/clipboard/clipboard_interface.h" + +namespace OHOS::Ace::Platform { + +using CallbackSetClipboardData = std::function< void(const std::string&) >; +using CallbackGetClipboardData = std::function< const std::string(void) >; + +class ACE_FORCE_EXPORT_WITH_PREVIEW ClipboardImpl : public Clipboard { +public: + explicit ClipboardImpl(const RefPtr& taskExecutor) : Clipboard(taskExecutor) {} + ~ClipboardImpl() override = default; + + void SetData(const std::string& data) override; + void GetData(const std::function& callback, bool syncMode = false) override; + void Clear() override; + void RegisterCallbackSetClipboardData(CallbackSetClipboardData callback); + void RegisterCallbackGetClipboardData(CallbackGetClipboardData callback); + +private: + CallbackSetClipboardData callbackSetClipboardData_; + CallbackGetClipboardData callbackGetClipboardData_; +}; + +} // namespace OHOS::Ace::Platform + +#endif // FOUNDATION_ACE_ACE_ENGINE_ADAPTER_PREVIEW_ENTRANCE_CLIPBOARD_CLIPBOARD_IMPL_H diff --git a/adapter/preview/entrance/clipboard/clipboard_proxy_impl.cpp b/adapter/preview/entrance/clipboard/clipboard_proxy_impl.cpp new file mode 100644 index 00000000..39913f26 --- /dev/null +++ b/adapter/preview/entrance/clipboard/clipboard_proxy_impl.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adapter/preview/entrance/clipboard/clipboard_proxy_impl.h" + +namespace OHOS::Ace::Platform { + +ClipboardProxyImpl::ClipboardProxyImpl(CallbackSetClipboardData callbackSetData, + CallbackGetClipboardData callbackGetData) : callbackSetClipboardData_(callbackSetData), + callbackGetClipboardData_(callbackGetData) {} + +RefPtr ClipboardProxyImpl::GetClipboard(const RefPtr& taskExecutor) const +{ + auto clipboard = AceType::MakeRefPtr(taskExecutor); + clipboard->RegisterCallbackSetClipboardData(callbackSetClipboardData_); + clipboard->RegisterCallbackGetClipboardData(callbackGetClipboardData_); + return clipboard; +} + +} // namespace OHOS::Ace::Platform diff --git a/adapter/preview/entrance/clipboard/clipboard_proxy_impl.h b/adapter/preview/entrance/clipboard/clipboard_proxy_impl.h new file mode 100644 index 00000000..5c2442f9 --- /dev/null +++ b/adapter/preview/entrance/clipboard/clipboard_proxy_impl.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_ACE_ENGINE_ADAPTER_PREVIEW_ENTRANCE_CLIPBOARD_CLIPBOARD_PROXY_IMPL_H +#define FOUNDATION_ACE_ACE_ENGINE_ADAPTER_PREVIEW_ENTRANCE_CLIPBOARD_CLIPBOARD_PROXY_IMPL_H + +#include "base/utils/macros.h" +#include "core/common/clipboard/clipboard.h" +#include "adapter/preview/entrance/clipboard/clipboard_impl.h" + +namespace OHOS::Ace::Platform { + +class ACE_FORCE_EXPORT_WITH_PREVIEW ClipboardProxyImpl : public ClipboardInterface { +public: + ClipboardProxyImpl(CallbackSetClipboardData callbackSetData, CallbackGetClipboardData callbackGetData); + ~ClipboardProxyImpl() = default; + + RefPtr GetClipboard(const RefPtr& taskExecutor) const override; + +private: + CallbackSetClipboardData callbackSetClipboardData_; + CallbackGetClipboardData callbackGetClipboardData_; +}; + +} // namespace OHOS::Ace::Platform + +#endif // FOUNDATION_ACE_ACE_ENGINE_ADAPTER_PREVIEW_ENTRANCE_CLIPBOARD_CLIPBOARD_PROXY_IMPL_H diff --git a/adapter/preview/entrance/editing/text_input_client_mgr.cpp b/adapter/preview/entrance/editing/text_input_client_mgr.cpp new file mode 100644 index 00000000..dc9a6fa5 --- /dev/null +++ b/adapter/preview/entrance/editing/text_input_client_mgr.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adapter/preview/entrance/editing/text_input_client_mgr.h" + +#include + +#include "base/utils/string_utils.h" +#include "base/memory/ace_type.h" +#include "core/common/ime/constant.h" +#include "core/common/ime/text_selection.h" +#include "core/common/ime/text_editing_value.h" +#include "core/common/ime/text_input_proxy.h" +#include "adapter/preview/entrance/editing/text_input_impl.h" + +namespace OHOS::Ace::Platform { +namespace { + +const wchar_t UPPER_CASE_A = L'A'; +const wchar_t LOWER_CASE_A = L'a'; +const wchar_t CASE_0 = L'0'; +const std::wstring NUM_SYMBOLS = L")!@#$%^&*("; +const std::map PRINTABEL_SYMBOLS = { + {KeyCode::KEY_GRAVE, L'`'}, + {KeyCode::KEY_MINUS, L'-'}, + {KeyCode::KEY_EQUALS, L'='}, + {KeyCode::KEY_LEFT_BRACKET, L'['}, + {KeyCode::KEY_RIGHT_BRACKET, L']'}, + {KeyCode::KEY_BACKSLASH, L'\\'}, + {KeyCode::KEY_SEMICOLON, L';'}, + {KeyCode::KEY_APOSTROPHE, L'\''}, + {KeyCode::KEY_COMMA, L','}, + {KeyCode::KEY_PERIOD, L'.'}, + {KeyCode::KEY_SLASH, L'/'}, + {KeyCode::KEY_SPACE, L' '}, + {KeyCode::KEY_NUMPAD_DIVIDE, L'/'}, + {KeyCode::KEY_NUMPAD_MULTIPLY, L'*'}, + {KeyCode::KEY_NUMPAD_SUBTRACT, L'-'}, + {KeyCode::KEY_NUMPAD_ADD, L'+'}, + {KeyCode::KEY_NUMPAD_DOT, L'.'}, + {KeyCode::KEY_NUMPAD_COMMA, L','}, + {KeyCode::KEY_NUMPAD_EQUALS, L'='}, +}; + +const std::map SHIFT_PRINTABEL_SYMBOLS = { + {KeyCode::KEY_GRAVE, L'~'}, + {KeyCode::KEY_MINUS, L'_'}, + {KeyCode::KEY_EQUALS, L'+'}, + {KeyCode::KEY_LEFT_BRACKET, L'{'}, + {KeyCode::KEY_RIGHT_BRACKET, L'}'}, + {KeyCode::KEY_BACKSLASH, L'|'}, + {KeyCode::KEY_SEMICOLON, L':'}, + {KeyCode::KEY_APOSTROPHE, L'\"'}, + {KeyCode::KEY_COMMA, L'<'}, + {KeyCode::KEY_PERIOD, L'>'}, + {KeyCode::KEY_SLASH, L'?'}, +}; + +} + +TextInputClientMgr::TextInputClientMgr() : clientId_(IME_CLIENT_ID_NONE), enableCapsLock_(false), + enableNumLock_(true), currentConnection_(nullptr) +{} + +TextInputClientMgr::~TextInputClientMgr() = default; + +void TextInputClientMgr::InitTextInputProxy() +{ + // Initial the proxy of Input method + TextInputProxy::GetInstance().SetDelegate(std::make_unique()); +} + +void TextInputClientMgr::SetClientId(const int32_t clientId) +{ + clientId_ = clientId; +} + +void TextInputClientMgr::ResetClientId() +{ + SetClientId(IME_CLIENT_ID_NONE); +} + +bool TextInputClientMgr::IsValidClientId() const +{ + return clientId_ != IME_CLIENT_ID_NONE; +} + +bool TextInputClientMgr::AddCharacter(const wchar_t wideChar) +{ + LOGI("The unicode of inputed character is: %{public}d.", static_cast(wideChar)); + if (!IsValidClientId()) { + return false; + } + std::wstring appendElement(1, wideChar); + auto textEditingValue = std::make_shared(); + textEditingValue->text = textEditingValue_.GetBeforeSelection() + StringUtils::ToString(appendElement) + + textEditingValue_.GetAfterSelection(); + textEditingValue->UpdateSelection(std::max(textEditingValue_.selection.GetStart(), 0) + appendElement.length()); + SetTextEditingValue(*textEditingValue); + return UpdateEditingValue(textEditingValue); +} + +bool TextInputClientMgr::HandleTextKeyEvent(const KeyEvent& event) +{ + // Only the keys involved in the input component are processed here, and the other keys will be forwarded. + if (!IsValidClientId()) { + return false; + } + + const static size_t maxKeySizes = 2; + wchar_t keyChar; + if (event.pressedCodes.size() == 1) { + auto iterCode = PRINTABEL_SYMBOLS.find(event.code); + if (iterCode != PRINTABEL_SYMBOLS.end()) { + keyChar = iterCode->second; + } else if (KeyCode::KEY_0 <= event.code && event.code <= KeyCode::KEY_9) { + keyChar = static_cast(event.code) - static_cast(KeyCode::KEY_0) + CASE_0; + } else if (KeyCode::KEY_NUMPAD_0 <= event.code && event.code <= KeyCode::KEY_NUMPAD_9) { + if (!enableNumLock_) { + return true; + } + keyChar = static_cast(event.code) - static_cast(KeyCode::KEY_NUMPAD_0) + CASE_0; + } else if (KeyCode::KEY_A <= event.code && event.code <= KeyCode::KEY_Z) { + keyChar = static_cast(event.code) - static_cast(KeyCode::KEY_A); + keyChar += (enableCapsLock_ ? UPPER_CASE_A : LOWER_CASE_A); + } else { + return false; + } + } else if (event.pressedCodes.size() == maxKeySizes && event.pressedCodes[0] == KeyCode::KEY_SHIFT_LEFT) { + auto iterCode = SHIFT_PRINTABEL_SYMBOLS.find(event.code); + if (iterCode != SHIFT_PRINTABEL_SYMBOLS.end()) { + keyChar = iterCode->second; + } else if (KeyCode::KEY_A <= event.code && event.code <= KeyCode::KEY_Z) { + keyChar = static_cast(event.code) - static_cast(KeyCode::KEY_A); + keyChar += (enableCapsLock_ ? LOWER_CASE_A : UPPER_CASE_A); + } else if (KeyCode::KEY_0 <= event.code && event.code <= KeyCode::KEY_9) { + keyChar = NUM_SYMBOLS[static_cast(event.code) - static_cast(KeyCode::KEY_0)]; + } else { + return false; + } + } else { + return false; + } + + if (event.action != KeyAction::DOWN) { + return true; + } + return AddCharacter(keyChar); +} + +bool TextInputClientMgr::HandleKeyEvent(const KeyEvent& event) +{ + if (event.action == KeyAction::DOWN) { + if (event.IsKey({ KeyCode::KEY_CAPS_LOCK })) { + enableCapsLock_ = !enableCapsLock_; + return true; + } else if (event.IsKey({ KeyCode::KEY_NUM_LOCK })) { + enableNumLock_ = !enableNumLock_; + return true; + } + } + + // Process all printable characters. + return HandleTextKeyEvent(event); +} + +void TextInputClientMgr::SetTextEditingValue(const TextEditingValue& textEditingValue) +{ + textEditingValue_ = textEditingValue; +} + +void TextInputClientMgr::SetCurrentConnection(const RefPtr& currentConnection) +{ + currentConnection_ = currentConnection; +} + +bool TextInputClientMgr::IsCurrentConnection(const TextInputConnection* connection) const +{ + return currentConnection_ == connection; +} + +bool TextInputClientMgr::UpdateEditingValue(const std::shared_ptr& value, bool needFireChangeEvent) +{ + if (!currentConnection_ || currentConnection_->GetClientId() != clientId_) { + return false; + } + auto weak = AceType::WeakClaim(AceType::RawPtr(currentConnection_)); + currentConnection_->GetTaskExecutor()->PostTask( + [weak, value, needFireChangeEvent]() { + auto currentConnection = weak.Upgrade(); + if (currentConnection == nullptr) { + LOGE("currentConnection is nullptr"); + return; + } + auto client = currentConnection->GetClient(); + if (client) { + client->UpdateEditingValue(value, needFireChangeEvent); + } + }, + TaskExecutor::TaskType::UI); + return true; +} + +bool TextInputClientMgr::PerformAction(const TextInputAction action) +{ + if (!currentConnection_ || currentConnection_->GetClientId() != clientId_) { + return false; + } + auto weak = AceType::WeakClaim(AceType::RawPtr(currentConnection_)); + currentConnection_->GetTaskExecutor()->PostTask( + [weak, action]() { + auto currentConnection = weak.Upgrade(); + if (currentConnection == nullptr) { + LOGE("currentConnection is nullptr"); + return; + } + auto client = currentConnection->GetClient(); + if (client) { + client->PerformAction(action); + } + }, + TaskExecutor::TaskType::UI); + return true; +} + +} // namespace OHOS::Ace::Platform diff --git a/adapter/preview/entrance/editing/text_input_client_mgr.h b/adapter/preview/entrance/editing/text_input_client_mgr.h new file mode 100644 index 00000000..cc5e817a --- /dev/null +++ b/adapter/preview/entrance/editing/text_input_client_mgr.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_CLIENT_MGR_H +#define FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_CLIENT_MGR_H + +#include "base/memory/referenced.h" +#include "base/utils/singleton.h" +#include "core/event/key_event.h" +#include "core/common/clipboard/clipboard_proxy.h" +#include "core/common/ime/text_input_connection.h" + +namespace OHOS::Ace::Platform { + +class TextInputClientMgr : public Singleton { + DECLARE_SINGLETON(TextInputClientMgr); +public: + void InitTextInputProxy(); + void SetClientId(const int32_t clientId); + // Set the current client id to invalid value. + void ResetClientId(); + bool IsValidClientId() const; + void SetTextEditingValue(const TextEditingValue& textEditingValue); + void SetCurrentConnection(const RefPtr& currentConnection); + bool IsCurrentConnection(const TextInputConnection* connection) const; + bool AddCharacter(const wchar_t wideChar); + bool HandleKeyEvent(const KeyEvent& event); + +private: + // Process all printable characters. If the input method is used, this function is invalid. + bool HandleTextKeyEvent(const KeyEvent& event); + bool PerformAction(const TextInputAction action); + bool UpdateEditingValue(const std::shared_ptr& value, bool needFireChangeEvent = true); + int32_t clientId_; + bool enableCapsLock_; + bool enableNumLock_; + TextEditingValue textEditingValue_; + RefPtr currentConnection_; +}; + +} // namespace OHOS::Ace::Platform + +#endif // FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_CLIENT_MGR_H diff --git a/adapter/preview/entrance/editing/text_input_connection_impl.cpp b/adapter/preview/entrance/editing/text_input_connection_impl.cpp new file mode 100644 index 00000000..9aeb7fe7 --- /dev/null +++ b/adapter/preview/entrance/editing/text_input_connection_impl.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "adapter/preview/entrance/editing/text_input_connection_impl.h" + +#include "adapter/preview/entrance/editing/text_input_client_mgr.h" + +namespace OHOS::Ace::Platform { + +void TextInputConnectionImpl::Show(bool isFocusViewChanged, int32_t instanceId) +{} + +void TextInputConnectionImpl::SetEditingState( + const TextEditingValue& value, int32_t instanceId, bool needFireChangeEvent) +{ + if (taskExecutor_ && Attached()) { + taskExecutor_->PostTask( + [value, instanceId, needFireChangeEvent] { + TextInputClientMgr::GetInstance().SetTextEditingValue(value); + }, + TaskExecutor::TaskType::PLATFORM); + } +} + +void TextInputConnectionImpl::Close(int32_t instanceId) +{ + // Set the current client id to invalid value. + TextInputClientMgr::GetInstance().ResetClientId(); + TextInputClientMgr::GetInstance().SetCurrentConnection(nullptr); + if (Attached()) { + LOGE("Text input connection close failed."); + } +} + +bool TextInputConnectionImpl::Attached() +{ + return TextInputClientMgr::GetInstance().IsCurrentConnection(this); +} + +} // namespace OHOS::Ace::Platform diff --git a/adapter/preview/entrance/editing/text_input_connection_impl.h b/adapter/preview/entrance/editing/text_input_connection_impl.h new file mode 100644 index 00000000..f837cde3 --- /dev/null +++ b/adapter/preview/entrance/editing/text_input_connection_impl.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_CONNECTION_IMPL_H +#define FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_CONNECTION_IMPL_H + +#include "base/memory/ace_type.h" +#include "base/thread/task_executor.h" +#include "core/common/ime/constant.h" +#include "core/common/ime/text_editing_value.h" +#include "core/common/ime/text_input_client.h" +#include "core/common/ime/text_input_connection.h" + +namespace OHOS::Ace::Platform { + +class TextInputConnectionImpl : public TextInputConnection { + DECLARE_ACE_TYPE(TextInputConnectionImpl, TextInputConnection); +public: + TextInputConnectionImpl() = delete; + TextInputConnectionImpl(const WeakPtr&client, const RefPtr& taskExecutor) + : TextInputConnection(client, taskExecutor) + {} + ~TextInputConnectionImpl() override = default; + + void Show(bool isFocusViewChanged, int32_t instanceId) override; + void SetEditingState( + const TextEditingValue& value, int32_t instanceId, bool needFireChangeEvent = true) override; + void Close(int32_t instanceId) override; +private: + bool Attached(); +}; + +} // namespace OHOS::Ace::Platform + +#endif // FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_CONNECTION_IMPL_H diff --git a/adapter/preview/entrance/editing/text_input_impl.cpp b/adapter/preview/entrance/editing/text_input_impl.cpp new file mode 100644 index 00000000..96e4afab --- /dev/null +++ b/adapter/preview/entrance/editing/text_input_impl.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adapter/preview/entrance/editing/text_input_impl.h" + +#include "base/log/log.h" +#include "adapter/preview/entrance/editing/text_input_client_mgr.h" +#include "adapter/preview/entrance/editing/text_input_connection_impl.h" + +namespace OHOS::Ace::Platform { + +RefPtr TextInputImpl::Attach(const WeakPtr& client, + const TextInputConfiguration& config, const RefPtr& taskExecutor, int32_t instanceId) +{ + auto connection = AceType::MakeRefPtr(client, taskExecutor); + TextInputClientMgr::GetInstance().SetCurrentConnection(connection); + taskExecutor->PostTask( + [clientId = connection->GetClientId(), config, instanceId] { + TextInputClientMgr::GetInstance().SetClientId(clientId); + }, + TaskExecutor::TaskType::PLATFORM); + return connection; +} + +} // namespace OHOS::Ace::Platform diff --git a/adapter/preview/entrance/editing/text_input_impl.h b/adapter/preview/entrance/editing/text_input_impl.h new file mode 100644 index 00000000..65f65a09 --- /dev/null +++ b/adapter/preview/entrance/editing/text_input_impl.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_IMPL_H +#define FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_IMPL_H + +#include "base/memory/referenced.h" +#include "base/thread/task_executor.h" +#include "core/common/ime/text_input.h" +#include "core/common/ime/text_input_client.h" +#include "core/common/ime/text_input_configuration.h" +#include "core/common/ime/text_input_connection.h" + +namespace OHOS::Ace::Platform { + +class TextInputImpl : public TextInput { +public: + TextInputImpl() = default; + ~TextInputImpl() override = default; + + RefPtr Attach(const WeakPtr& client, const TextInputConfiguration& config, + const RefPtr& taskExecutor, int32_t instanceId) override; +}; + +} // namespace OHOS::Ace::Platform + +#endif // FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_IMPL_H diff --git a/adapter/preview/entrance/editing/text_input_plugin.cpp b/adapter/preview/entrance/editing/text_input_plugin.cpp new file mode 100644 index 00000000..048e342c --- /dev/null +++ b/adapter/preview/entrance/editing/text_input_plugin.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adapter/preview/entrance/editing/text_input_plugin.h" + +#include +#include "base/log/log.h" + +namespace OHOS::Ace::Platform { +namespace { + +const std::map ACTION_MAP = { + {GLFW_RELEASE, KeyAction::UP}, + {GLFW_PRESS, KeyAction::DOWN}, + {GLFW_REPEAT, KeyAction::LONG_PRESS}, +}; + +const std::map CODE_MAP = { + {GLFW_KEY_BACKSPACE, KeyCode::KEY_FORWARD_DEL}, + {GLFW_KEY_DELETE, KeyCode::KEY_DEL}, + {GLFW_KEY_ESCAPE, KeyCode::KEY_ESCAPE}, + {GLFW_KEY_ENTER, KeyCode::KEY_ENTER}, + {GLFW_KEY_CAPS_LOCK, KeyCode::KEY_CAPS_LOCK}, + {GLFW_KEY_UP, KeyCode::KEY_DPAD_UP}, + {GLFW_KEY_DOWN, KeyCode::KEY_DPAD_DOWN}, + {GLFW_KEY_LEFT, KeyCode::KEY_DPAD_LEFT}, + {GLFW_KEY_RIGHT, KeyCode::KEY_DPAD_RIGHT}, + {GLFW_KEY_GRAVE_ACCENT, KeyCode::KEY_GRAVE}, + {GLFW_KEY_MINUS, KeyCode::KEY_MINUS}, + {GLFW_KEY_EQUAL, KeyCode::KEY_EQUALS}, + {GLFW_KEY_TAB, KeyCode::KEY_TAB}, + {GLFW_KEY_LEFT_BRACKET, KeyCode::KEY_LEFT_BRACKET}, + {GLFW_KEY_RIGHT_BRACKET, KeyCode::KEY_RIGHT_BRACKET}, + {GLFW_KEY_BACKSLASH, KeyCode::KEY_BACKSLASH}, + {GLFW_KEY_SEMICOLON, KeyCode::KEY_SEMICOLON}, + {GLFW_KEY_APOSTROPHE, KeyCode::KEY_APOSTROPHE}, + {GLFW_KEY_COMMA, KeyCode::KEY_COMMA}, + {GLFW_KEY_PERIOD, KeyCode::KEY_PERIOD}, + {GLFW_KEY_SLASH, KeyCode::KEY_SLASH}, + {GLFW_KEY_SPACE, KeyCode::KEY_SPACE}, + {GLFW_KEY_KP_DIVIDE, KeyCode::KEY_NUMPAD_DIVIDE}, + {GLFW_KEY_KP_MULTIPLY, KeyCode::KEY_NUMPAD_MULTIPLY}, + {GLFW_KEY_KP_SUBTRACT, KeyCode::KEY_NUMPAD_SUBTRACT}, + {GLFW_KEY_KP_ADD, KeyCode::KEY_NUMPAD_ADD}, + {GLFW_KEY_KP_ENTER, KeyCode::KEY_NUMPAD_ENTER}, + {GLFW_KEY_KP_EQUAL, KeyCode::KEY_NUMPAD_EQUALS}, + {GLFW_KEY_NUM_LOCK, KeyCode::KEY_NUM_LOCK}, +}; + +} + +void TextInputPlugin::KeyboardHook(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (RecognizeKeyEvent(key, action, mods)) { + keyboardHookCallback_(keyEvent_); + } else { + LOGW("Unrecognized key type."); + } +} + +void TextInputPlugin::CharHook(GLFWwindow* window, unsigned int code_point) +{ + LOGW("Input method is not currently supported"); +} + +void TextInputPlugin::RegisterKeyboardHookCallback(KeyboardHookCallback&& keyboardHookCallback) +{ + if (keyboardHookCallback) { + keyboardHookCallback_ = std::move(keyboardHookCallback); + } +} + +void TextInputPlugin::RegisterCharHookCallback(CharHookCallback&& charHookCallback) +{ + if (charHookCallback) { + charHookCallback_ = std::move(charHookCallback); + } +} + +bool TextInputPlugin::RecognizeKeyEvent(int key, int action, int mods) +{ + auto iterAction = ACTION_MAP.find(action); + if (iterAction == ACTION_MAP.end()) { + return false; + } + keyEvent_.action = iterAction->second; + + keyEvent_.pressedCodes.clear(); + if (mods & GLFW_MOD_CONTROL) { + keyEvent_.pressedCodes.push_back(KeyCode::KEY_CTRL_LEFT); + } + if (mods & GLFW_MOD_SHIFT) { + keyEvent_.pressedCodes.push_back(KeyCode::KEY_SHIFT_LEFT); + } + if (mods & GLFW_MOD_ALT) { + keyEvent_.pressedCodes.push_back(KeyCode::KEY_ALT_LEFT); + } + + auto iterCode = CODE_MAP.find(key); + if (iterCode == CODE_MAP.end() && !(key >= GLFW_KEY_A && key <= GLFW_KEY_Z) && + !(key >= GLFW_KEY_0 && key <= GLFW_KEY_9) && !(key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_9)) { + return false; + } + if (iterCode != CODE_MAP.end()) { + keyEvent_.code = iterCode->second; + } + if (key >= GLFW_KEY_A && key <= GLFW_KEY_Z) { + keyEvent_.code = static_cast(static_cast(KeyCode::KEY_A) + key - GLFW_KEY_A); + } + if (key >= GLFW_KEY_0 && key <= GLFW_KEY_9) { + keyEvent_.code = static_cast(static_cast(KeyCode::KEY_0) + key - GLFW_KEY_0); + } + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_9) { + keyEvent_.code = static_cast(static_cast(KeyCode::KEY_0) + key - GLFW_KEY_KP_0); + } + + keyEvent_.key = KeyToString(static_cast(keyEvent_.code)); + keyEvent_.pressedCodes.push_back(keyEvent_.code); + + return true; +} + +} // namespace OHOS::Ace::Platform diff --git a/adapter/preview/entrance/editing/text_input_plugin.h b/adapter/preview/entrance/editing/text_input_plugin.h new file mode 100644 index 00000000..9bbfba09 --- /dev/null +++ b/adapter/preview/entrance/editing/text_input_plugin.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_PLUGIN_H +#define FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_PLUGIN_H + +#include "flutter/shell/platform/glfw/keyboard_hook_handler.h" +#include "core/event/key_event.h" + +namespace OHOS::Ace::Platform { + +using KeyboardHookCallback = std::function; +using CharHookCallback = std::function; + +class TextInputPlugin : public flutter::KeyboardHookHandler { +public: + TextInputPlugin() = default; + ~TextInputPlugin() override = default; + + // A function for hooking into keyboard input. + void KeyboardHook(GLFWwindow* window, int key, int scancode, int action, int mods) override; + + // A function for hooking into unicode code point input. + void CharHook(GLFWwindow* window, unsigned int code_point) override; + + // Register the dispatch function of the keyboard event + void RegisterKeyboardHookCallback(KeyboardHookCallback&& keyboardHookCallback); + + // Register the dispatch function of the input method event + void RegisterCharHookCallback(CharHookCallback&& charHookCallback); + +private: + bool RecognizeKeyEvent(int key, int action, int mods); + KeyEvent keyEvent_; + CharHookCallback charHookCallback_; + KeyboardHookCallback keyboardHookCallback_; +}; + +} // namespace OHOS::Ace::Platform + +#endif // FOUNDATION_ACE_ADAPTER_PREVIEW_ENTRANCE_EDITING_TEXT_INPUT_PLUGIN_H diff --git a/adapter/preview/entrance/flutter_ace_view.cpp b/adapter/preview/entrance/flutter_ace_view.cpp index 3f327dd5..79d51d5a 100644 --- a/adapter/preview/entrance/flutter_ace_view.cpp +++ b/adapter/preview/entrance/flutter_ace_view.cpp @@ -172,6 +172,15 @@ bool FlutterAceView::HandleTouchEvent(const TouchEvent& touchEvent) return true; } +bool FlutterAceView::HandleKeyEvent(const KeyEvent& keyEvent) +{ + if (!keyEventCallback_) { + return false; + } + + return keyEventCallback_(keyEvent); +} + std::unique_ptr FlutterAceView::GetDrawDelegate() { auto drawDelegate = std::make_unique(); diff --git a/adapter/preview/entrance/flutter_ace_view.h b/adapter/preview/entrance/flutter_ace_view.h index 53d2958b..881762ad 100644 --- a/adapter/preview/entrance/flutter_ace_view.h +++ b/adapter/preview/entrance/flutter_ace_view.h @@ -142,6 +142,9 @@ public: // Use to receive event from pc previewer bool HandleTouchEvent(const TouchEvent& touchEvent) override; + // Use to receive event from pc previewer + bool HandleKeyEvent(const KeyEvent& keyEvent) override; + ViewType GetViewType() const override { return AceView::ViewType::SURFACE_VIEW; diff --git a/frameworks/core/common/ace_view.h b/frameworks/core/common/ace_view.h index 16e540ec..59c415e8 100644 --- a/frameworks/core/common/ace_view.h +++ b/frameworks/core/common/ace_view.h @@ -90,6 +90,11 @@ public: { return false; } + // Use to receive key event from pc previewer + virtual bool HandleKeyEvent(const KeyEvent& keyEvent) + { + return false; + } // Use to get native window handle by texture id virtual const void* GetNativeWindowById(uint64_t textureId) = 0; diff --git a/frameworks/core/common/clipboard/clipboard_proxy.h b/frameworks/core/common/clipboard/clipboard_proxy.h index 76a99b49..d317542a 100644 --- a/frameworks/core/common/clipboard/clipboard_proxy.h +++ b/frameworks/core/common/clipboard/clipboard_proxy.h @@ -21,7 +21,7 @@ namespace OHOS::Ace { -class ACE_EXPORT ClipboardProxy : public ClipboardInterface { +class ACE_EXPORT_WITH_PREVIEW ClipboardProxy : public ClipboardInterface { public: static ClipboardProxy* GetInstance(); ClipboardProxy() = default; diff --git a/frameworks/core/components/text_field/render_text_field.cpp b/frameworks/core/components/text_field/render_text_field.cpp index 55650555..eed04669 100644 --- a/frameworks/core/components/text_field/render_text_field.cpp +++ b/frameworks/core/components/text_field/render_text_field.cpp @@ -947,10 +947,14 @@ const TextEditingValue& RenderTextField::GetPreEditingValue() const return controller_->GetPreValue(); } -void RenderTextField::SetEditingValue(TextEditingValue&& newValue, bool needFireChangeEvent) +void RenderTextField::SetEditingValue(TextEditingValue&& newValue, bool needFireChangeEvent, bool isClearRecords) { if (newValue.text != GetEditingValue().text && needFireChangeEvent) { needNotifyChangeEvent_ = true; + operationRecords_.push_back(newValue); + if (isClearRecords) { + inverseOperationRecords_.clear(); + } } ChangeCounterStyle(newValue); auto context = context_.Upgrade(); @@ -1218,6 +1222,12 @@ bool RenderTextField::OnKeyEvent(const KeyEvent& event) if (event.action == KeyAction::DOWN) { cursorPositionType_ = CursorPositionType::NONE; + if (KeyCode::TV_CONTROL_UP <= event.code && event.code <= KeyCode::TV_CONTROL_RIGHT && ( + event.IsKey({ KeyCode::KEY_SHIFT_LEFT, event.code }) || + event.IsKey({ KeyCode::KEY_SHIFT_RIGHT, event.code }))) { + HandleOnSelect(event.code); + return true; + } if (event.code == KeyCode::TV_CONTROL_LEFT) { CursorMoveLeft(); obscureTickPendings_ = 0; @@ -1238,6 +1248,18 @@ bool RenderTextField::OnKeyEvent(const KeyEvent& event) obscureTickPendings_ = 0; return true; } + if (event.code == KeyCode::KEY_FORWARD_DEL) { + int32_t startPos = GetEditingValue().selection.GetStart(); + int32_t endPos = GetEditingValue().selection.GetEnd(); + Delete(startPos, startPos==endPos ? startPos-1 : endPos); + return true; + } + if (event.code == KeyCode::KEY_DEL) { + int32_t startPos = GetEditingValue().selection.GetStart(); + int32_t endPos = GetEditingValue().selection.GetEnd(); + Delete(startPos, startPos==endPos ? startPos+1 : endPos); + return true; + } } return HandleKeyEvent(event); } @@ -1665,6 +1687,77 @@ void RenderTextField::SetIsOverlayShowed(bool isOverlayShowed, bool needStartTwi } } +void RenderTextField::HandleOnSelect(KeyCode keyCode, CursorMoveSkip skip) +{ + if (skip != CursorMoveSkip::CHARACTER) { + // Not support yet. + LOGE("move skip not support character yet"); + return; + } + + isValueFromRemote_ = false; + auto value = GetEditingValue(); + int32_t startPos = value.selection.GetStart(); + int32_t endPos = value.selection.GetEnd(); + static bool isForwardSelect; + switch (keyCode) { + case KeyCode::KEY_DPAD_LEFT: + if (startPos == endPos) { + isForwardSelect = true; + } + if (isForwardSelect) { + value.UpdateSelection(startPos-1, endPos); + } else { + value.UpdateSelection(startPos, endPos-1); + } + break; + case KeyCode::KEY_DPAD_RIGHT: + if (startPos == endPos) { + isForwardSelect = false; + } + if (isForwardSelect) { + value.UpdateSelection(startPos+1, endPos); + } else { + value.UpdateSelection(startPos, endPos+1); + } + break; + default: + LOGI("Currently only left and right selections are supported."); + return; + } + + SetEditingValue(std::move(value)); + MarkNeedLayout(); +} + +void RenderTextField::HandleOnRevoke() +{ + if (operationRecords_.empty()) { + return; + } + inverseOperationRecords_.push_back(GetEditingValue()); + operationRecords_.pop_back(); + auto value = operationRecords_.back(); + operationRecords_.pop_back(); + isValueFromRemote_ = false; + SetEditingValue(std::move(value), true, false); + cursorPositionType_ = CursorPositionType::NONE; + MarkNeedLayout(); +} + +void RenderTextField::HandleOnInverseRevoke() +{ + if (inverseOperationRecords_.empty()) { + return; + } + auto value = inverseOperationRecords_.back(); + inverseOperationRecords_.pop_back(); + isValueFromRemote_ = false; + SetEditingValue(std::move(value), true, false); + cursorPositionType_ = CursorPositionType::NONE; + MarkNeedLayout(); +} + void RenderTextField::HandleOnCut() { if (!clipboard_) { @@ -1778,11 +1871,25 @@ bool RenderTextField::HandleKeyEvent(const KeyEvent& event) { std::string appendElement; if (event.action == KeyAction::DOWN) { - if (event.IsNumberKey()) { + if (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_NUMPAD_ENTER) { + if (keyboard_ == TextInputType::MULTILINE) { + appendElement = "\n"; + } + } else if (event.IsNumberKey()) { appendElement = event.ConvertCodeToString(); } else if (event.IsLetterKey()) { - if (event.IsKey({ KeyCode::KEY_CTRL_LEFT, KeyCode::KEY_A }) || - event.IsKey({ KeyCode::KEY_CTRL_RIGHT, KeyCode::KEY_A })) { + if (event.IsKey({ KeyCode::KEY_CTRL_LEFT, KeyCode::KEY_SHIFT_LEFT, KeyCode::KEY_Z }) || + event.IsKey({ KeyCode::KEY_CTRL_LEFT, KeyCode::KEY_SHIFT_RIGHT, KeyCode::KEY_Z }) || + event.IsKey({ KeyCode::KEY_CTRL_RIGHT, KeyCode::KEY_SHIFT_LEFT, KeyCode::KEY_Z }) || + event.IsKey({ KeyCode::KEY_CTRL_RIGHT, KeyCode::KEY_SHIFT_RIGHT, KeyCode::KEY_Z }) || + event.IsKey({ KeyCode::KEY_CTRL_LEFT, KeyCode::KEY_Y }) || + event.IsKey({ KeyCode::KEY_CTRL_RIGHT, KeyCode::KEY_Y })) { + HandleOnInverseRevoke(); + } else if (event.IsKey({ KeyCode::KEY_CTRL_LEFT, KeyCode::KEY_Z }) || + event.IsKey({ KeyCode::KEY_CTRL_RIGHT, KeyCode::KEY_Z })) { + HandleOnRevoke(); + } else if (event.IsKey({ KeyCode::KEY_CTRL_LEFT, KeyCode::KEY_A }) || + event.IsKey({ KeyCode::KEY_CTRL_RIGHT, KeyCode::KEY_A })) { HandleOnCopyAll(nullptr); } else if (event.IsKey({ KeyCode::KEY_CTRL_LEFT, KeyCode::KEY_C }) || event.IsKey({ KeyCode::KEY_CTRL_RIGHT, KeyCode::KEY_C })) { @@ -1802,11 +1909,19 @@ bool RenderTextField::HandleKeyEvent(const KeyEvent& event) if (appendElement.empty()) { return false; } +#if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM) + auto editingValue = GetEditingValue(); + editingValue.text = editingValue.GetBeforeSelection() + appendElement + editingValue.GetAfterSelection(); + editingValue.UpdateSelection( + std::max(editingValue.selection.GetEnd(), 0) + StringUtils::Str8ToStr16(appendElement).length()); + SetEditingValue(std::move(editingValue)); +#else auto editingValue = std::make_shared(); editingValue->text = GetEditingValue().GetBeforeSelection() + appendElement + GetEditingValue().GetAfterSelection(); editingValue->UpdateSelection( std::max(GetEditingValue().selection.GetEnd(), 0) + StringUtils::Str8ToStr16(appendElement).length()); UpdateEditingValue(editingValue); +#endif MarkNeedLayout(); return true; } diff --git a/frameworks/core/components/text_field/render_text_field.h b/frameworks/core/components/text_field/render_text_field.h index f4db4647..81ce5cbf 100644 --- a/frameworks/core/components/text_field/render_text_field.h +++ b/frameworks/core/components/text_field/render_text_field.h @@ -353,7 +353,7 @@ protected: void OnLongPress(const LongPressInfo& longPressInfo); bool HandleMouseEvent(const MouseEvent& event) override; - void SetEditingValue(TextEditingValue&& newValue, bool needFireChangeEvent = true); + void SetEditingValue(TextEditingValue&& newValue, bool needFireChangeEvent = true, bool isClearRecords = true); std::u16string GetTextForDisplay(const std::string& text) const; void UpdateStartSelection(int32_t end, const Offset& pos, bool isSingleHandle, bool isLongPress); @@ -525,6 +525,9 @@ private: void UpdatePasswordIcon(const RefPtr& textField); void UpdateOverlay(); void RegisterFontCallbacks(); + void HandleOnSelect(KeyCode keyCode, CursorMoveSkip skip = CursorMoveSkip::CHARACTER); + void HandleOnRevoke(); + void HandleOnInverseRevoke(); void HandleOnCut(); void HandleOnCopy(); void HandleOnPaste(); @@ -618,6 +621,8 @@ private: RefPtr rawRecognizer_; RefPtr pressController_; RefPtr animator_; + std::vector operationRecords_; + std::vector inverseOperationRecords_; #if defined(OHOS_STANDARD_SYSTEM) && !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) bool imeAttached_ = false; #endif diff --git a/frameworks/core/event/key_event.h b/frameworks/core/event/key_event.h index d567d459..a5075544 100644 --- a/frameworks/core/event/key_event.h +++ b/frameworks/core/event/key_event.h @@ -442,7 +442,7 @@ enum class KeyAction : int32_t { constexpr int32_t ASCII_START_UPPER_CASE_LETTER = 65; constexpr int32_t ASCII_START_LOWER_CASE_LETTER = 97; -const char* KeyToString(int32_t code); +ACE_FORCE_EXPORT_WITH_PREVIEW const char* KeyToString(int32_t code); struct KeyEvent final { KeyEvent() diff --git a/frameworks/core/pipeline/pipeline_context.cpp b/frameworks/core/pipeline/pipeline_context.cpp index 2d5c0cbc..a2394495 100644 --- a/frameworks/core/pipeline/pipeline_context.cpp +++ b/frameworks/core/pipeline/pipeline_context.cpp @@ -1594,7 +1594,6 @@ bool PipelineContext::OnKeyEvent(const KeyEvent& event) if (event.action == KeyAction::UP) { SetIsKeyEvent(true); } - return true; } else if (event.code == KeyCode::KEY_ENTER) { if (event.action == KeyAction::CLICK) { SetIsKeyEvent(true);