From f5bc4106d9a795c3e3667b9542ebe0cb5ec2fd46 Mon Sep 17 00:00:00 2001 From: Boram Bae Date: Thu, 19 Aug 2021 18:11:54 +0900 Subject: [PATCH] Use TextInputModel's composing * Use composing methods of TextInputModel in preedit events callback. * Make Implementations about editing text simpler. * Remove unnecessary public methods of TizenInputMethodContext. * Fix a bug that didn't reset IMF-context when the selection base changed Signed-off-by: Boram Bae --- .../tizen/channels/text_input_channel.cc | 165 ++++++++++-------- .../tizen/channels/text_input_channel.h | 5 - .../tizen/tizen_input_method_context.cc | 152 ++++++++-------- .../tizen/tizen_input_method_context.h | 42 +++-- 4 files changed, 194 insertions(+), 170 deletions(-) diff --git a/shell/platform/tizen/channels/text_input_channel.cc b/shell/platform/tizen/channels/text_input_channel.cc index bf45155eab73f..1b59c9573889e 100644 --- a/shell/platform/tizen/channels/text_input_channel.cc +++ b/shell/platform/tizen/channels/text_input_channel.cc @@ -63,59 +63,63 @@ TextInputChannel::TextInputChannel( HandleMethodCall(call, std::move(result)); }); - // Set input method callbacks - input_method_context_->SetOnCommitCallback([this](std::string str) -> void { - FT_LOG(Debug) << "OnCommit: " << str; - text_editing_context_.edit_status_ = EditStatus::kCommit; - ConsumeLastPreedit(); - active_model_->AddText(str); - SendStateUpdate(*active_model_); + // Set input method callbacks. + input_method_context_->SetOnPreeditStart([this]() { + FT_LOG(Debug) << "onPreeditStart"; + text_editing_context_.edit_status_ = EditStatus::kPreeditStart; + active_model_->BeginComposing(); }); - input_method_context_->SetOnPreeditCallback( + input_method_context_->SetOnPreeditChanged( [this](std::string str, int cursor_pos) -> void { - text_editing_context_.edit_status_ = EditStatus::kPreeditStart; - if (str.compare("") == 0) { - text_editing_context_.edit_status_ = EditStatus::kPreeditEnd; + FT_LOG(Debug) << "onPreedit: str[" << str << "] cursor_pos[" + << cursor_pos << "]"; + if (str == "") { + // Enter pre-edit end stage. + return; } + active_model_->UpdateComposingText(str); + SendStateUpdate(*active_model_); + }); - if (text_editing_context_.edit_status_ == EditStatus::kPreeditStart || - (text_editing_context_.edit_status_ == EditStatus::kPreeditEnd && - // For tv, fix me - text_editing_context_.last_handled_ecore_event_keyname_.compare( - "Return") != 0)) { - text_editing_context_.last_handled_ecore_event_keyname_ = ""; - ConsumeLastPreedit(); - } + input_method_context_->SetOnPreeditEnd([this]() { + text_editing_context_.edit_status_ = EditStatus::kPreeditEnd; + FT_LOG(Debug) << "onPreeditEnd"; - text_editing_context_.has_preedit_ = false; - if (text_editing_context_.edit_status_ == EditStatus::kPreeditStart) { - text_editing_context_.preedit_start_pos_ = - active_model_->selection().base(); - active_model_->AddText(str); - text_editing_context_.preedit_end_pos_ = - active_model_->selection().base(); - text_editing_context_.has_preedit_ = true; - SendStateUpdate(*active_model_); - FT_LOG(Debug) << "Preedit start position: " - << text_editing_context_.preedit_start_pos_ - << ", end position: " - << text_editing_context_.preedit_end_pos_; - } - }); + // Delete preedit-string, it will be committed. + int count = active_model_->composing_range().extent() - + active_model_->composing_range().base(); - input_method_context_->SetOnInputPannelStateChangedCallback( - [this](int state) { - if (state == ECORE_IMF_INPUT_PANEL_STATE_HIDE) { - // Fallback for HW back-key - input_method_context_->HideInputPannel(); - input_method_context_->ResetInputMethodContext(); - ResetTextEditingContext(); - is_software_keyboard_showing_ = false; - } else { - is_software_keyboard_showing_ = true; - } - }); + active_model_->CommitComposing(); + active_model_->EndComposing(); + + active_model_->DeleteSurrounding(-count, count); + + SendStateUpdate(*active_model_); + }); + + input_method_context_->SetOnCommit([this](std::string str) -> void { + FT_LOG(Debug) << "OnCommit: str[" << str << "]"; + text_editing_context_.edit_status_ = EditStatus::kCommit; + active_model_->AddText(str); + if (active_model_->composing()) { + active_model_->CommitComposing(); + active_model_->EndComposing(); + } + SendStateUpdate(*active_model_); + }); + + input_method_context_->SetOnInputPannelStateChanged([this](int state) { + if (state == ECORE_IMF_INPUT_PANEL_STATE_HIDE) { + // Fallback for HW back-key. + input_method_context_->HideInputPannel(); + input_method_context_->ResetInputMethodContext(); + ResetTextEditingContext(); + is_software_keyboard_showing_ = false; + } else { + is_software_keyboard_showing_ = true; + } + }); } TextInputChannel::~TextInputChannel() {} @@ -125,7 +129,7 @@ bool TextInputChannel::SendKeyEvent(Ecore_Event_Key* key, bool is_down) { return false; } - if (!FilterEvent(key) && !text_editing_context_.has_preedit_) { + if (!FilterEvent(key)) { HandleUnfilteredEvent(key); } @@ -136,7 +140,7 @@ void TextInputChannel::HandleMethodCall( const MethodCall& method_call, std::unique_ptr> result) { const std::string& method = method_call.method_name(); - FT_LOG(Debug) << "Handle a method: " << method; + FT_LOG(Debug) << "method: " << method; if (method.compare(kShowMethod) == 0) { input_method_context_->ShowInputPannel(); @@ -194,6 +198,9 @@ void TextInputChannel::HandleMethodCall( active_model_ = std::make_unique(); } else if (method.compare(kSetEditingStateMethod) == 0) { + input_method_context_->ResetInputMethodContext(); + ResetTextEditingContext(); + if (!method_call.arguments() || method_call.arguments()->IsNull()) { result->Error(kBadArgumentError, "Method invoked without args"); return; @@ -224,10 +231,34 @@ void TextInputChannel::HandleMethodCall( "Selection base/extent values invalid."); return; } + auto selection_base_value = selection_base->value.GetInt(); + auto selection_extent_value = selection_extent->value.GetInt(); active_model_->SetText(text->value.GetString()); - active_model_->SetSelection(TextRange(selection_base->value.GetInt(), - selection_extent->value.GetInt())); + active_model_->SetSelection( + TextRange(selection_base_value, selection_extent_value)); + + auto composing_base = args.FindMember(kComposingBaseKey); + auto composing_extent = args.FindMember(kComposingBaseKey); + auto composing_base_value = composing_base != args.MemberEnd() + ? composing_base->value.GetInt() + : -1; + auto composing_extent_value = composing_extent != args.MemberEnd() + ? composing_extent->value.GetInt() + : -1; + + if (composing_base_value == -1 && composing_extent_value == -1) { + active_model_->EndComposing(); + } else { + size_t composing_start = + std::min(composing_base_value, composing_extent_value); + size_t cursor_offset = selection_base_value - composing_start; + + active_model_->SetComposingRange( + flutter::TextRange(composing_base_value, composing_extent_value), + cursor_offset); + } + SendStateUpdate(*active_model_); } else { result->NotImplemented(); return; @@ -244,8 +275,14 @@ void TextInputChannel::SendStateUpdate(const TextInputModel& model) { TextRange selection = model.selection(); rapidjson::Value editing_state(rapidjson::kObjectType); - editing_state.AddMember(kComposingBaseKey, -1, allocator); - editing_state.AddMember(kComposingExtentKey, -1, allocator); + int composing_base = + active_model_->composing() ? active_model_->composing_range().base() : -1; + int composing_extent = active_model_->composing() + ? active_model_->composing_range().extent() + : -1; + + editing_state.AddMember(kComposingBaseKey, composing_base, allocator); + editing_state.AddMember(kComposingExtentKey, composing_extent, allocator); editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream, allocator); editing_state.AddMember(kSelectionBaseKey, selection.base(), allocator); @@ -255,7 +292,7 @@ void TextInputChannel::SendStateUpdate(const TextInputModel& model) { kTextKey, rapidjson::Value(model.GetText(), allocator).Move(), allocator); args->PushBack(editing_state, allocator); - FT_LOG(Info) << "Send text: " << model.GetText(); + FT_LOG(Debug) << "Send text:[" << model.GetText() << "]"; channel_->InvokeMethod(kUpdateEditingStateMethod, std::move(args)); } @@ -285,10 +322,6 @@ bool TextInputChannel::FilterEvent(Ecore_Event_Key* event) { handled = input_method_context_->FilterEvent(event, is_ime ? "ime" : ""); - if (handled) { - text_editing_context_.last_handled_ecore_event_keyname_ = event->keyname; - } - #ifdef WEARABLE_PROFILE if (!handled && !strcmp(event->key, "Return") && text_editing_context_.is_in_select_mode_) { @@ -305,7 +338,7 @@ void TextInputChannel::HandleUnfilteredEvent(Ecore_Event_Key* event) { #ifdef MOBILE_PROFILE // FIXME: Only for mobile. if (text_editing_context_.edit_status_ == EditStatus::kPreeditEnd) { - FT_LOG(Warn) << "Ignore a key event: " << event->keyname; + FT_LOG(Debug) << "Ignore a key event: " << event->keyname; ResetTextEditingContext(); return; } @@ -367,22 +400,6 @@ void TextInputChannel::EnterPressed(TextInputModel* model, bool select) { channel_->InvokeMethod(kPerformActionMethod, std::move(args)); } -void TextInputChannel::ConsumeLastPreedit() { - if (text_editing_context_.has_preedit_) { - std::string before = active_model_->GetText(); - int count = text_editing_context_.preedit_end_pos_ - - text_editing_context_.preedit_start_pos_; - active_model_->DeleteSurrounding(-count, count); - std::string after = active_model_->GetText(); - FT_LOG(Debug) << "Last preedit count: " << count << ", text: " << before - << " -> " << after; - SendStateUpdate(*active_model_); - } - text_editing_context_.has_preedit_ = false; - text_editing_context_.preedit_end_pos_ = 0; - text_editing_context_.preedit_start_pos_ = 0; -} - bool TextInputChannel::ShouldNotFilterEvent(std::string key, bool is_ime) { // Force redirect to HandleUnfilteredEvent(especially on TV) // If you don't do this, it will affects the input panel. diff --git a/shell/platform/tizen/channels/text_input_channel.h b/shell/platform/tizen/channels/text_input_channel.h index b97177b2162ff..0691a2fd7cffb 100644 --- a/shell/platform/tizen/channels/text_input_channel.h +++ b/shell/platform/tizen/channels/text_input_channel.h @@ -25,11 +25,7 @@ enum class EditStatus { kNone, kPreeditStart, kPreeditEnd, kCommit }; struct TextEditingContext { EditStatus edit_status_ = EditStatus::kNone; - bool has_preedit_ = false; bool is_in_select_mode_ = false; - std::string last_handled_ecore_event_keyname_ = ""; - int preedit_end_pos_ = 0; - int preedit_start_pos_ = 0; }; class TextInputChannel { @@ -51,7 +47,6 @@ class TextInputChannel { bool FilterEvent(Ecore_Event_Key* event); void HandleUnfilteredEvent(Ecore_Event_Key* event); void EnterPressed(TextInputModel* model, bool select); - void ConsumeLastPreedit(); void ResetTextEditingContext() { text_editing_context_ = TextEditingContext(); } diff --git a/shell/platform/tizen/tizen_input_method_context.cc b/shell/platform/tizen/tizen_input_method_context.cc index eca5e9b86bc8a..0b928989d0d9a 100644 --- a/shell/platform/tizen/tizen_input_method_context.cc +++ b/shell/platform/tizen/tizen_input_method_context.cc @@ -100,43 +100,6 @@ Ecore_IMF_Keyboard_Locks EcoreInputModifierToEcoreIMFLock( return static_cast(lock); } -void CommitCallback(void* data, Ecore_IMF_Context* ctx, void* event_info) { - FT_ASSERT(data); - - flutter::TizenInputMethodContext* self = - static_cast(data); - - char* str = static_cast(event_info); - - self->OnCommit(str); -} - -void PreeditCallback(void* data, Ecore_IMF_Context* ctx, void* event_info) { - FT_ASSERT(data); - flutter::TizenInputMethodContext* self = - static_cast(data); - - char* str = nullptr; - int cursor_pos = 0; - - ecore_imf_context_preedit_string_get(ctx, &str, &cursor_pos); - - if (str) { - self->OnPreedit(str, cursor_pos); - free(str); - } -} - -void InputPanelStateChangedCallback(void* data, - Ecore_IMF_Context* context, - int value) { - FT_ASSERT(data); - flutter::TizenInputMethodContext* self = - static_cast(data); - - self->OnInputPannelStateChanged(value); -} - } // namespace namespace flutter { @@ -238,31 +201,6 @@ void TizenInputMethodContext::HideInputPannel() { ecore_imf_context_input_panel_hide(imf_context_); } -void TizenInputMethodContext::OnCommit(std::string str) { - if (!on_commit_callback_) { - FT_LOG(Warn) << "SetOnCommitCallback() has not been called."; - return; - } - on_commit_callback_(str); -} - -void TizenInputMethodContext::OnPreedit(std::string str, int cursor_pos) { - if (!on_preedit_callback_) { - FT_LOG(Warn) << "SetOnPreeditCallback() has not been called."; - return; - } - on_preedit_callback_(str, cursor_pos); -} - -void TizenInputMethodContext::OnInputPannelStateChanged(int state) { - if (!on_input_pannel_state_changed_callback_) { - FT_LOG(Warn) - << "SetOnInputPannelStateChangedCallback() has not been called."; - return; - } - on_input_pannel_state_changed_callback_(state); -} - void TizenInputMethodContext::SetInputPannelLayout(std::string input_type) { FT_ASSERT(imf_context_); Ecore_IMF_Input_Panel_Layout panel_layout; @@ -273,24 +211,94 @@ void TizenInputMethodContext::SetInputPannelLayout(std::string input_type) { void TizenInputMethodContext::RegisterEventCallbacks() { FT_ASSERT(imf_context_); - ecore_imf_context_event_callback_add(imf_context_, ECORE_IMF_CALLBACK_COMMIT, - CommitCallback, this); + + // commit callback + event_callbacks_[ECORE_IMF_CALLBACK_COMMIT] = + [](void* data, Ecore_IMF_Context* ctx, void* event_info) { + flutter::TizenInputMethodContext* self = + static_cast(data); + char* str = static_cast(event_info); + if (self->on_commit_) { + self->on_commit_(str); + } + }; ecore_imf_context_event_callback_add( - imf_context_, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreeditCallback, this); + imf_context_, ECORE_IMF_CALLBACK_COMMIT, + event_callbacks_[ECORE_IMF_CALLBACK_COMMIT], this); + + // pre-edit start callback + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_START] = + [](void* data, Ecore_IMF_Context* ctx, void* event_info) { + flutter::TizenInputMethodContext* self = + static_cast(data); + if (self->on_preedit_start_) { + self->on_preedit_start_(); + } + }; + ecore_imf_context_event_callback_add( + imf_context_, ECORE_IMF_CALLBACK_PREEDIT_START, + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_START], this); + + // pre-edit end callback + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_END] = + [](void* data, Ecore_IMF_Context* ctx, void* event_info) { + flutter::TizenInputMethodContext* self = + static_cast(data); + if (self->on_preedit_end_) { + self->on_preedit_end_(); + } + }; + ecore_imf_context_event_callback_add( + imf_context_, ECORE_IMF_CALLBACK_PREEDIT_END, + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_END], this); + + // pre-edit changed callback + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_CHANGED] = + [](void* data, Ecore_IMF_Context* ctx, void* event_info) { + flutter::TizenInputMethodContext* self = + static_cast(data); + if (self->on_preedit_changed_) { + char* str = nullptr; + int cursor_pos = 0; + ecore_imf_context_preedit_string_get(ctx, &str, &cursor_pos); + if (str) { + self->on_preedit_changed_(str, cursor_pos); + free(str); + } + } + }; + ecore_imf_context_event_callback_add( + imf_context_, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_CHANGED], this); + + // input panel state callback ecore_imf_context_input_panel_event_callback_add( imf_context_, ECORE_IMF_INPUT_PANEL_STATE_EVENT, - InputPanelStateChangedCallback, this); + [](void* data, Ecore_IMF_Context* context, int value) { + flutter::TizenInputMethodContext* self = + static_cast(data); + if (self->on_input_pannel_state_changed_) { + self->on_input_pannel_state_changed_(value); + } + }, + this); } void TizenInputMethodContext::UnregisterEventCallbacks() { FT_ASSERT(imf_context_); - ecore_imf_context_event_callback_del(imf_context_, ECORE_IMF_CALLBACK_COMMIT, - CommitCallback); ecore_imf_context_event_callback_del( - imf_context_, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreeditCallback); - ecore_imf_context_input_panel_event_callback_del( - imf_context_, ECORE_IMF_INPUT_PANEL_STATE_EVENT, - InputPanelStateChangedCallback); + imf_context_, ECORE_IMF_CALLBACK_COMMIT, + event_callbacks_[ECORE_IMF_CALLBACK_COMMIT]); + ecore_imf_context_event_callback_del( + imf_context_, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_CHANGED]); + ecore_imf_context_event_callback_del( + imf_context_, ECORE_IMF_CALLBACK_PREEDIT_START, + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_START]); + ecore_imf_context_event_callback_del( + imf_context_, ECORE_IMF_CALLBACK_PREEDIT_END, + event_callbacks_[ECORE_IMF_CALLBACK_PREEDIT_END]); + ecore_imf_context_input_panel_event_callback_clear(imf_context_); } void TizenInputMethodContext::SetContextOptions() { diff --git a/shell/platform/tizen/tizen_input_method_context.h b/shell/platform/tizen/tizen_input_method_context.h index 0fe29c70f9b4b..81ca71885e52d 100644 --- a/shell/platform/tizen/tizen_input_method_context.h +++ b/shell/platform/tizen/tizen_input_method_context.h @@ -10,12 +10,15 @@ #include #include +#include namespace flutter { -using OnCommitCallback = std::function; -using OnPreeditCallback = std::function; -using OnInputPannelStateChangedCallback = std::function; +using OnCommit = std::function; +using OnPreeditChanged = std::function; +using OnPreeditStart = std::function; +using OnPreeditEnd = std::function; +using OnInputPannelStateChanged = std::function; class FlutterTizenEngine; @@ -38,25 +41,22 @@ class TizenInputMethodContext { void HideInputPannel(); - void OnCommit(std::string str); - - void OnPreedit(std::string str, int cursor_pos); - - void OnInputPannelStateChanged(int state); - void SetInputPannelLayout(std::string layout); - void SetOnCommitCallback(OnCommitCallback callback) { - on_commit_callback_ = callback; + void SetOnCommit(OnCommit callback) { on_commit_ = callback; } + + void SetOnPreeditChanged(OnPreeditChanged callback) { + on_preedit_changed_ = callback; } - void SetOnPreeditCallback(OnPreeditCallback callback) { - on_preedit_callback_ = callback; + void SetOnPreeditStart(OnPreeditStart callback) { + on_preedit_start_ = callback; } - void SetOnInputPannelStateChangedCallback( - OnInputPannelStateChangedCallback callback) { - on_input_pannel_state_changed_callback_ = callback; + void SetOnPreeditEnd(OnPreeditEnd callback) { on_preedit_end_ = callback; } + + void SetOnInputPannelStateChanged(OnInputPannelStateChanged callback) { + on_input_pannel_state_changed_ = callback; } private: @@ -74,9 +74,13 @@ class TizenInputMethodContext { FlutterTizenEngine* engine_{nullptr}; Ecore_IMF_Context* imf_context_{nullptr}; - OnCommitCallback on_commit_callback_; - OnPreeditCallback on_preedit_callback_; - OnInputPannelStateChangedCallback on_input_pannel_state_changed_callback_; + OnCommit on_commit_; + OnPreeditChanged on_preedit_changed_; + OnPreeditStart on_preedit_start_; + OnPreeditEnd on_preedit_end_; + OnInputPannelStateChanged on_input_pannel_state_changed_; + std::unordered_map + event_callbacks_; }; } // namespace flutter