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.
This commit is contained in:
Robert Ancell 2020-05-15 14:59:56 +12:00 committed by GitHub
parent 88eff68a82
commit 474e6f121e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 121 additions and 85 deletions

View File

@ -8,26 +8,6 @@
#include <codecvt>
#include <locale>
// 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<rapidjson::Document> 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::Document>(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<int>(selection_base_ - text_.begin()),
allocator);
editing_state.AddMember(kSelectionExtentKey,
static_cast<int>(selection_extent_ - text_.begin()),
allocator);
editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator);
std::string TextInputModel::GetText() const {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, 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

View File

@ -8,15 +8,14 @@
#include <memory>
#include <string>
#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<rapidjson::Document> 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<int>(selection_base_ - text_.begin());
}
// The end of the selection
int selection_extent() const {
return static_cast<int>(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_;

View File

@ -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<TextInputModel>(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<TextInputModel>(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::Document>(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::Document>(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);

View File

@ -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<flutter::MethodChannel<rapidjson::Document>> channel_;
// The active client id.
int client_id_;
// The active model. nullptr if not set.
std::unique_ptr<TextInputModel> active_model_;
};

View File

@ -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<TextInputModel>(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<TextInputModel>(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::Document>(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::Document>(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);

View File

@ -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<flutter::MethodChannel<rapidjson::Document>> channel_;
// The active client id.
int client_id_;
// The active model. nullptr if not set.
std::unique_ptr<TextInputModel> active_model_;
};