From 474e6f121eec162e43d30ff1938fa3e606add625 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Fri, 15 May 2020 14:59:56 +1200 Subject: [PATCH] Remove rapidjson from TextInputModel (#18397) This allows this class to be shared with the Linux plugin. Also move client_id outside this class as was noted in a TODO. --- shell/platform/common/cpp/text_input_model.cc | 74 ++----------------- shell/platform/common/cpp/text_input_model.h | 21 ++++-- shell/platform/glfw/text_input_plugin.cc | 51 +++++++++++-- shell/platform/glfw/text_input_plugin.h | 5 ++ shell/platform/windows/text_input_plugin.cc | 51 +++++++++++-- shell/platform/windows/text_input_plugin.h | 4 + 6 files changed, 121 insertions(+), 85 deletions(-) diff --git a/shell/platform/common/cpp/text_input_model.cc b/shell/platform/common/cpp/text_input_model.cc index b8377ede8ec..0a92dc7fdc7 100644 --- a/shell/platform/common/cpp/text_input_model.cc +++ b/shell/platform/common/cpp/text_input_model.cc @@ -8,26 +8,6 @@ #include #include -// TODO(awdavies): Need to fix this regarding issue #47. -static constexpr char kComposingBaseKey[] = "composingBase"; - -static constexpr char kComposingExtentKey[] = "composingExtent"; - -static constexpr char kSelectionAffinityKey[] = "selectionAffinity"; -static constexpr char kAffinityDownstream[] = "TextAffinity.downstream"; - -static constexpr char kSelectionBaseKey[] = "selectionBase"; -static constexpr char kSelectionExtentKey[] = "selectionExtent"; - -static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional"; - -static constexpr char kTextKey[] = "text"; - -// Input client configuration keys. -static constexpr char kTextInputAction[] = "inputAction"; -static constexpr char kTextInputType[] = "inputType"; -static constexpr char kTextInputTypeName[] = "name"; - #if defined(_MSC_VER) // TODO(naifu): This temporary code is to solve link error.(VS2015/2017) // https://social.msdn.microsoft.com/Forums/vstudio/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error @@ -49,28 +29,12 @@ bool IsTrailingSurrogate(char32_t code_point) { } // namespace -TextInputModel::TextInputModel(int client_id, const rapidjson::Value& config) - : client_id_(client_id), +TextInputModel::TextInputModel(const std::string& input_type, + const std::string& input_action) + : input_type_(input_type), + input_action_(input_action), selection_base_(text_.begin()), - selection_extent_(text_.begin()) { - // TODO: Improve error handling during refactoring; this is just minimal - // checking to avoid asserts since RapidJSON is stricter than jsoncpp. - if (config.IsObject()) { - auto input_action = config.FindMember(kTextInputAction); - if (input_action != config.MemberEnd() && input_action->value.IsString()) { - input_action_ = input_action->value.GetString(); - } - auto input_type_info = config.FindMember(kTextInputType); - if (input_type_info != config.MemberEnd() && - input_type_info->value.IsObject()) { - auto input_type = input_type_info->value.FindMember(kTextInputTypeName); - if (input_type != input_type_info->value.MemberEnd() && - input_type->value.IsString()) { - input_type_ = input_type->value.GetString(); - } - } - } -} + selection_extent_(text_.begin()) {} TextInputModel::~TextInputModel() = default; @@ -192,34 +156,10 @@ bool TextInputModel::MoveCursorBack() { return false; } -std::unique_ptr TextInputModel::GetState() const { - // TODO(stuartmorgan): Move client_id out up to the plugin so that this - // function just returns the editing state. - auto args = std::make_unique(rapidjson::kArrayType); - auto& allocator = args->GetAllocator(); - args->PushBack(client_id_, allocator); - - rapidjson::Value editing_state(rapidjson::kObjectType); - // TODO(awdavies): Most of these are hard-coded for now. - editing_state.AddMember(kComposingBaseKey, -1, allocator); - editing_state.AddMember(kComposingExtentKey, -1, allocator); - editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream, - allocator); - editing_state.AddMember(kSelectionBaseKey, - static_cast(selection_base_ - text_.begin()), - allocator); - editing_state.AddMember(kSelectionExtentKey, - static_cast(selection_extent_ - text_.begin()), - allocator); - editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator); +std::string TextInputModel::GetText() const { std::wstring_convert, char16_t> utf8_converter; - editing_state.AddMember( - kTextKey, - rapidjson::Value(utf8_converter.to_bytes(text_), allocator).Move(), - allocator); - args->PushBack(editing_state, allocator); - return args; + return utf8_converter.to_bytes(text_); } } // namespace flutter diff --git a/shell/platform/common/cpp/text_input_model.h b/shell/platform/common/cpp/text_input_model.h index 5aeda0af4b3..ac6cf8d2d82 100644 --- a/shell/platform/common/cpp/text_input_model.h +++ b/shell/platform/common/cpp/text_input_model.h @@ -8,15 +8,14 @@ #include #include -#include "rapidjson/document.h" - namespace flutter { // Handles underlying text input state, using a simple ASCII model. // // Ignores special states like "insert mode" for now. class TextInputModel { public: - TextInputModel(int client_id, const rapidjson::Value& config); + TextInputModel(const std::string& input_type, + const std::string& input_action); virtual ~TextInputModel(); // Attempts to set the text state. @@ -78,11 +77,18 @@ class TextInputModel { // Returns true if the cursor could be moved. void MoveCursorToEnd(); - // Returns the state in the form of a platform message. - std::unique_ptr GetState() const; + // Get the current text + std::string GetText() const; - // Id of the text input client. - int client_id() const { return client_id_; } + // The position of the cursor + int selection_base() const { + return static_cast(selection_base_ - text_.begin()); + } + + // The end of the selection + int selection_extent() const { + return static_cast(selection_extent_ - text_.begin()); + } // Keyboard type of the client. See available options: // https://docs.flutter.io/flutter/services/TextInputType-class.html @@ -96,7 +102,6 @@ class TextInputModel { void DeleteSelected(); std::u16string text_; - int client_id_; std::string input_type_; std::string input_action_; std::u16string::iterator selection_base_; diff --git a/shell/platform/glfw/text_input_plugin.cc b/shell/platform/glfw/text_input_plugin.cc index f95d0d05c81..484c6c8dd46 100644 --- a/shell/platform/glfw/text_input_plugin.cc +++ b/shell/platform/glfw/text_input_plugin.cc @@ -21,9 +21,16 @@ static constexpr char kUpdateEditingStateMethod[] = "TextInputClient.updateEditingState"; static constexpr char kPerformActionMethod[] = "TextInputClient.performAction"; +static constexpr char kTextInputAction[] = "inputAction"; +static constexpr char kTextInputType[] = "inputType"; +static constexpr char kTextInputTypeName[] = "name"; +static constexpr char kComposingBaseKey[] = "composingBase"; +static constexpr char kComposingExtentKey[] = "composingExtent"; +static constexpr char kSelectionAffinityKey[] = "selectionAffinity"; +static constexpr char kAffinityDownstream[] = "TextAffinity.downstream"; static constexpr char kSelectionBaseKey[] = "selectionBase"; static constexpr char kSelectionExtentKey[] = "selectionExtent"; - +static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional"; static constexpr char kTextKey[] = "text"; static constexpr char kChannelName[] = "flutter/textinput"; @@ -133,8 +140,25 @@ void TextInputPlugin::HandleMethodCall( result->Error(kBadArgumentError, "Could not set client, missing arguments."); } - int client_id = client_id_json.GetInt(); - active_model_ = std::make_unique(client_id, client_config); + client_id_ = client_id_json.GetInt(); + std::string input_action; + auto input_action_json = client_config.FindMember(kTextInputAction); + if (input_action_json != client_config.MemberEnd() && + input_action_json->value.IsString()) { + input_action = input_action_json->value.GetString(); + } + std::string input_type; + auto input_type_info_json = client_config.FindMember(kTextInputType); + if (input_type_info_json != client_config.MemberEnd() && + input_type_info_json->value.IsObject()) { + auto input_type_json = + input_type_info_json->value.FindMember(kTextInputTypeName); + if (input_type_json != input_type_info_json->value.MemberEnd() && + input_type_json->value.IsString()) { + input_type = input_type_json->value.GetString(); + } + } + active_model_ = std::make_unique(input_type, input_action); } else if (method.compare(kSetEditingStateMethod) == 0) { if (!method_call.arguments() || method_call.arguments()->IsNull()) { result->Error(kBadArgumentError, "Method invoked without args"); @@ -176,7 +200,24 @@ void TextInputPlugin::HandleMethodCall( } void TextInputPlugin::SendStateUpdate(const TextInputModel& model) { - channel_->InvokeMethod(kUpdateEditingStateMethod, model.GetState()); + auto args = std::make_unique(rapidjson::kArrayType); + auto& allocator = args->GetAllocator(); + args->PushBack(client_id_, allocator); + + rapidjson::Value editing_state(rapidjson::kObjectType); + editing_state.AddMember(kComposingBaseKey, -1, allocator); + editing_state.AddMember(kComposingExtentKey, -1, allocator); + editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream, + allocator); + editing_state.AddMember(kSelectionBaseKey, model.selection_base(), allocator); + editing_state.AddMember(kSelectionExtentKey, model.selection_extent(), + allocator); + editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator); + editing_state.AddMember( + kTextKey, rapidjson::Value(model.GetText(), allocator).Move(), allocator); + args->PushBack(editing_state, allocator); + + channel_->InvokeMethod(kUpdateEditingStateMethod, std::move(args)); } void TextInputPlugin::EnterPressed(TextInputModel* model) { @@ -186,7 +227,7 @@ void TextInputPlugin::EnterPressed(TextInputModel* model) { } auto args = std::make_unique(rapidjson::kArrayType); auto& allocator = args->GetAllocator(); - args->PushBack(model->client_id(), allocator); + args->PushBack(client_id_, allocator); args->PushBack(rapidjson::Value(model->input_action(), allocator).Move(), allocator); diff --git a/shell/platform/glfw/text_input_plugin.h b/shell/platform/glfw/text_input_plugin.h index c3e6673d50a..00c9a732c4f 100644 --- a/shell/platform/glfw/text_input_plugin.h +++ b/shell/platform/glfw/text_input_plugin.h @@ -14,6 +14,8 @@ #include "flutter/shell/platform/glfw/keyboard_hook_handler.h" #include "flutter/shell/platform/glfw/public/flutter_glfw.h" +#include "rapidjson/document.h" + namespace flutter { // Implements a text input plugin. @@ -50,6 +52,9 @@ class TextInputPlugin : public KeyboardHookHandler { // The MethodChannel used for communication with the Flutter engine. std::unique_ptr> channel_; + // The active client id. + int client_id_; + // The active model. nullptr if not set. std::unique_ptr active_model_; }; diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index be34dee567d..36f6389f8a7 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -23,9 +23,16 @@ static constexpr char kUpdateEditingStateMethod[] = "TextInputClient.updateEditingState"; static constexpr char kPerformActionMethod[] = "TextInputClient.performAction"; +static constexpr char kTextInputAction[] = "inputAction"; +static constexpr char kTextInputType[] = "inputType"; +static constexpr char kTextInputTypeName[] = "name"; +static constexpr char kComposingBaseKey[] = "composingBase"; +static constexpr char kComposingExtentKey[] = "composingExtent"; +static constexpr char kSelectionAffinityKey[] = "selectionAffinity"; +static constexpr char kAffinityDownstream[] = "TextAffinity.downstream"; static constexpr char kSelectionBaseKey[] = "selectionBase"; static constexpr char kSelectionExtentKey[] = "selectionExtent"; - +static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional"; static constexpr char kTextKey[] = "text"; static constexpr char kChannelName[] = "flutter/textinput"; @@ -135,8 +142,25 @@ void TextInputPlugin::HandleMethodCall( "Could not set client, missing arguments."); return; } - int client_id = client_id_json.GetInt(); - active_model_ = std::make_unique(client_id, client_config); + client_id_ = client_id_json.GetInt(); + std::string input_action; + auto input_action_json = client_config.FindMember(kTextInputAction); + if (input_action_json != client_config.MemberEnd() && + input_action_json->value.IsString()) { + input_action = input_action_json->value.GetString(); + } + std::string input_type; + auto input_type_info_json = client_config.FindMember(kTextInputType); + if (input_type_info_json != client_config.MemberEnd() && + input_type_info_json->value.IsObject()) { + auto input_type_json = + input_type_info_json->value.FindMember(kTextInputTypeName); + if (input_type_json != input_type_info_json->value.MemberEnd() && + input_type_json->value.IsString()) { + input_type = input_type_json->value.GetString(); + } + } + active_model_ = std::make_unique(input_type, input_action); } else if (method.compare(kSetEditingStateMethod) == 0) { if (!method_call.arguments() || method_call.arguments()->IsNull()) { result->Error(kBadArgumentError, "Method invoked without args"); @@ -178,7 +202,24 @@ void TextInputPlugin::HandleMethodCall( } void TextInputPlugin::SendStateUpdate(const TextInputModel& model) { - channel_->InvokeMethod(kUpdateEditingStateMethod, model.GetState()); + auto args = std::make_unique(rapidjson::kArrayType); + auto& allocator = args->GetAllocator(); + args->PushBack(client_id_, allocator); + + rapidjson::Value editing_state(rapidjson::kObjectType); + editing_state.AddMember(kComposingBaseKey, -1, allocator); + editing_state.AddMember(kComposingExtentKey, -1, allocator); + editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream, + allocator); + editing_state.AddMember(kSelectionBaseKey, model.selection_base(), allocator); + editing_state.AddMember(kSelectionExtentKey, model.selection_extent(), + allocator); + editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator); + editing_state.AddMember( + kTextKey, rapidjson::Value(model.GetText(), allocator).Move(), allocator); + args->PushBack(editing_state, allocator); + + channel_->InvokeMethod(kUpdateEditingStateMethod, std::move(args)); } void TextInputPlugin::EnterPressed(TextInputModel* model) { @@ -188,7 +229,7 @@ void TextInputPlugin::EnterPressed(TextInputModel* model) { } auto args = std::make_unique(rapidjson::kArrayType); auto& allocator = args->GetAllocator(); - args->PushBack(model->client_id(), allocator); + args->PushBack(client_id_, allocator); args->PushBack(rapidjson::Value(model->input_action(), allocator).Move(), allocator); diff --git a/shell/platform/windows/text_input_plugin.h b/shell/platform/windows/text_input_plugin.h index 44179f8b8b8..9000da7e079 100644 --- a/shell/platform/windows/text_input_plugin.h +++ b/shell/platform/windows/text_input_plugin.h @@ -10,6 +10,7 @@ #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h" #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h" +#include "flutter/shell/platform/common/cpp/json_method_codec.h" #include "flutter/shell/platform/common/cpp/text_input_model.h" #include "flutter/shell/platform/windows/keyboard_hook_handler.h" #include "flutter/shell/platform/windows/public/flutter_windows.h" @@ -53,6 +54,9 @@ class TextInputPlugin : public KeyboardHookHandler { // The MethodChannel used for communication with the Flutter engine. std::unique_ptr> channel_; + // The active client id. + int client_id_; + // The active model. nullptr if not set. std::unique_ptr active_model_; };