From e9e593951aa0a13f6f4ab6a917518b7884194853 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 19 Jan 2021 22:16:41 -0800 Subject: [PATCH 1/2] Add TextInput.setMarkedTextRect handler for Windows When inputting text via multi-step text input, such as via Chinese, Japanese, or Korean IME input methods, the framework computes the rectangle of the compose text and updates the embedder via the TextInput.setMarkedTextRect message over the channel. We add a handler for this method which stores the composing rect as a field on TextInputPlugin. In a followup patch, a similar handler will be added for the TextInput.setEditableSizeAndTransform message. Them when compose rect is needed in window coordinates, the rect can be multipled by the 4x4 transform matrix, so the result may be fetched by the view/window class and the IME candidates window position updated. --- shell/platform/windows/text_input_plugin.cc | 26 +++++++++++++++++++++ shell/platform/windows/text_input_plugin.h | 25 ++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index ebfe004292644..6d856d4bcc28f 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -16,6 +16,7 @@ static constexpr char kClearClientMethod[] = "TextInput.clearClient"; static constexpr char kSetClientMethod[] = "TextInput.setClient"; static constexpr char kShowMethod[] = "TextInput.show"; static constexpr char kHideMethod[] = "TextInput.hide"; +static constexpr char kSetMarkedTextRect[] = "TextInput.setMarkedTextRect"; static constexpr char kMultilineInputType[] = "TextInputType.multiline"; @@ -34,6 +35,10 @@ static constexpr char kSelectionBaseKey[] = "selectionBase"; static constexpr char kSelectionExtentKey[] = "selectionExtent"; static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional"; static constexpr char kTextKey[] = "text"; +static constexpr char kXKey[] = "x"; +static constexpr char kYKey[] = "y"; +static constexpr char kWidthKey[] = "width"; +static constexpr char kHeightKey[] = "height"; static constexpr char kChannelName[] = "flutter/textinput"; @@ -171,6 +176,27 @@ void TextInputPlugin::HandleMethodCall( } active_model_->SetText(text->value.GetString()); active_model_->SetSelection(TextRange(base, extent)); + } else if (method.compare(kSetMarkedTextRect) == 0) { + if (!method_call.arguments() || method_call.arguments()->IsNull()) { + result->Error(kBadArgumentError, "Method invoked without args"); + return; + } + const rapidjson::Document& args = *method_call.arguments(); + auto x = args.FindMember(kXKey); + auto y = args.FindMember(kYKey); + auto width = args.FindMember(kWidthKey); + auto height = args.FindMember(kHeightKey); + if (x == args.MemberEnd() || x->value.IsNull() || + y == args.MemberEnd() || y->value.IsNull() || + width == args.MemberEnd() || width->value.IsNull() || + height == args.MemberEnd() || height->value.IsNull()) { + result->Error(kInternalConsistencyError, "Composing rect values invalid."); + return; + } + compose_rect_.x = x->value.GetDouble(); + compose_rect_.y = y->value.GetDouble(); + compose_rect_.width = width->value.GetDouble(); + compose_rect_.height = height->value.GetDouble(); } else { result->NotImplemented(); return; diff --git a/shell/platform/windows/text_input_plugin.h b/shell/platform/windows/text_input_plugin.h index d74b2516afcf3..7a7954ba3c7b2 100644 --- a/shell/platform/windows/text_input_plugin.h +++ b/shell/platform/windows/text_input_plugin.h @@ -19,6 +19,21 @@ namespace flutter { class FlutterWindowsView; +// A rectangle with a position and extent. +// +// Used to store the current composing rectangle when using multi-step (IME) +// text input. +struct Rect { + Rect() = default; + Rect(const Rect& rect) = default; + Rect& operator=(const Rect& other) = default; + + double x; + double y; + double width; + double height; +}; + // Implements a text input plugin. // // Specifically handles window events within windows. @@ -50,6 +65,10 @@ class TextInputPlugin : public KeyboardHookHandler { const flutter::MethodCall& method_call, std::unique_ptr> result); + void SetMarkedTextRect( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + // The MethodChannel used for communication with the Flutter engine. std::unique_ptr> channel_; @@ -66,6 +85,12 @@ class TextInputPlugin : public KeyboardHookHandler { // An action requested by the user on the input client. See available options: // https://api.flutter.dev/flutter/services/TextInputAction-class.html std::string input_action_; + + // The smallest rect, in local coordinates, of the text in the composing + // range, or of the caret in the case where there is no current composing + // range. This value is updated via `TextInput.setMarkedTextRect` messages + // over the text input channel. + Rect compose_rect_; }; } // namespace flutter From cf331730597a527182e4c5cba9465893c87586d2 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 19 Jan 2021 23:06:58 -0800 Subject: [PATCH 2/2] Add TextInput.setEditableSizeAndTransform handler When inputting text, the framework computes a transform from local coordinates to the coordinate system of PipelineOwner.rootNode and updates the embedder via the TextInput.setEditableSizeAndTransform message over the channel. We add a handler for this method which stores this transform in an array on TextInputPlugin. In a previous patch, a similar handler was be added for the TextInput.setMarkedTextRect message. When compose rect is needed in window coordinates, the rect can be multipled by this 4x4 transform matrix, so the result may be read by the view/window class and the IME candidates window position updated. --- shell/platform/windows/text_input_plugin.cc | 25 +++++++++++++++++++++ shell/platform/windows/text_input_plugin.h | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index 6d856d4bcc28f..4d3cac7add4d2 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -17,6 +17,8 @@ static constexpr char kSetClientMethod[] = "TextInput.setClient"; static constexpr char kShowMethod[] = "TextInput.show"; static constexpr char kHideMethod[] = "TextInput.hide"; static constexpr char kSetMarkedTextRect[] = "TextInput.setMarkedTextRect"; +static constexpr char kSetEditableSizeAndTransform[] = + "TextInput.setEditableSizeAndTransform"; static constexpr char kMultilineInputType[] = "TextInputType.multiline"; @@ -39,6 +41,7 @@ static constexpr char kXKey[] = "x"; static constexpr char kYKey[] = "y"; static constexpr char kWidthKey[] = "width"; static constexpr char kHeightKey[] = "height"; +static constexpr char kTransformKey[] = "transform"; static constexpr char kChannelName[] = "flutter/textinput"; @@ -197,6 +200,28 @@ void TextInputPlugin::HandleMethodCall( compose_rect_.y = y->value.GetDouble(); compose_rect_.width = width->value.GetDouble(); compose_rect_.height = height->value.GetDouble(); + } else if (method.compare(kSetEditableSizeAndTransform) == 0) { + if (!method_call.arguments() || method_call.arguments()->IsNull()) { + result->Error(kBadArgumentError, "Method invoked without args"); + return; + } + const rapidjson::Document& args = *method_call.arguments(); + auto transform = args.FindMember(kTransformKey); + if (transform == args.MemberEnd() || transform->value.IsNull() || + !transform->value.IsArray() || transform->value.Size() != 16) { + result->Error(kInternalConsistencyError, "EditableText transform invalid."); + return; + } + size_t i = 0; + for (auto& entry : transform->value.GetArray()) { + if (entry.IsNull()) { + result->Error(kInternalConsistencyError, + "EditableText transform contains null value."); + return; + } + editabletext_transform_[i / 4][i % 4] = entry.GetDouble(); + ++i; + } } else { result->NotImplemented(); return; diff --git a/shell/platform/windows/text_input_plugin.h b/shell/platform/windows/text_input_plugin.h index 7a7954ba3c7b2..837549996d3ff 100644 --- a/shell/platform/windows/text_input_plugin.h +++ b/shell/platform/windows/text_input_plugin.h @@ -91,6 +91,10 @@ class TextInputPlugin : public KeyboardHookHandler { // range. This value is updated via `TextInput.setMarkedTextRect` messages // over the text input channel. Rect compose_rect_; + + // A 4x4 matrix that maps from `EditableText` local coordinates to the + // coordinate system of `PipelineOwner.rootNode`. + double editabletext_transform_[4][4]; }; } // namespace flutter