Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
flutter_runner::TextDelegate Class Reference

#include <text_delegate.h>

Inheritance diagram for flutter_runner::TextDelegate:

Public Member Functions

 TextDelegate (fuchsia::ui::views::ViewRef view_ref, fuchsia::ui::input::ImeServiceHandle ime_service, fuchsia::ui::input3::KeyboardHandle keyboard, std::function< void(std::unique_ptr< flutter::PlatformMessage >)> dispatch_callback)
 
void OnKeyEvent (fuchsia::ui::input3::KeyEvent key_event, fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) override
 
void DidUpdateState (fuchsia::ui::input::TextInputState state, std::unique_ptr< fuchsia::ui::input::InputEvent > event) override
 
void OnAction (fuchsia::ui::input::InputMethodAction action) override
 
void ActivateIme ()
 
void DeactivateIme ()
 
bool HandleFlutterTextInputChannelPlatformMessage (std::unique_ptr< flutter::PlatformMessage > message)
 Channel handler for kTextInputChannel.
 
bool HasTextState ()
 

Detailed Description

TextDelegate handles keyboard inpout and text editing.

It mediates between Fuchsia's input and Flutter's platform messages. When it is initialized, it contacts fuchsia.ui.input.Keyboard to register itself as listener of key events.

Whenever a text editing request comes from the Flutter app, it will activate Fuchsia's input method editor, and will send text edit actions coming from the Fuchsia platform over to the Flutter app, by converting FIDL messages (fuchsia.ui.input.InputMethodEditorClient calls) to appropriate text editing Flutter platform messages.

For details refer to:

Definition at line 46 of file text_delegate.h.

Constructor & Destructor Documentation

◆ TextDelegate()

flutter_runner::TextDelegate::TextDelegate ( fuchsia::ui::views::ViewRef  view_ref,
fuchsia::ui::input::ImeServiceHandle  ime_service,
fuchsia::ui::input3::KeyboardHandle  keyboard,
std::function< void(std::unique_ptr< flutter::PlatformMessage >)>  dispatch_callback 
)

Creates a new TextDelegate.

Args: view_ref: the reference to the app's view. Required for registration with Fuchsia. ime_service: a handle to Fuchsia's input method service. keyboard: the keyboard listener, gets notified of key presses and releases. dispatch_callback: a function used to send a Flutter platform message.

Definition at line 113 of file text_delegate.cc.

119 : dispatch_callback_(dispatch_callback),
120 ime_client_(this),
121 text_sync_service_(ime_service.Bind()),
122 keyboard_listener_binding_(this),
123 keyboard_(keyboard.Bind()) {
124 // Register all error handlers.
125 SetInterfaceErrorHandler(ime_, "Input Method Editor");
126 SetInterfaceErrorHandler(ime_client_, "IME Client");
127 SetInterfaceErrorHandler(text_sync_service_, "Text Sync Service");
128 SetInterfaceErrorHandler(keyboard_listener_binding_, "Keyboard Listener");
129 SetInterfaceErrorHandler(keyboard_, "Keyboard");
130
131 // Configure keyboard listener.
132 keyboard_->AddListener(std::move(view_ref),
133 keyboard_listener_binding_.NewBinding(), [] {});
134}
void SetInterfaceErrorHandler(fidl::InterfacePtr< T > &interface, std::string name)

References flutter_runner::SetInterfaceErrorHandler().

Member Function Documentation

◆ ActivateIme()

void flutter_runner::TextDelegate::ActivateIme ( )

Gets a new input method editor from the input connection. Run when both Scenic has focus and Flutter has requested input with setClient.

Definition at line 136 of file text_delegate.cc.

136 {
137 ActivateIme(requested_text_action_.value_or(
138 fuchsia::ui::input::InputMethodAction::DONE));
139}

References ActivateIme().

Referenced by ActivateIme(), and HandleFlutterTextInputChannelPlatformMessage().

◆ DeactivateIme()

void flutter_runner::TextDelegate::DeactivateIme ( )

Detaches the input method editor connection, ending the edit session and closing the onscreen keyboard. Call when input is no longer desired, either because Scenic says we lost focus or when Flutter no longer has a text field focused.

Definition at line 154 of file text_delegate.cc.

154 {
155 if (ime_) {
156 text_sync_service_->HideKeyboard();
157 ime_ = nullptr;
158 }
159 if (ime_client_.is_bound()) {
160 ime_client_.Unbind();
161 }
162}

Referenced by HandleFlutterTextInputChannelPlatformMessage().

◆ DidUpdateState()

void flutter_runner::TextDelegate::DidUpdateState ( fuchsia::ui::input::TextInputState  state,
std::unique_ptr< fuchsia::ui::input::InputEvent >  event 
)
override

|fuchsia::ui::input::InputMethodEditorClient| Called by the embedder every time the edit state is updated.

Definition at line 165 of file text_delegate.cc.

167 {
168 rapidjson::Document document;
169 auto& allocator = document.GetAllocator();
170 rapidjson::Value encoded_state(rapidjson::kObjectType);
171 encoded_state.AddMember("text", state.text, allocator);
172 encoded_state.AddMember("selectionBase", state.selection.base, allocator);
173 encoded_state.AddMember("selectionExtent", state.selection.extent, allocator);
174 switch (state.selection.affinity) {
175 case fuchsia::ui::input::TextAffinity::UPSTREAM:
176 encoded_state.AddMember("selectionAffinity",
177 rapidjson::Value("TextAffinity.upstream"),
178 allocator);
179 break;
180 case fuchsia::ui::input::TextAffinity::DOWNSTREAM:
181 encoded_state.AddMember("selectionAffinity",
182 rapidjson::Value("TextAffinity.downstream"),
183 allocator);
184 break;
185 }
186 encoded_state.AddMember("selectionIsDirectional", true, allocator);
187 encoded_state.AddMember("composingBase", state.composing.start, allocator);
188 encoded_state.AddMember("composingExtent", state.composing.end, allocator);
189
190 rapidjson::Value args(rapidjson::kArrayType);
191 args.PushBack(current_text_input_client_, allocator);
192 args.PushBack(encoded_state, allocator);
193
194 document.SetObject();
195 document.AddMember("method",
196 rapidjson::Value("TextInputClient.updateEditingState"),
197 allocator);
198 document.AddMember("args", args, allocator);
199
200 rapidjson::StringBuffer buffer;
201 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
202 document.Accept(writer);
203
204 const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
205 dispatch_callback_(std::make_unique<flutter::PlatformMessage>(
206 kTextInputChannel, // channel
207 fml::MallocMapping::Copy(data, buffer.GetSize()), // message
208 nullptr) // response
209 );
210 last_text_state_ = std::move(state);
211}
static MallocMapping Copy(const T *begin, const T *end)
Definition mapping.h:162
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
std::shared_ptr< ImpellerAllocator > allocator
constexpr char kTextInputChannel[]
The channel name used for text editing platofrm messages.
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98

References allocator, args, fml::MallocMapping::Copy(), and flutter_runner::kTextInputChannel.

◆ HandleFlutterTextInputChannelPlatformMessage()

bool flutter_runner::TextDelegate::HandleFlutterTextInputChannelPlatformMessage ( std::unique_ptr< flutter::PlatformMessage message)

Channel handler for kTextInputChannel.

Definition at line 244 of file text_delegate.cc.

245 {
246 FML_DCHECK(message->channel() == kTextInputChannel);
247 const auto& data = message->data();
248
249 rapidjson::Document document;
250 document.Parse(reinterpret_cast<const char*>(data.GetMapping()),
251 data.GetSize());
252 if (document.HasParseError() || !document.IsObject()) {
253 return false;
254 }
255 auto root = document.GetObject();
256 auto method = root.FindMember("method");
257 if (method == root.MemberEnd() || !method->value.IsString()) {
258 return false;
259 }
260
261 if (method->value == "TextInput.show") {
262 if (ime_) {
263 text_sync_service_->ShowKeyboard();
264 }
265 } else if (method->value == "TextInput.hide") {
266 if (ime_) {
267 text_sync_service_->HideKeyboard();
268 }
269 } else if (method->value == "TextInput.setClient") {
270 // Sample "setClient" message:
271 //
272 // {
273 // "method": "TextInput.setClient",
274 // "args": [
275 // 7,
276 // {
277 // "inputType": {
278 // "name": "TextInputType.multiline",
279 // "signed":null,
280 // "decimal":null
281 // },
282 // "readOnly": false,
283 // "obscureText": false,
284 // "autocorrect":true,
285 // "smartDashesType":"1",
286 // "smartQuotesType":"1",
287 // "enableSuggestions":true,
288 // "enableInteractiveSelection":true,
289 // "actionLabel":null,
290 // "inputAction":"TextInputAction.newline",
291 // "textCapitalization":"TextCapitalization.none",
292 // "keyboardAppearance":"Brightness.dark",
293 // "enableIMEPersonalizedLearning":true,
294 // "enableDeltaModel":false
295 // }
296 // ]
297 // }
298
299 current_text_input_client_ = 0;
301 auto args = root.FindMember("args");
302 if (args == root.MemberEnd() || !args->value.IsArray() ||
303 args->value.Size() != 2)
304 return false;
305 const auto& configuration = args->value[1];
306 if (!configuration.IsObject()) {
307 return false;
308 }
309 // TODO(abarth): Read the keyboard type from the configuration.
310 current_text_input_client_ = args->value[0].GetInt();
311
312 auto initial_text_input_state = fuchsia::ui::input::TextInputState{};
313 initial_text_input_state.text = "";
314 last_text_state_ = std::move(initial_text_input_state);
315
316 const auto configuration_object = configuration.GetObject();
317 if (!configuration_object.HasMember(kInputActionKey)) {
318 return false;
319 }
320 const auto& action_object = configuration_object[kInputActionKey];
321 if (!action_object.IsString()) {
322 return false;
323 }
324 const auto action_string =
325 std::string(action_object.GetString(), action_object.GetStringLength());
326 ActivateIme(IntoInputMethodAction(std::move(action_string)));
327 } else if (method->value == "TextInput.setEditingState") {
328 if (ime_) {
329 auto args_it = root.FindMember("args");
330 if (args_it == root.MemberEnd() || !args_it->value.IsObject()) {
331 return false;
332 }
333 const auto& args = args_it->value;
334 fuchsia::ui::input::TextInputState state;
335 state.text = "";
336 // TODO(abarth): Deserialize state.
337 auto text = args.FindMember("text");
338 if (text != args.MemberEnd() && text->value.IsString()) {
339 state.text = text->value.GetString();
340 }
341 auto selection_base = args.FindMember("selectionBase");
342 if (selection_base != args.MemberEnd() && selection_base->value.IsInt()) {
343 state.selection.base = selection_base->value.GetInt();
344 }
345 auto selection_extent = args.FindMember("selectionExtent");
346 if (selection_extent != args.MemberEnd() &&
347 selection_extent->value.IsInt()) {
348 state.selection.extent = selection_extent->value.GetInt();
349 }
350 auto selection_affinity = args.FindMember("selectionAffinity");
351 if (selection_affinity != args.MemberEnd() &&
352 selection_affinity->value.IsString() &&
353 selection_affinity->value == "TextAffinity.upstream") {
354 state.selection.affinity = fuchsia::ui::input::TextAffinity::UPSTREAM;
355 } else {
356 state.selection.affinity = fuchsia::ui::input::TextAffinity::DOWNSTREAM;
357 }
358 // We ignore selectionIsDirectional because that concept doesn't exist on
359 // Fuchsia.
360 auto composing_base = args.FindMember("composingBase");
361 if (composing_base != args.MemberEnd() && composing_base->value.IsInt()) {
362 state.composing.start = composing_base->value.GetInt();
363 }
364 auto composing_extent = args.FindMember("composingExtent");
365 if (composing_extent != args.MemberEnd() &&
366 composing_extent->value.IsInt()) {
367 state.composing.end = composing_extent->value.GetInt();
368 }
369 ime_->SetState(std::move(state));
370 }
371 } else if (method->value == "TextInput.clearClient") {
372 current_text_input_client_ = 0;
373 last_text_state_ = std::nullopt;
374 requested_text_action_ = std::nullopt;
376 } else if (method->value == "TextInput.setCaretRect" ||
377 method->value == "TextInput.setEditableSizeAndTransform" ||
378 method->value == "TextInput.setMarkedTextRect" ||
379 method->value == "TextInput.setStyle") {
380 // We don't have these methods implemented and they get
381 // sent a lot during text input, so we create an empty case for them
382 // here to avoid "Unknown flutter/textinput method TextInput.*"
383 // log spam.
384 //
385 // TODO(fxb/101619): We should implement these.
386 } else {
387 FML_LOG(ERROR) << "Unknown " << message->channel() << " method "
388 << method->value.GetString();
389 }
390 // Complete with an empty response.
391 return false;
392}
const char * message
#define FML_LOG(severity)
Definition logging.h:101
#define FML_DCHECK(condition)
Definition logging.h:122
std::u16string text
static constexpr char kInputActionKey[]
static fuchsia::ui::input::InputMethodAction IntoInputMethodAction(const std::string action_string)

References ActivateIme(), args, DeactivateIme(), FML_DCHECK, FML_LOG, flutter_runner::IntoInputMethodAction(), flutter_runner::kInputActionKey, flutter_runner::kTextInputChannel, message, and text.

◆ HasTextState()

bool flutter_runner::TextDelegate::HasTextState ( )
inline

Returns true if there is a text state (i.e. if some text editing is in progress).

Definition at line 97 of file text_delegate.h.

97{ return last_text_state_.has_value(); }

◆ OnAction()

void flutter_runner::TextDelegate::OnAction ( fuchsia::ui::input::InputMethodAction  action)
override

|fuchsia::ui::input::InputMethodEditorClient| Called by the embedder when the action key is pressed, and the requested action is supplied to Flutter.

Definition at line 214 of file text_delegate.cc.

214 {
215 rapidjson::Document document;
216 auto& allocator = document.GetAllocator();
217
218 rapidjson::Value args(rapidjson::kArrayType);
219 args.PushBack(current_text_input_client_, allocator);
220
221 const std::string action_string = IntoTextInputAction(action);
222 args.PushBack(rapidjson::Value{}.SetString(action_string.c_str(),
223 action_string.length()),
224 allocator);
225
226 document.SetObject();
227 document.AddMember(
228 "method", rapidjson::Value("TextInputClient.performAction"), allocator);
229 document.AddMember("args", args, allocator);
230
231 rapidjson::StringBuffer buffer;
232 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
233 document.Accept(writer);
234
235 const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
236 dispatch_callback_(std::make_unique<flutter::PlatformMessage>(
237 kTextInputChannel, // channel
238 fml::MallocMapping::Copy(data, buffer.GetSize()), // message
239 nullptr) // response
240 );
241}
static const std::string IntoTextInputAction(fuchsia::ui::input::InputMethodAction action)

References action, allocator, args, fml::MallocMapping::Copy(), flutter_runner::IntoTextInputAction(), and flutter_runner::kTextInputChannel.

◆ OnKeyEvent()

void flutter_runner::TextDelegate::OnKeyEvent ( fuchsia::ui::input3::KeyEvent  key_event,
fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback  callback 
)
override

|fuchsia.ui.input3.KeyboardListener| Called by the embedder every time there is a key event to process.

Definition at line 395 of file text_delegate.cc.

397 {
398 const char* type = nullptr;
399 switch (key_event.type()) {
400 case fuchsia::ui::input3::KeyEventType::PRESSED:
401 type = "keydown";
402 break;
403 case fuchsia::ui::input3::KeyEventType::RELEASED:
404 type = "keyup";
405 break;
406 case fuchsia::ui::input3::KeyEventType::SYNC:
407 // SYNC means the key was pressed while focus was not on this application.
408 // This should possibly behave like PRESSED in the future, though it
409 // doesn't hurt to ignore it today.
410 case fuchsia::ui::input3::KeyEventType::CANCEL:
411 // CANCEL means the key was released while focus was not on this
412 // application.
413 // This should possibly behave like RELEASED in the future to ensure that
414 // a key is not repeated forever if it is pressed while focus is lost.
415 default:
416 break;
417 }
418 if (type == nullptr) {
419 FML_VLOG(1) << "Unknown key event phase.";
420 callback(fuchsia::ui::input3::KeyEventStatus::NOT_HANDLED);
421 return;
422 }
423 keyboard_translator_.ConsumeEvent(std::move(key_event));
424
425 rapidjson::Document document;
426 auto& allocator = document.GetAllocator();
427 document.SetObject();
428 document.AddMember("type", rapidjson::Value(type, strlen(type)), allocator);
429 document.AddMember("keymap", rapidjson::Value("fuchsia"), allocator);
430 document.AddMember("hidUsage", keyboard_translator_.LastHIDUsage(),
431 allocator);
432 document.AddMember("codePoint", keyboard_translator_.LastCodePoint(),
433 allocator);
434 document.AddMember("modifiers", keyboard_translator_.Modifiers(), allocator);
435 rapidjson::StringBuffer buffer;
436 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
437 document.Accept(writer);
438
439 const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
440 dispatch_callback_(std::make_unique<flutter::PlatformMessage>(
441 kKeyEventChannel, // channel
442 fml::MallocMapping::Copy(data, buffer.GetSize()), // data
443 nullptr) // response
444 );
445 callback(fuchsia::ui::input3::KeyEventStatus::HANDLED);
446}
bool ConsumeEvent(fuchsia::ui::input3::KeyEvent event)
Definition keyboard.cc:174
FlutterDesktopBinaryReply callback
#define FML_VLOG(verbose_level)
Definition logging.h:117
FlutterKeyEvent key_event
constexpr char kKeyEventChannel[]
The channel name used for key event platform messages.
impeller::ShaderType type
FlutterKeyEventType type
The event kind.
Definition embedder.h:1428

References allocator, callback, flutter_runner::Keyboard::ConsumeEvent(), fml::MallocMapping::Copy(), FML_VLOG, key_event, flutter_runner::kKeyEventChannel, flutter_runner::Keyboard::LastCodePoint(), flutter_runner::Keyboard::LastHIDUsage(), flutter_runner::Keyboard::Modifiers(), type, and FlutterKeyEvent::type.


The documentation for this class was generated from the following files: