Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/ime/input_method_auralinux.h" | 5 #include "ui/base/ime/input_method_auralinux.h" |
| 6 | 6 |
| 7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
| 8 #include "base/environment.h" | 8 #include "base/environment.h" |
| 9 #include "ui/base/ime/linux/linux_input_method_context_factory.h" | 9 #include "ui/base/ime/linux/linux_input_method_context_factory.h" |
| 10 #include "ui/base/ime/text_input_client.h" | 10 #include "ui/base/ime/text_input_client.h" |
| 11 #include "ui/events/event.h" | 11 #include "ui/events/event.h" |
| 12 | 12 |
| 13 namespace ui { | 13 namespace ui { |
| 14 | 14 |
| 15 InputMethodAuraLinux::InputMethodAuraLinux( | 15 InputMethodAuraLinux::InputMethodAuraLinux( |
| 16 internal::InputMethodDelegate* delegate) | 16 internal::InputMethodDelegate* delegate) |
| 17 : text_input_type_(TEXT_INPUT_TYPE_NONE), | 17 : text_input_type_(TEXT_INPUT_TYPE_NONE), |
| 18 is_sync_mode_(false), | 18 is_sync_mode_(false), |
| 19 composition_changed_(false), | 19 composition_changed_(false), |
| 20 suppress_next_result_(false), | 20 suppress_next_result_(false) { |
| 21 destroyed_ptr_(nullptr) { | |
| 22 SetDelegate(delegate); | 21 SetDelegate(delegate); |
| 23 context_ = | 22 context_ = |
| 24 LinuxInputMethodContextFactory::instance()->CreateInputMethodContext( | 23 LinuxInputMethodContextFactory::instance()->CreateInputMethodContext( |
| 25 this, false); | 24 this, false); |
| 26 context_simple_ = | 25 context_simple_ = |
| 27 LinuxInputMethodContextFactory::instance()->CreateInputMethodContext( | 26 LinuxInputMethodContextFactory::instance()->CreateInputMethodContext( |
| 28 this, true); | 27 this, true); |
| 29 } | 28 } |
| 30 | 29 |
| 31 InputMethodAuraLinux::~InputMethodAuraLinux() { | 30 InputMethodAuraLinux::~InputMethodAuraLinux() { |
| 32 if (destroyed_ptr_) | |
| 33 *destroyed_ptr_ = true; | |
| 34 } | 31 } |
| 35 | 32 |
| 36 LinuxInputMethodContext* InputMethodAuraLinux::GetContextForTesting( | 33 LinuxInputMethodContext* InputMethodAuraLinux::GetContextForTesting( |
| 37 bool is_simple) { | 34 bool is_simple) { |
| 38 return is_simple ? context_simple_.get() : context_.get(); | 35 return is_simple ? context_simple_.get() : context_.get(); |
| 39 } | 36 } |
| 40 | 37 |
| 41 // Overriden from InputMethod. | 38 // Overriden from InputMethod. |
| 42 | 39 |
| 43 bool InputMethodAuraLinux::OnUntranslatedIMEMessage( | 40 bool InputMethodAuraLinux::OnUntranslatedIMEMessage( |
| 44 const base::NativeEvent& event, | 41 const base::NativeEvent& event, |
| 45 NativeEventResult* result) { | 42 NativeEventResult* result) { |
| 46 return false; | 43 return false; |
| 47 } | 44 } |
| 48 | 45 |
| 49 bool InputMethodAuraLinux::DispatchKeyEvent(const ui::KeyEvent& event) { | 46 void InputMethodAuraLinux::DispatchKeyEvent(ui::KeyEvent* event) { |
| 50 DCHECK(event.type() == ET_KEY_PRESSED || event.type() == ET_KEY_RELEASED); | 47 DCHECK(event->type() == ET_KEY_PRESSED || event->type() == ET_KEY_RELEASED); |
| 51 DCHECK(system_toplevel_window_focused()); | 48 DCHECK(system_toplevel_window_focused()); |
| 52 | 49 |
| 53 // If no text input client, do nothing. | 50 // If no text input client, do nothing. |
| 54 if (!GetTextInputClient()) | 51 if (!GetTextInputClient()) { |
| 55 return DispatchKeyEventPostIME(event); | 52 ignore_result(DispatchKeyEventPostIME(event)); |
| 53 return; | |
| 54 } | |
| 56 | 55 |
| 57 suppress_next_result_ = false; | 56 suppress_next_result_ = false; |
| 58 composition_changed_ = false; | 57 composition_changed_ = false; |
| 59 result_text_.clear(); | 58 result_text_.clear(); |
| 60 | 59 |
| 61 bool filtered = false; | 60 bool filtered = false; |
| 62 { | 61 { |
| 63 base::AutoReset<bool> flipper(&is_sync_mode_, true); | 62 base::AutoReset<bool> flipper(&is_sync_mode_, true); |
| 64 if (text_input_type_ != TEXT_INPUT_TYPE_NONE && | 63 if (text_input_type_ != TEXT_INPUT_TYPE_NONE && |
| 65 text_input_type_ != TEXT_INPUT_TYPE_PASSWORD) { | 64 text_input_type_ != TEXT_INPUT_TYPE_PASSWORD) { |
| 66 filtered = context_->DispatchKeyEvent(event); | 65 filtered = context_->DispatchKeyEvent(*event); |
| 67 } else { | 66 } else { |
| 68 filtered = context_simple_->DispatchKeyEvent(event); | 67 filtered = context_simple_->DispatchKeyEvent(*event); |
| 69 } | 68 } |
| 70 } | 69 } |
| 71 | 70 |
| 72 bool destroyed = false; | 71 ui::EventDispatchDetails details; |
| 73 bool handled = false; | 72 if (event->type() == ui::ET_KEY_PRESSED && filtered) { |
| 74 if (event.type() == ui::ET_KEY_PRESSED && filtered) { | 73 if (NeedInsertChar()) |
| 75 { | 74 details = DispatchKeyEventPostIME(event); |
| 76 base::AutoReset<bool*> auto_reset(&destroyed_ptr_, &destroyed); | 75 else if (HasInputMethodResult()) |
| 77 if (NeedInsertChar()) | 76 details = SendFakeProcessKeyEvent(event); |
| 78 handled = DispatchKeyEventPostIME(event); | 77 if (details.dispatcher_destroyed) |
| 79 else if (HasInputMethodResult()) | 78 return; |
| 80 handled = SendFakeProcessKeyEvent(event.flags()); | |
| 81 if (destroyed) | |
| 82 return true; | |
| 83 } | |
| 84 // If the KEYDOWN is stopped propagation (e.g. triggered an accelerator), | 79 // If the KEYDOWN is stopped propagation (e.g. triggered an accelerator), |
| 85 // don't InsertChar/InsertText to the input field. | 80 // don't InsertChar/InsertText to the input field. |
| 86 if (handled) { | 81 if (event->stopped_propagation() || details.target_destroyed) { |
| 87 ResetContext(); | 82 ResetContext(); |
| 88 return true; | 83 return; |
| 89 } | 84 } |
| 90 | 85 |
| 91 // Don't send VKEY_PROCESSKEY event if there is no result text or | 86 // Don't send VKEY_PROCESSKEY event if there is no result text or |
| 92 // composition. This is to workaround the weird behavior of IBus with US | 87 // composition. This is to workaround the weird behavior of IBus with US |
| 93 // keyboard, which mutes the keydown and later fake a new keydown with IME | 88 // keyboard, which mutes the keydown and later fake a new keydown with IME |
| 94 // result in sync mode. In that case, user would expect only | 89 // result in sync mode. In that case, user would expect only |
| 95 // keydown/keypress/keyup event without an initial 229 keydown event. | 90 // keydown/keypress/keyup event without an initial 229 keydown event. |
| 96 } | 91 } |
| 97 | 92 |
| 98 TextInputClient* client = GetTextInputClient(); | 93 TextInputClient* client = GetTextInputClient(); |
| 99 // Processes the result text before composition for sync mode. | 94 // Processes the result text before composition for sync mode. |
| 100 if (!result_text_.empty()) { | 95 if (!result_text_.empty()) { |
| 101 if (filtered && NeedInsertChar()) { | 96 if (filtered && NeedInsertChar()) { |
| 102 for (const auto ch : result_text_) | 97 for (const auto ch : result_text_) |
| 103 client->InsertChar(ch, event.flags()); | 98 client->InsertChar(ch, event->flags()); |
| 104 } else { | 99 } else { |
| 105 // If |filtered| is false, that means the IME wants to commit some text | 100 // If |filtered| is false, that means the IME wants to commit some text |
| 106 // but still release the key to the application. For example, Korean IME | 101 // but still release the key to the application. For example, Korean IME |
| 107 // handles ENTER key to confirm its composition but still release it for | 102 // handles ENTER key to confirm its composition but still release it for |
| 108 // the default behavior (e.g. trigger search, etc.) | 103 // the default behavior (e.g. trigger search, etc.) |
| 109 // In such case, don't do InsertChar because a key should only trigger the | 104 // In such case, don't do InsertChar because a key should only trigger the |
| 110 // keydown event once. | 105 // keydown event once. |
| 111 client->InsertText(result_text_); | 106 client->InsertText(result_text_); |
| 112 } | 107 } |
| 108 event->StopPropagation(); | |
| 113 } | 109 } |
| 114 | 110 |
| 115 if (composition_changed_ && !IsTextInputTypeNone()) { | 111 if (composition_changed_ && !IsTextInputTypeNone()) { |
| 116 // If composition changed, does SetComposition if composition is not empty. | 112 // If composition changed, does SetComposition if composition is not empty. |
| 117 // And ClearComposition if composition is empty. | 113 // And ClearComposition if composition is empty. |
| 118 if (!composition_.text.empty()) | 114 if (!composition_.text.empty()) |
| 119 client->SetCompositionText(composition_); | 115 client->SetCompositionText(composition_); |
| 120 else if (result_text_.empty()) | 116 else if (result_text_.empty()) |
| 121 client->ClearCompositionText(); | 117 client->ClearCompositionText(); |
| 118 event->StopPropagation(); | |
| 122 } | 119 } |
| 123 | 120 |
| 124 // Makes sure the cached composition is cleared after committing any text or | 121 // Makes sure the cached composition is cleared after committing any text or |
| 125 // cleared composition. | 122 // cleared composition. |
| 126 if (!client->HasCompositionText()) | 123 if (!client->HasCompositionText()) |
| 127 composition_.Clear(); | 124 composition_.Clear(); |
| 128 | 125 |
| 129 if (!filtered) { | 126 if (!filtered) { |
| 130 { | 127 details = DispatchKeyEventPostIME(event); |
| 131 base::AutoReset<bool*> auto_reset(&destroyed_ptr_, &destroyed); | 128 if (details.dispatcher_destroyed) |
| 132 handled = DispatchKeyEventPostIME(event); | 129 return; |
| 133 if (destroyed) | 130 if (event->stopped_propagation() || details.target_destroyed) { |
| 134 return true; | 131 ResetContext(); |
| 132 return; | |
| 135 } | 133 } |
| 136 if (handled) { | 134 if (event->type() == ui::ET_KEY_PRESSED) { |
| 137 ResetContext(); | |
| 138 return true; | |
| 139 } | |
| 140 if (event.type() == ui::ET_KEY_PRESSED) { | |
| 141 // If a key event was not filtered by |context_| or |context_simple_|, | 135 // If a key event was not filtered by |context_| or |context_simple_|, |
| 142 // then it means the key event didn't generate any result text. For some | 136 // then it means the key event didn't generate any result text. For some |
| 143 // cases, the key event may still generate a valid character, eg. a | 137 // cases, the key event may still generate a valid character, eg. a |
| 144 // control-key event (ctrl-a, return, tab, etc.). We need to send the | 138 // control-key event (ctrl-a, return, tab, etc.). We need to send the |
| 145 // character to the focused text input client by calling | 139 // character to the focused text input client by calling |
| 146 // TextInputClient::InsertChar(). | 140 // TextInputClient::InsertChar(). |
| 147 // Note: don't use |client| and use GetTextInputClient() here because | 141 // Note: don't use |client| and use GetTextInputClient() here because |
| 148 // DispatchKeyEventPostIME may cause the current text input client change. | 142 // DispatchKeyEventPostIME may cause the current text input client change. |
| 149 base::char16 ch = event.GetCharacter(); | 143 base::char16 ch = event->GetCharacter(); |
| 150 if (ch && GetTextInputClient()) | 144 if (ch && GetTextInputClient()) |
| 151 GetTextInputClient()->InsertChar(ch, event.flags()); | 145 GetTextInputClient()->InsertChar(ch, event->flags()); |
| 146 event->StopPropagation(); | |
|
James Su
2015/08/03 06:58:55
why this line is moved into this if block?
Shu Chen
2015/08/03 07:03:54
Event is StopPropagation'ed only when TextInputCli
| |
| 152 } | 147 } |
| 153 } | 148 } |
| 154 | |
| 155 return true; | |
| 156 } | 149 } |
| 157 | 150 |
| 158 void InputMethodAuraLinux::UpdateContextFocusState() { | 151 void InputMethodAuraLinux::UpdateContextFocusState() { |
| 159 bool old_text_input_type = text_input_type_; | 152 bool old_text_input_type = text_input_type_; |
| 160 text_input_type_ = GetTextInputType(); | 153 text_input_type_ = GetTextInputType(); |
| 161 | 154 |
| 162 // We only focus in |context_| when the focus is in a textfield. | 155 // We only focus in |context_| when the focus is in a textfield. |
| 163 if (old_text_input_type != TEXT_INPUT_TYPE_NONE && | 156 if (old_text_input_type != TEXT_INPUT_TYPE_NONE && |
| 164 text_input_type_ == TEXT_INPUT_TYPE_NONE) { | 157 text_input_type_ == TEXT_INPUT_TYPE_NONE) { |
| 165 context_->Blur(); | 158 context_->Blur(); |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 241 return; | 234 return; |
| 242 } | 235 } |
| 243 | 236 |
| 244 if (is_sync_mode_) { | 237 if (is_sync_mode_) { |
| 245 // Append the text to the buffer, because commit signal might be fired | 238 // Append the text to the buffer, because commit signal might be fired |
| 246 // multiple times when processing a key event. | 239 // multiple times when processing a key event. |
| 247 result_text_.append(text); | 240 result_text_.append(text); |
| 248 } else if (!IsTextInputTypeNone()) { | 241 } else if (!IsTextInputTypeNone()) { |
| 249 // If we are not handling key event, do not bother sending text result if | 242 // If we are not handling key event, do not bother sending text result if |
| 250 // the focused text input client does not support text input. | 243 // the focused text input client does not support text input. |
| 251 SendFakeProcessKeyEvent(0); | 244 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0); |
| 252 GetTextInputClient()->InsertText(text); | 245 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event); |
| 246 if (details.dispatcher_destroyed) | |
| 247 return; | |
| 248 if (!event.stopped_propagation()) | |
|
James Su
2015/08/03 06:58:55
check target_destroyed?
Shu Chen
2015/08/03 07:03:54
Done.
| |
| 249 GetTextInputClient()->InsertText(text); | |
| 253 composition_.Clear(); | 250 composition_.Clear(); |
| 254 } | 251 } |
| 255 } | 252 } |
| 256 | 253 |
| 257 void InputMethodAuraLinux::OnPreeditChanged( | 254 void InputMethodAuraLinux::OnPreeditChanged( |
| 258 const CompositionText& composition_text) { | 255 const CompositionText& composition_text) { |
| 259 if (suppress_next_result_ || IsTextInputTypeNone()) | 256 if (suppress_next_result_ || IsTextInputTypeNone()) |
| 260 return; | 257 return; |
| 261 | 258 |
| 262 if (is_sync_mode_) { | 259 if (is_sync_mode_) { |
| 263 if (!composition_.text.empty() || !composition_text.text.empty()) | 260 if (!composition_.text.empty() || !composition_text.text.empty()) |
| 264 composition_changed_ = true; | 261 composition_changed_ = true; |
| 265 } else { | 262 } else { |
| 266 SendFakeProcessKeyEvent(0); | 263 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0); |
| 267 GetTextInputClient()->SetCompositionText(composition_text); | 264 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event); |
| 265 if (details.dispatcher_destroyed) | |
| 266 return; | |
| 267 if (!event.stopped_propagation()) | |
|
James Su
2015/08/03 06:58:55
ditto.
Shu Chen
2015/08/03 07:03:54
Done.
| |
| 268 GetTextInputClient()->SetCompositionText(composition_text); | |
| 268 } | 269 } |
| 269 | 270 |
| 270 composition_ = composition_text; | 271 composition_ = composition_text; |
| 271 } | 272 } |
| 272 | 273 |
| 273 void InputMethodAuraLinux::OnPreeditEnd() { | 274 void InputMethodAuraLinux::OnPreeditEnd() { |
| 274 if (suppress_next_result_ || IsTextInputTypeNone()) | 275 if (suppress_next_result_ || IsTextInputTypeNone()) |
| 275 return; | 276 return; |
| 276 | 277 |
| 277 if (is_sync_mode_) { | 278 if (is_sync_mode_) { |
| 278 if (!composition_.text.empty()) { | 279 if (!composition_.text.empty()) { |
| 279 composition_.Clear(); | 280 composition_.Clear(); |
| 280 composition_changed_ = true; | 281 composition_changed_ = true; |
| 281 } | 282 } |
| 282 } else { | 283 } else { |
| 283 TextInputClient* client = GetTextInputClient(); | 284 TextInputClient* client = GetTextInputClient(); |
| 284 if (client && client->HasCompositionText()) { | 285 if (client && client->HasCompositionText()) { |
| 285 SendFakeProcessKeyEvent(0); | 286 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0); |
| 286 client->ClearCompositionText(); | 287 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event); |
| 288 if (details.dispatcher_destroyed) | |
| 289 return; | |
| 290 if (!event.stopped_propagation()) | |
|
James Su
2015/08/03 06:58:55
ditto
Shu Chen
2015/08/03 07:03:54
Done.
| |
| 291 client->ClearCompositionText(); | |
| 287 } | 292 } |
| 288 composition_.Clear(); | 293 composition_.Clear(); |
| 289 } | 294 } |
| 290 } | 295 } |
| 291 | 296 |
| 292 // Overridden from InputMethodBase. | 297 // Overridden from InputMethodBase. |
| 293 | 298 |
| 294 void InputMethodAuraLinux::OnFocus() { | 299 void InputMethodAuraLinux::OnFocus() { |
| 295 InputMethodBase::OnFocus(); | 300 InputMethodBase::OnFocus(); |
| 296 UpdateContextFocusState(); | 301 UpdateContextFocusState(); |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 326 bool InputMethodAuraLinux::HasInputMethodResult() { | 331 bool InputMethodAuraLinux::HasInputMethodResult() { |
| 327 return !result_text_.empty() || composition_changed_; | 332 return !result_text_.empty() || composition_changed_; |
| 328 } | 333 } |
| 329 | 334 |
| 330 bool InputMethodAuraLinux::NeedInsertChar() const { | 335 bool InputMethodAuraLinux::NeedInsertChar() const { |
| 331 return IsTextInputTypeNone() || | 336 return IsTextInputTypeNone() || |
| 332 (!composition_changed_ && composition_.text.empty() && | 337 (!composition_changed_ && composition_.text.empty() && |
| 333 result_text_.length() == 1); | 338 result_text_.length() == 1); |
| 334 } | 339 } |
| 335 | 340 |
| 336 bool InputMethodAuraLinux::SendFakeProcessKeyEvent(int flags) const { | 341 ui::EventDispatchDetails InputMethodAuraLinux::SendFakeProcessKeyEvent( |
| 337 return DispatchKeyEventPostIME( | 342 ui::KeyEvent* event) const { |
| 338 KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, flags)); | 343 KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, event->flags()); |
| 344 ui::EventDispatchDetails details = DispatchKeyEventPostIME(&key_event); | |
| 345 if (key_event.stopped_propagation()) | |
| 346 event->StopPropagation(); | |
| 347 return details; | |
| 339 } | 348 } |
| 340 | 349 |
| 341 void InputMethodAuraLinux::ConfirmCompositionText() { | 350 void InputMethodAuraLinux::ConfirmCompositionText() { |
| 342 TextInputClient* client = GetTextInputClient(); | 351 TextInputClient* client = GetTextInputClient(); |
| 343 if (client && client->HasCompositionText()) | 352 if (client && client->HasCompositionText()) |
| 344 client->ConfirmCompositionText(); | 353 client->ConfirmCompositionText(); |
| 345 | 354 |
| 346 ResetContext(); | 355 ResetContext(); |
| 347 } | 356 } |
| 348 | 357 |
| 349 } // namespace ui | 358 } // namespace ui |
| OLD | NEW |