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 SendFakeProcessKeyEvent(event->flags()); |
James Su
2015/07/31 11:43:57
This method should return ui::EventDispatchDetails
Shu Chen
2015/08/03 01:44:47
Done.
| |
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()) { |
James Su
2015/07/31 11:43:58
needs to check details.target_destroyed here.
Shu Chen
2015/08/03 01:44:47
Done.
| |
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 } |
113 } | 108 } |
114 | 109 |
115 if (composition_changed_ && !IsTextInputTypeNone()) { | 110 if (composition_changed_ && !IsTextInputTypeNone()) { |
116 // If composition changed, does SetComposition if composition is not empty. | 111 // If composition changed, does SetComposition if composition is not empty. |
117 // And ClearComposition if composition is empty. | 112 // And ClearComposition if composition is empty. |
118 if (!composition_.text.empty()) | 113 if (!composition_.text.empty()) |
119 client->SetCompositionText(composition_); | 114 client->SetCompositionText(composition_); |
120 else if (result_text_.empty()) | 115 else if (result_text_.empty()) |
121 client->ClearCompositionText(); | 116 client->ClearCompositionText(); |
122 } | 117 } |
123 | 118 |
124 // Makes sure the cached composition is cleared after committing any text or | 119 // Makes sure the cached composition is cleared after committing any text or |
125 // cleared composition. | 120 // cleared composition. |
126 if (!client->HasCompositionText()) | 121 if (!client->HasCompositionText()) |
127 composition_.Clear(); | 122 composition_.Clear(); |
128 | 123 |
129 if (!filtered) { | 124 if (!filtered) { |
James Su
2015/07/31 11:43:57
just noticed that we send nothing if a key up even
Shu Chen
2015/08/03 01:44:47
This behavior has not been changed for years.
Wha
| |
130 { | 125 details = DispatchKeyEventPostIME(event); |
131 base::AutoReset<bool*> auto_reset(&destroyed_ptr_, &destroyed); | 126 if (details.dispatcher_destroyed) |
132 handled = DispatchKeyEventPostIME(event); | 127 return; |
133 if (destroyed) | 128 if (event->stopped_propagation()) { |
James Su
2015/07/31 11:43:57
needs to check details.target_destroyed here.
Shu Chen
2015/08/03 01:44:47
Done.
| |
134 return true; | 129 ResetContext(); |
130 return; | |
135 } | 131 } |
136 if (handled) { | 132 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_|, | 133 // 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 | 134 // 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 | 135 // 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 | 136 // control-key event (ctrl-a, return, tab, etc.). We need to send the |
145 // character to the focused text input client by calling | 137 // character to the focused text input client by calling |
146 // TextInputClient::InsertChar(). | 138 // TextInputClient::InsertChar(). |
147 // Note: don't use |client| and use GetTextInputClient() here because | 139 // Note: don't use |client| and use GetTextInputClient() here because |
148 // DispatchKeyEventPostIME may cause the current text input client change. | 140 // DispatchKeyEventPostIME may cause the current text input client change. |
149 base::char16 ch = event.GetCharacter(); | 141 base::char16 ch = event->GetCharacter(); |
150 if (ch && GetTextInputClient()) | 142 if (ch && GetTextInputClient()) |
151 GetTextInputClient()->InsertChar(ch, event.flags()); | 143 GetTextInputClient()->InsertChar(ch, event->flags()); |
152 } | 144 } |
153 } | 145 } |
154 | |
155 return true; | |
156 } | 146 } |
157 | 147 |
158 void InputMethodAuraLinux::UpdateContextFocusState() { | 148 void InputMethodAuraLinux::UpdateContextFocusState() { |
159 bool old_text_input_type = text_input_type_; | 149 bool old_text_input_type = text_input_type_; |
160 text_input_type_ = GetTextInputType(); | 150 text_input_type_ = GetTextInputType(); |
161 | 151 |
162 // We only focus in |context_| when the focus is in a textfield. | 152 // We only focus in |context_| when the focus is in a textfield. |
163 if (old_text_input_type != TEXT_INPUT_TYPE_NONE && | 153 if (old_text_input_type != TEXT_INPUT_TYPE_NONE && |
164 text_input_type_ == TEXT_INPUT_TYPE_NONE) { | 154 text_input_type_ == TEXT_INPUT_TYPE_NONE) { |
165 context_->Blur(); | 155 context_->Blur(); |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
326 bool InputMethodAuraLinux::HasInputMethodResult() { | 316 bool InputMethodAuraLinux::HasInputMethodResult() { |
327 return !result_text_.empty() || composition_changed_; | 317 return !result_text_.empty() || composition_changed_; |
328 } | 318 } |
329 | 319 |
330 bool InputMethodAuraLinux::NeedInsertChar() const { | 320 bool InputMethodAuraLinux::NeedInsertChar() const { |
331 return IsTextInputTypeNone() || | 321 return IsTextInputTypeNone() || |
332 (!composition_changed_ && composition_.text.empty() && | 322 (!composition_changed_ && composition_.text.empty() && |
333 result_text_.length() == 1); | 323 result_text_.length() == 1); |
334 } | 324 } |
335 | 325 |
336 bool InputMethodAuraLinux::SendFakeProcessKeyEvent(int flags) const { | 326 void InputMethodAuraLinux::SendFakeProcessKeyEvent(int flags) const { |
337 return DispatchKeyEventPostIME( | 327 KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, flags); |
338 KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, flags)); | 328 ignore_result(DispatchKeyEventPostIME(&key_event)); |
339 } | 329 } |
340 | 330 |
341 void InputMethodAuraLinux::ConfirmCompositionText() { | 331 void InputMethodAuraLinux::ConfirmCompositionText() { |
342 TextInputClient* client = GetTextInputClient(); | 332 TextInputClient* client = GetTextInputClient(); |
343 if (client && client->HasCompositionText()) | 333 if (client && client->HasCompositionText()) |
344 client->ConfirmCompositionText(); | 334 client->ConfirmCompositionText(); |
345 | 335 |
346 ResetContext(); | 336 ResetContext(); |
347 } | 337 } |
348 | 338 |
349 } // namespace ui | 339 } // namespace ui |
OLD | NEW |