| Flutter Windows Embedder |

text_input_plugin_unittest.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 #include "flutter/shell/platform/windows/text_input_plugin.h"
5
6 #include <rapidjson/document.h>
7 #include <windows.h>
8 #include <memory>
9
10 #include "flutter/fml/macros.h"
11 #include "flutter/shell/platform/common/json_message_codec.h"
12 #include "flutter/shell/platform/common/json_method_codec.h"
13 #include "flutter/shell/platform/windows/testing/test_binary_messenger.h"
14 #include "flutter/shell/platform/windows/text_input_plugin_delegate.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17
18 namespace flutter {
19 namespace testing {
20
21 namespace {
22 static constexpr char kScanCodeKey[] = "scanCode";
23 static constexpr int kHandledScanCode = 20;
24 static constexpr int kUnhandledScanCode = 21;
25 static constexpr char kTextPlainFormat[] = "text/plain";
26 static constexpr int kDefaultClientId = 42;
27 // Should be identical to constants in text_input_plugin.cc.
28 static constexpr char kChannelName[] = "flutter/textinput";
29 static constexpr char kEnableDeltaModel[] = "enableDeltaModel";
30 static constexpr char kSetClientMethod[] = "TextInput.setClient";
31 static constexpr char kAffinityDownstream[] = "TextAffinity.downstream";
32 static constexpr char kTextKey[] = "text";
33 static constexpr char kSelectionBaseKey[] = "selectionBase";
34 static constexpr char kSelectionExtentKey[] = "selectionExtent";
35 static constexpr char kSelectionAffinityKey[] = "selectionAffinity";
36 static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional";
37 static constexpr char kComposingBaseKey[] = "composingBase";
38 static constexpr char kComposingExtentKey[] = "composingExtent";
39 static constexpr char kUpdateEditingStateMethod[] =
40 "TextInputClient.updateEditingState";
41
42 static std::unique_ptr<std::vector<uint8_t>> CreateResponse(bool handled) {
43 auto response_doc =
44 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
45 auto& allocator = response_doc->GetAllocator();
46 response_doc->AddMember("handled", handled, allocator);
47 return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc);
48 }
49
50 static std::unique_ptr<rapidjson::Document> EncodedClientConfig(
51 std::string type_name,
52 std::string input_action) {
53 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
54 auto& allocator = arguments->GetAllocator();
55 arguments->PushBack(kDefaultClientId, allocator);
56
57 rapidjson::Value config(rapidjson::kObjectType);
58 config.AddMember("inputAction", input_action, allocator);
59 config.AddMember(kEnableDeltaModel, false, allocator);
60 rapidjson::Value type_info(rapidjson::kObjectType);
61 type_info.AddMember("name", type_name, allocator);
62 config.AddMember("inputType", type_info, allocator);
63 arguments->PushBack(config, allocator);
64
65 return arguments;
66 }
67
68 static std::unique_ptr<rapidjson::Document> EncodedEditingState(
69 std::string text,
70 TextRange selection) {
71 auto model = std::make_unique<TextInputModel>();
72 model->SetText(text);
73 model->SetSelection(selection);
74
75 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
76 auto& allocator = arguments->GetAllocator();
77 arguments->PushBack(kDefaultClientId, allocator);
78
79 rapidjson::Value editing_state(rapidjson::kObjectType);
80 editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream,
81 allocator);
82 editing_state.AddMember(kSelectionBaseKey, selection.base(), allocator);
83 editing_state.AddMember(kSelectionExtentKey, selection.extent(), allocator);
84 editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator);
85
86 int composing_base =
87 model->composing() ? model->composing_range().base() : -1;
88 int composing_extent =
89 model->composing() ? model->composing_range().extent() : -1;
90 editing_state.AddMember(kComposingBaseKey, composing_base, allocator);
91 editing_state.AddMember(kComposingExtentKey, composing_extent, allocator);
92 editing_state.AddMember(kTextKey,
93 rapidjson::Value(model->GetText(), allocator).Move(),
94 allocator);
95 arguments->PushBack(editing_state, allocator);
96
97 return arguments;
98 }
99
100 class MockTextInputPluginDelegate : public TextInputPluginDelegate {
101 public:
102 MockTextInputPluginDelegate() {}
103 virtual ~MockTextInputPluginDelegate() = default;
104
105 MOCK_METHOD(void, OnCursorRectUpdated, (const Rect&), (override));
106 MOCK_METHOD(void, OnResetImeComposing, (), (override));
107
108 private:
109 FML_DISALLOW_COPY_AND_ASSIGN(MockTextInputPluginDelegate);
110 };
111
112 } // namespace
113
114 TEST(TextInputPluginTest, TextMethodsWorksWithEmptyModel) {
115 auto handled_message = CreateResponse(true);
116 auto unhandled_message = CreateResponse(false);
117 int received_scancode = 0;
118
119 TestBinaryMessenger messenger(
120 [&received_scancode, &handled_message, &unhandled_message](
121 const std::string& channel, const uint8_t* message,
122 size_t message_size, BinaryReply reply) {});
123 MockTextInputPluginDelegate delegate;
124
125 int redispatch_scancode = 0;
126 TextInputPlugin handler(&messenger, &delegate);
127
128 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
129 handler.ComposeBeginHook();
130 std::u16string text;
131 text.push_back('\n');
132 handler.ComposeChangeHook(text, 1);
133 handler.ComposeEndHook();
134
135 // Passes if it did not crash
136 }
137
138 TEST(TextInputPluginTest, ClearClientResetsComposing) {
139 TestBinaryMessenger messenger([](const std::string& channel,
140 const uint8_t* message, size_t message_size,
141 BinaryReply reply) {});
142 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
143
144 MockTextInputPluginDelegate delegate;
145 TextInputPlugin handler(&messenger, &delegate);
146
147 EXPECT_CALL(delegate, OnResetImeComposing());
148
149 auto& codec = JsonMethodCodec::GetInstance();
150 auto message = codec.EncodeMethodCall({"TextInput.clearClient", nullptr});
151 messenger.SimulateEngineMessage(kChannelName, message->data(),
152 message->size(), reply_handler);
153 }
154
155 // Verify that the embedder sends state update messages to the framework during
156 // IME composing.
157 TEST(TextInputPluginTest, VerifyComposingSendStateUpdate) {
158 bool sent_message = false;
159 TestBinaryMessenger messenger(
160 [&sent_message](const std::string& channel, const uint8_t* message,
161 size_t message_size,
162 BinaryReply reply) { sent_message = true; });
163 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
164
165 MockTextInputPluginDelegate delegate;
166 TextInputPlugin handler(&messenger, &delegate);
167
168 auto& codec = JsonMethodCodec::GetInstance();
169
170 // Call TextInput.setClient to initialize the TextInputModel.
171 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
172 auto& allocator = arguments->GetAllocator();
173 arguments->PushBack(kDefaultClientId, allocator);
174 rapidjson::Value config(rapidjson::kObjectType);
175 config.AddMember("inputAction", "done", allocator);
176 config.AddMember("inputType", "text", allocator);
177 config.AddMember(kEnableDeltaModel, false, allocator);
178 arguments->PushBack(config, allocator);
179 auto message =
180 codec.EncodeMethodCall({"TextInput.setClient", std::move(arguments)});
181 messenger.SimulateEngineMessage("flutter/textinput", message->data(),
182 message->size(), reply_handler);
183
184 // ComposeBeginHook should send state update.
185 sent_message = false;
186 handler.ComposeBeginHook();
187 EXPECT_TRUE(sent_message);
188
189 // ComposeChangeHook should send state update.
190 sent_message = false;
191 handler.ComposeChangeHook(u"4", 1);
192 EXPECT_TRUE(sent_message);
193
194 // ComposeCommitHook should NOT send state update.
195 //
196 // Commit messages are always immediately followed by a change message or an
197 // end message, both of which will send an update. Sending intermediate state
198 // with a collapsed composing region will trigger the framework to assume
199 // composing has ended, which is not the case until a WM_IME_ENDCOMPOSING
200 // event is received in the main event loop, which will trigger a call to
201 // ComposeEndHook.
202 sent_message = false;
203 handler.ComposeCommitHook();
204 EXPECT_FALSE(sent_message);
205
206 // ComposeEndHook should send state update.
207 sent_message = false;
208 handler.ComposeEndHook();
209 EXPECT_TRUE(sent_message);
210 }
211
212 TEST(TextInputPluginTest, VerifyInputActionNewlineInsertNewLine) {
213 // Store messages as std::string for convenience.
214 std::vector<std::string> messages;
215
216 TestBinaryMessenger messenger(
217 [&messages](const std::string& channel, const uint8_t* message,
218 size_t message_size, BinaryReply reply) {
219 std::string last_message(reinterpret_cast<const char*>(message),
220 message_size);
221 messages.push_back(last_message);
222 });
223 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
224
225 MockTextInputPluginDelegate delegate;
226 TextInputPlugin handler(&messenger, &delegate);
227
228 auto& codec = JsonMethodCodec::GetInstance();
229
230 // Call TextInput.setClient to initialize the TextInputModel.
231 auto set_client_arguments =
232 EncodedClientConfig("TextInputType.multiline", "TextInputAction.newline");
233 auto message = codec.EncodeMethodCall(
234 {"TextInput.setClient", std::move(set_client_arguments)});
235 messenger.SimulateEngineMessage("flutter/textinput", message->data(),
236 message->size(), reply_handler);
237
238 // Simulate a key down event for '\n'.
239 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
240
241 // Two messages are expected, the first is TextInput.updateEditingState and
242 // the second is TextInputClient.performAction.
243 EXPECT_EQ(messages.size(), 2);
244
245 // Editing state should have been updated.
246 auto encoded_arguments = EncodedEditingState("\n", TextRange(1));
247 auto update_state_message = codec.EncodeMethodCall(
248 {kUpdateEditingStateMethod, std::move(encoded_arguments)});
249
250 EXPECT_TRUE(std::equal(update_state_message->begin(),
251 update_state_message->end(),
252 messages.front().begin()));
253
254 // TextInputClient.performAction should have been called.
255 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
256 auto& allocator = arguments->GetAllocator();
257 arguments->PushBack(kDefaultClientId, allocator);
258 arguments->PushBack(
259 rapidjson::Value("TextInputAction.newline", allocator).Move(), allocator);
260 auto invoke_action_message = codec.EncodeMethodCall(
261 {"TextInputClient.performAction", std::move(arguments)});
262
263 EXPECT_TRUE(std::equal(invoke_action_message->begin(),
264 invoke_action_message->end(),
265 messages.back().begin()));
266 }
267
268 // Regression test for https://github.com/flutter/flutter/issues/125879.
269 TEST(TextInputPluginTest, VerifyInputActionSendDoesNotInsertNewLine) {
270 std::vector<std::vector<uint8_t>> messages;
271
272 TestBinaryMessenger messenger(
273 [&messages](const std::string& channel, const uint8_t* message,
274 size_t message_size, BinaryReply reply) {
275 int length = static_cast<int>(message_size);
276 std::vector<uint8_t> last_message(length);
277 memcpy(&last_message[0], &message[0], length * sizeof(uint8_t));
278 messages.push_back(last_message);
279 });
280 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
281
282 MockTextInputPluginDelegate delegate;
283 TextInputPlugin handler(&messenger, &delegate);
284
285 auto& codec = JsonMethodCodec::GetInstance();
286
287 // Call TextInput.setClient to initialize the TextInputModel.
288 auto set_client_arguments =
289 EncodedClientConfig("TextInputType.multiline", "TextInputAction.send");
290 auto message = codec.EncodeMethodCall(
291 {"TextInput.setClient", std::move(set_client_arguments)});
292 messenger.SimulateEngineMessage("flutter/textinput", message->data(),
293 message->size(), reply_handler);
294
295 // Simulate a key down event for '\n'.
296 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
297
298 // Only a call to TextInputClient.performAction is expected.
299 EXPECT_EQ(messages.size(), 1);
300
301 // TextInputClient.performAction should have been called.
302 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
303 auto& allocator = arguments->GetAllocator();
304 arguments->PushBack(kDefaultClientId, allocator);
305 arguments->PushBack(
306 rapidjson::Value("TextInputAction.send", allocator).Move(), allocator);
307 auto invoke_action_message = codec.EncodeMethodCall(
308 {"TextInputClient.performAction", std::move(arguments)});
309
310 EXPECT_TRUE(std::equal(invoke_action_message->begin(),
311 invoke_action_message->end(),
312 messages.front().begin()));
313 }
314
315 TEST(TextInputPluginTest, TextEditingWorksWithDeltaModel) {
316 auto handled_message = CreateResponse(true);
317 auto unhandled_message = CreateResponse(false);
318 int received_scancode = 0;
319
320 TestBinaryMessenger messenger(
321 [&received_scancode, &handled_message, &unhandled_message](
322 const std::string& channel, const uint8_t* message,
323 size_t message_size, BinaryReply reply) {});
324 MockTextInputPluginDelegate delegate;
325
326 int redispatch_scancode = 0;
327 TextInputPlugin handler(&messenger, &delegate);
328
329 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
330 auto& allocator = args->GetAllocator();
331 args->PushBack(123, allocator); // client_id
332
333 rapidjson::Value client_config(rapidjson::kObjectType);
334 client_config.AddMember(kEnableDeltaModel, true, allocator);
335
336 args->PushBack(client_config, allocator);
337 auto encoded = JsonMethodCodec::GetInstance().EncodeMethodCall(
338 MethodCall<rapidjson::Document>(kSetClientMethod, std::move(args)));
339
340 EXPECT_TRUE(messenger.SimulateEngineMessage(
341 kChannelName, encoded->data(), encoded->size(),
342 [](const uint8_t* reply, size_t reply_size) {}));
343
344 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
345 handler.ComposeBeginHook();
346 std::u16string text;
347 text.push_back('\n');
348 handler.ComposeChangeHook(text, 1);
349 handler.ComposeEndHook();
350
351 handler.KeyboardHook(0x4E, 100, WM_KEYDOWN, 'n', false, false);
352 handler.ComposeBeginHook();
353 std::u16string textN;
354 text.push_back('n');
355 handler.ComposeChangeHook(textN, 1);
356 handler.KeyboardHook(0x49, 100, WM_KEYDOWN, 'i', false, false);
357 std::u16string textNi;
358 text.push_back('n');
359 text.push_back('i');
360 handler.ComposeChangeHook(textNi, 2);
361 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
362 std::u16string textChineseCharacter;
363 text.push_back(u'\u4F60');
364 handler.ComposeChangeHook(textChineseCharacter, 1);
365 handler.ComposeCommitHook();
366 handler.ComposeEndHook();
367
368 // Passes if it did not crash
369 }
370
371 // Regression test for https://github.com/flutter/flutter/issues/123749
372 TEST(TextInputPluginTest, CompositionCursorPos) {
373 int selection_base = -1;
374 TestBinaryMessenger messenger([&](const std::string& channel,
375 const uint8_t* message, size_t size,
376 BinaryReply reply) {
377 auto method = JsonMethodCodec::GetInstance().DecodeMethodCall(
378 std::vector<uint8_t>(message, message + size));
379 if (method->method_name() == kUpdateEditingStateMethod) {
380 const auto& args = *method->arguments();
381 const auto& editing_state = args[1];
382 auto base = editing_state.FindMember(kSelectionBaseKey);
383 auto extent = editing_state.FindMember(kSelectionExtentKey);
384 ASSERT_NE(base, editing_state.MemberEnd());
385 ASSERT_TRUE(base->value.IsInt());
386 ASSERT_NE(extent, editing_state.MemberEnd());
387 ASSERT_TRUE(extent->value.IsInt());
388 selection_base = base->value.GetInt();
389 EXPECT_EQ(extent->value.GetInt(), selection_base);
390 }
391 });
392 MockTextInputPluginDelegate delegate;
393
394 TextInputPlugin plugin(&messenger, &delegate);
395
396 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
397 auto& allocator = args->GetAllocator();
398 args->PushBack(123, allocator); // client_id
399 rapidjson::Value client_config(rapidjson::kObjectType);
400 args->PushBack(client_config, allocator);
401 auto encoded = JsonMethodCodec::GetInstance().EncodeMethodCall(
402 MethodCall<rapidjson::Document>(kSetClientMethod, std::move(args)));
403 EXPECT_TRUE(messenger.SimulateEngineMessage(
404 kChannelName, encoded->data(), encoded->size(),
405 [](const uint8_t* reply, size_t reply_size) {}));
406
407 plugin.ComposeBeginHook();
408 EXPECT_EQ(selection_base, 0);
409 plugin.ComposeChangeHook(u"abc", 3);
410 EXPECT_EQ(selection_base, 3);
411
412 plugin.ComposeCommitHook();
413 plugin.ComposeEndHook();
414 EXPECT_EQ(selection_base, 3);
415
416 plugin.ComposeBeginHook();
417 plugin.ComposeChangeHook(u"1", 1);
418 EXPECT_EQ(selection_base, 4);
419
420 plugin.ComposeChangeHook(u"12", 2);
421 EXPECT_EQ(selection_base, 5);
422
423 plugin.ComposeChangeHook(u"12", 1);
424 EXPECT_EQ(selection_base, 4);
425
426 plugin.ComposeChangeHook(u"12", 2);
427 EXPECT_EQ(selection_base, 5);
428 }
429
430 TEST(TextInputPluginTest, TransformCursorRect) {
431 // A position of `EditableText`.
432 double view_x = 100;
433 double view_y = 200;
434
435 // A position and size of marked text, in `EditableText` local coordinates.
436 double ime_x = 3;
437 double ime_y = 4;
438 double ime_width = 50;
439 double ime_height = 60;
440
441 // Transformation matrix.
442 std::array<std::array<double, 4>, 4> editabletext_transform = {
443 1.0, 0.0, 0.0, view_x, //
444 0.0, 1.0, 0.0, view_y, //
445 0.0, 0.0, 0.0, 0.0, //
446 0.0, 0.0, 0.0, 1.0};
447
448 TestBinaryMessenger messenger([](const std::string& channel,
449 const uint8_t* message, size_t message_size,
450 BinaryReply reply) {});
451 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
452
453 MockTextInputPluginDelegate delegate;
454 TextInputPlugin handler(&messenger, &delegate);
455
456 auto& codec = JsonMethodCodec::GetInstance();
457
458 EXPECT_CALL(delegate, OnCursorRectUpdated(Rect{{view_x, view_y}, {0, 0}}));
459
460 {
461 auto arguments =
462 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
463 auto& allocator = arguments->GetAllocator();
464
465 rapidjson::Value transoform(rapidjson::kArrayType);
466 for (int i = 0; i < 4 * 4; i++) {
467 // Pack 2-dimensional array by column-major order.
468 transoform.PushBack(editabletext_transform[i % 4][i / 4], allocator);
469 }
470
471 arguments->AddMember("transform", transoform, allocator);
472
473 auto message = codec.EncodeMethodCall(
474 {"TextInput.setEditableSizeAndTransform", std::move(arguments)});
475 messenger.SimulateEngineMessage(kChannelName, message->data(),
476 message->size(), reply_handler);
477 }
478
479 EXPECT_CALL(delegate,
480 OnCursorRectUpdated(Rect{{view_x + ime_x, view_y + ime_y},
481 {ime_width, ime_height}}));
482
483 {
484 auto arguments =
485 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
486 auto& allocator = arguments->GetAllocator();
487
488 arguments->AddMember("x", ime_x, allocator);
489 arguments->AddMember("y", ime_y, allocator);
490 arguments->AddMember("width", ime_width, allocator);
491 arguments->AddMember("height", ime_height, allocator);
492
493 auto message = codec.EncodeMethodCall(
494 {"TextInput.setMarkedTextRect", std::move(arguments)});
495 messenger.SimulateEngineMessage(kChannelName, message->data(),
496 message->size(), reply_handler);
497 }
498 }
499
500 } // namespace testing
501 } // namespace flutter
flutter::TextInputPlugin::ComposeBeginHook
virtual void ComposeBeginHook()
Definition: text_input_plugin.cc:124
flutter::TextInputPlugin::ComposeChangeHook
virtual void ComposeChangeHook(const std::u16string &text, int cursor_pos)
Definition: text_input_plugin.cc:191
kAffinityDownstream
static constexpr char kAffinityDownstream[]
Definition: text_input_plugin.cc:45
flutter::JsonMessageCodec::GetInstance
static const JsonMessageCodec & GetInstance()
Definition: json_message_codec.cc:17
text_input_plugin.h
kUpdateEditingStateMethod
static constexpr char kUpdateEditingStateMethod[]
Definition: text_input_plugin.cc:27
flutter::TextInputPlugin::ComposeEndHook
virtual void ComposeEndHook()
Definition: text_input_plugin.cc:174
json_method_codec.h
kComposingExtentKey
static constexpr char kComposingExtentKey[]
Definition: text_input_plugin.cc:43
flutter::TextInputPlugin::ComposeCommitHook
virtual void ComposeCommitHook()
Definition: text_input_plugin.cc:139
flutter::Rect
Definition: geometry.h:56
json_message_codec.h
flutter::JsonMethodCodec::GetInstance
static const JsonMethodCodec & GetInstance()
Definition: json_method_codec.cc:36
kEnableDeltaModel
static constexpr char kEnableDeltaModel[]
Definition: text_input_plugin.cc:38
kSelectionIsDirectionalKey
static constexpr char kSelectionIsDirectionalKey[]
Definition: text_input_plugin.cc:48
kSelectionExtentKey
static constexpr char kSelectionExtentKey[]
Definition: text_input_plugin.cc:47
flutter::TextRange
Definition: text_range.h:19
text
std::u16string text
Definition: keyboard_unittests.cc:332
flutter::MethodCall
Definition: method_call.h:18
flutter::BinaryReply
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
Definition: binary_messenger.h:17
flutter::MethodCodec::EncodeMethodCall
std::unique_ptr< std::vector< uint8_t > > EncodeMethodCall(const MethodCall< T > &method_call) const
Definition: method_codec.h:48
kSelectionBaseKey
static constexpr char kSelectionBaseKey[]
Definition: text_input_plugin.cc:46
flutter
Definition: accessibility_bridge_windows.cc:11
kTextPlainFormat
static constexpr char kTextPlainFormat[]
Definition: platform_handler.cc:38
kSetClientMethod
static constexpr char kSetClientMethod[]
Definition: text_input_plugin.cc:18
kChannelName
static constexpr char kChannelName[]
Definition: cursor_handler.cc:13
kTextKey
static constexpr char kTextKey[]
Definition: platform_handler.cc:39
flutter::TextInputPlugin
Definition: text_input_plugin.h:28
kSelectionAffinityKey
static constexpr char kSelectionAffinityKey[]
Definition: text_input_plugin.cc:44
flutter::TextInputPlugin::KeyboardHook
virtual void KeyboardHook(int key, int scancode, int action, char32_t character, bool extended, bool was_down)
Definition: text_input_plugin.cc:84
flutter::MessageCodec::EncodeMessage
std::unique_ptr< std::vector< uint8_t > > EncodeMessage(const T &message) const
Definition: message_codec.h:45
flutter::testing::TEST
TEST(AccessibilityBridgeWindows, GetParent)
Definition: accessibility_bridge_windows_unittests.cc:233
message
Win32Message message
Definition: keyboard_unittests.cc:137
kComposingBaseKey
static constexpr char kComposingBaseKey[]
Definition: text_input_plugin.cc:42
text_input_plugin_delegate.h
flutter::MethodCodec::DecodeMethodCall
std::unique_ptr< MethodCall< T > > DecodeMethodCall(const uint8_t *message, size_t message_size) const
Definition: method_codec.h:32