Chromium Code Reviews| Index: ui/base/ime/input_method_auralinux.cc |
| diff --git a/ui/base/ime/input_method_auralinux.cc b/ui/base/ime/input_method_auralinux.cc |
| index f5d4bee59a4cf01377bcb2b16920c94b233ab7aa..b4e118b679ca54cd26ab44fded1c92ec91d86b9c 100644 |
| --- a/ui/base/ime/input_method_auralinux.cc |
| +++ b/ui/base/ime/input_method_auralinux.cc |
| @@ -13,32 +13,29 @@ namespace ui { |
| InputMethodAuraLinux::InputMethodAuraLinux( |
| internal::InputMethodDelegate* delegate) |
| - : allowed_to_fire_vkey_process_key_(false), vkey_processkey_flags_(0) { |
| + : context_focused_(false), handling_key_event_(false), |
| + composing_text_(false), composition_changed_(false), |
| + suppress_next_result_(false) { |
| SetDelegate(delegate); |
| + context_ = LinuxInputMethodContextFactory::instance()-> |
| + CreateInputMethodContext(this, false); |
| + context_simple_ = LinuxInputMethodContextFactory::instance()-> |
| + CreateInputMethodContext(this, true); |
| } |
| InputMethodAuraLinux::~InputMethodAuraLinux() {} |
| +LinuxInputMethodContext* InputMethodAuraLinux::GetContextForTesting( |
| + bool is_simple) { |
| + return is_simple ? context_simple_.get() : context_.get(); |
| +} |
| + |
| // Overriden from InputMethod. |
| void InputMethodAuraLinux::Init(bool focused) { |
| - CHECK(LinuxInputMethodContextFactory::instance()) |
| - << "This failure was likely caused because " |
| - << "ui::InitializeInputMethod(ForTesting) was not called " |
| - << "before instantiating this class."; |
| - input_method_context_ = |
| - LinuxInputMethodContextFactory::instance()->CreateInputMethodContext( |
| - this); |
| - CHECK(input_method_context_.get()); |
| - |
| InputMethodBase::Init(focused); |
| - if (focused) { |
| - input_method_context_->OnTextInputTypeChanged( |
| - GetTextInputClient() ? |
| - GetTextInputClient()->GetTextInputType() : |
| - TEXT_INPUT_TYPE_TEXT); |
| - } |
| + UpdateContextFocusState(); |
| } |
| bool InputMethodAuraLinux::OnUntranslatedIMEMessage( |
| @@ -50,54 +47,129 @@ bool InputMethodAuraLinux::OnUntranslatedIMEMessage( |
| bool InputMethodAuraLinux::DispatchKeyEvent(const ui::KeyEvent& event) { |
| DCHECK(event.type() == ET_KEY_PRESSED || event.type() == ET_KEY_RELEASED); |
| DCHECK(system_toplevel_window_focused()); |
| + suppress_next_result_ = false; |
| // If no text input client, do nothing. |
| if (!GetTextInputClient()) |
| return DispatchKeyEventPostIME(event); |
| - // Let an IME handle the key event first, and allow to fire a VKEY_PROCESSKEY |
| - // event for keydown events. Note that DOM Level 3 Events Sepc requires that |
| - // only keydown events fire keyCode=229 events and not for keyup events. |
| - if (event.type() == ET_KEY_PRESSED && |
| - (event.flags() & ui::EF_IME_FABRICATED_KEY) == 0) |
| - AllowToFireProcessKey(event); |
| - if (input_method_context_->DispatchKeyEvent(event)) |
| - return true; |
| - StopFiringProcessKey(); |
| - |
| - // Otherwise, insert the character. |
| - const bool handled = DispatchKeyEventPostIME(event); |
| - if (event.type() == ET_KEY_PRESSED && GetTextInputClient()) { |
| - const uint16 ch = event.GetCharacter(); |
| - if (ch) { |
| - GetTextInputClient()->InsertChar(ch, event.flags()); |
| - return true; |
| + handling_key_event_ = true; |
| + composition_changed_ = false; |
| + result_text_.clear(); |
| + |
| + bool filtered = false; |
| + if (context_focused_) |
| + filtered = context_->DispatchKeyEvent(event); |
| + else |
| + filtered = context_simple_->DispatchKeyEvent(event); |
| + |
| + handling_key_event_ = true; |
|
Seigo Nonaka
2015/04/08 07:14:58
Please remove.
Optional: probably using a RAII obj
Shu Chen
2015/04/08 08:24:12
Done.
|
| + handling_key_event_ = false; |
| + |
| + if (event.type() == ui::ET_KEY_PRESSED && filtered) { |
| + if (NeedInsertChar()) { |
| + DispatchKeyEventPostIME(event); |
| + } else { |
| + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, event.flags()); |
| + DispatchKeyEventPostIME(key); |
| } |
| } |
| - return handled; |
| + |
| + // If has input result process the result. |
| + if (result_text_.length() || composition_changed_) |
| + ProcessInputMethodResult(event, filtered); |
| + |
| + if (event.type() == ui::ET_KEY_PRESSED && !filtered) { |
| + DispatchKeyEventPostIME(event); |
| + |
| + // If a key event was not filtered by |context_| or |context_simple_|, then |
| + // it means the key event didn't generate any result text. For some cases, |
| + // the key event may still generate a valid character, eg. a control-key |
| + // event (ctrl-a, return, tab, etc.). We need to send the character to the |
| + // focused text input client by calling TextInputClient::InsertChar(). |
| + base::char16 ch = event.GetCharacter(); |
| + TextInputClient* client = GetTextInputClient(); |
| + if (ch && client) |
| + client->InsertChar(ch, event.flags()); |
| + } else if (event.type() == ui::ET_KEY_RELEASED && !filtered) { |
| + DispatchKeyEventPostIME(event); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +void InputMethodAuraLinux::UpdateContextFocusState() { |
| + bool old_context_focused = context_focused_; |
| + // Use switch here in case we are going to add more text input types. |
| + switch (GetTextInputType()) { |
| + case ui::TEXT_INPUT_TYPE_NONE: |
| + case ui::TEXT_INPUT_TYPE_PASSWORD: |
| + context_focused_ = false; |
| + break; |
| + default: |
| + context_focused_ = true; |
| + break; |
| + } |
| + |
| + // We only focus in |context_| when the focus is in a normal textfield. |
| + if (old_context_focused && !context_focused_) |
| + context_->Blur(); |
| + else if (!old_context_focused && context_focused_) |
| + context_->Focus(); |
| + |
| + // |context_simple_| can be used in any textfield, including password box, and |
| + // even if the focused text input client's text input type is |
| + // ui::TEXT_INPUT_TYPE_NONE. |
| + if (GetTextInputClient()) |
| + context_simple_->Focus(); |
| + else |
| + context_simple_->Blur(); |
| } |
| void InputMethodAuraLinux::OnTextInputTypeChanged( |
| const TextInputClient* client) { |
| - if (!IsTextInputClientFocused(client)) |
| - return; |
| - input_method_context_->Reset(); |
| + DCHECK(!composing_text_); |
| + UpdateContextFocusState(); |
| // TODO(yoichio): Support inputmode HTML attribute. |
| - input_method_context_->OnTextInputTypeChanged(client->GetTextInputType()); |
| } |
| void InputMethodAuraLinux::OnCaretBoundsChanged(const TextInputClient* client) { |
| if (!IsTextInputClientFocused(client)) |
| return; |
| - input_method_context_->OnCaretBoundsChanged( |
| - GetTextInputClient()->GetCaretBounds()); |
| + context_->SetCursorLocation(GetTextInputClient()->GetCaretBounds()); |
| } |
| void InputMethodAuraLinux::CancelComposition(const TextInputClient* client) { |
| if (!IsTextInputClientFocused(client)) |
| return; |
| - input_method_context_->Reset(); |
| - input_method_context_->OnTextInputTypeChanged(client->GetTextInputType()); |
| + ResetContext(); |
| +} |
| + |
| +void InputMethodAuraLinux::ResetContext() { |
| + if (!GetTextInputClient()) |
| + return; |
| + |
| + DCHECK(!handling_key_event_); |
| + |
| + // To prevent any text from being committed when resetting the |context_|; |
| + handling_key_event_ = true; |
| + suppress_next_result_ = true; |
| + |
| + context_->Reset(); |
| + context_simple_->Reset(); |
| + |
| + // Some input methods may not honour the reset call. Focusing out/in the |
| + // |context_| to make sure it gets reset correctly. |
| + if (context_focused_) { |
| + context_->Blur(); |
| + context_->Focus(); |
| + } |
| + |
| + composition_.Clear(); |
| + result_text_.clear(); |
| + handling_key_event_ = false; |
| + composing_text_ = false; |
| + composition_changed_ = false; |
| } |
| void InputMethodAuraLinux::OnInputLocaleChanged() { |
| @@ -120,63 +192,146 @@ bool InputMethodAuraLinux::IsCandidatePopupOpen() const { |
| // Overriden from ui::LinuxInputMethodContextDelegate |
| void InputMethodAuraLinux::OnCommit(const base::string16& text) { |
| - MaybeFireProcessKey(); |
| - if (!IsTextInputTypeNone()) |
| + if (suppress_next_result_) { |
| + suppress_next_result_ = false; |
| + return; |
| + } |
| + if (!GetTextInputClient()) |
| + return; |
| + |
| + if (!composing_text_ && !text.length()) |
| + return; |
| + |
| + // Append the text to the buffer, because commit signal might be fired |
| + // multiple times when processing a key event. |
| + result_text_.append(text); |
| + |
| + // If we are not handling key event, do not bother sending text result if the |
| + // focused text input client does not support text input. |
| + if (!handling_key_event_ && !IsTextInputTypeNone()) { |
| + SendFakeProcessKeyEvent(true); |
| GetTextInputClient()->InsertText(text); |
| + } |
| +} |
| + |
| +void InputMethodAuraLinux::OnPreeditStart() { |
| + if (suppress_next_result_ || IsTextInputTypeNone()) |
| + return; |
| + |
| + composing_text_ = true; |
| } |
| void InputMethodAuraLinux::OnPreeditChanged( |
| const CompositionText& composition_text) { |
| - MaybeFireProcessKey(); |
| - TextInputClient* text_input_client = GetTextInputClient(); |
| - if (text_input_client) |
| - text_input_client->SetCompositionText(composition_text); |
| + if (suppress_next_result_ || IsTextInputTypeNone()) |
| + return; |
| + |
| + if (!composition_.text.length() && !composition_text.text.length()) |
| + return; |
| + |
| + composition_ = composition_text; |
| + composition_changed_ = true; |
| + if (composition_.text.length()) |
| + composing_text_ = true; |
| + |
| + if (!handling_key_event_ && !IsTextInputTypeNone()) { |
| + SendFakeProcessKeyEvent(true); |
| + GetTextInputClient()->SetCompositionText(composition_); |
| + } |
| } |
| void InputMethodAuraLinux::OnPreeditEnd() { |
| - MaybeFireProcessKey(); |
| - TextInputClient* text_input_client = GetTextInputClient(); |
| - if (text_input_client && text_input_client->HasCompositionText()) |
| - text_input_client->ClearCompositionText(); |
| -} |
| + if (composition_.text.empty() || IsTextInputTypeNone()) |
| + return; |
| -void InputMethodAuraLinux::OnPreeditStart() { |
| - MaybeFireProcessKey(); |
| + composition_changed_ = true; |
| + composition_.Clear(); |
| + |
| + if (!handling_key_event_) { |
| + TextInputClient* client = GetTextInputClient(); |
| + if (client && client->HasCompositionText()) |
| + client->ClearCompositionText(); |
| + } |
| } |
| // Overridden from InputMethodBase. |
| +void InputMethodAuraLinux::OnFocus() { |
| + InputMethodBase::OnFocus(); |
| + UpdateContextFocusState(); |
| +} |
| + |
| +void InputMethodAuraLinux::OnBlur() { |
| + ConfirmCompositionText(); |
| + InputMethodBase::OnBlur(); |
| + UpdateContextFocusState(); |
| +} |
| + |
| +void InputMethodAuraLinux::OnWillChangeFocusedClient( |
| + TextInputClient* focused_before, |
| + TextInputClient* focused) { |
| + ConfirmCompositionText(); |
| +} |
| + |
| void InputMethodAuraLinux::OnDidChangeFocusedClient( |
| TextInputClient* focused_before, |
| TextInputClient* focused) { |
| - input_method_context_->Reset(); |
| - input_method_context_->OnTextInputTypeChanged( |
| - focused ? focused->GetTextInputType() : TEXT_INPUT_TYPE_NONE); |
| + UpdateContextFocusState(); |
| + |
| + // Force to update caret bounds, in case the View thinks that the caret |
| + // bounds has not changed. |
| + if (context_focused_) |
| + OnCaretBoundsChanged(GetTextInputClient()); |
| InputMethodBase::OnDidChangeFocusedClient(focused_before, focused); |
| } |
| -// Helper functions to support VKEY_PROCESSKEY. |
| +// private |
| + |
| +void InputMethodAuraLinux::ProcessInputMethodResult(const KeyEvent& key, |
| + bool filtered) { |
| + TextInputClient* client = GetTextInputClient(); |
| + DCHECK(client); |
| + |
| + if (result_text_.length()) { |
| + if (filtered && NeedInsertChar()) { |
| + for (base::string16::const_iterator i = result_text_.begin(); |
| + i != result_text_.end(); ++i) { |
| + client->InsertChar(*i, key.flags()); |
| + } |
| + } else { |
| + client->InsertText(result_text_); |
| + composing_text_ = false; |
| + } |
| + } |
| -void InputMethodAuraLinux::AllowToFireProcessKey(const ui::KeyEvent& event) { |
| - allowed_to_fire_vkey_process_key_ = true; |
| - vkey_processkey_flags_ = event.flags(); |
| + if (composition_changed_ && !IsTextInputTypeNone()) { |
| + if (composition_.text.length()) { |
| + composing_text_ = true; |
| + client->SetCompositionText(composition_); |
| + } else if (result_text_.empty()) { |
| + client->ClearCompositionText(); |
| + } |
| + } |
| } |
| -void InputMethodAuraLinux::MaybeFireProcessKey() { |
| - if (!allowed_to_fire_vkey_process_key_) |
| - return; |
| +bool InputMethodAuraLinux::NeedInsertChar() const { |
| + return IsTextInputTypeNone() || |
| + (!composing_text_ && result_text_.length() == 1); |
| +} |
| - const ui::KeyEvent fabricated_event(ET_KEY_PRESSED, |
| - VKEY_PROCESSKEY, |
| - vkey_processkey_flags_); |
| - DispatchKeyEventPostIME(fabricated_event); |
| - StopFiringProcessKey(); |
| +void InputMethodAuraLinux::SendFakeProcessKeyEvent(bool pressed) const { |
| + KeyEvent key(pressed ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED, |
| + ui::VKEY_PROCESSKEY, 0); |
| + DispatchKeyEventPostIME(key); |
| } |
| -void InputMethodAuraLinux::StopFiringProcessKey() { |
| - allowed_to_fire_vkey_process_key_ = false; |
| - vkey_processkey_flags_ = 0; |
| +void InputMethodAuraLinux::ConfirmCompositionText() { |
| + TextInputClient* client = GetTextInputClient(); |
| + if (client && client->HasCompositionText()) |
| + client->ConfirmCompositionText(); |
| + |
| + ResetContext(); |
| } |
| } // namespace ui |