blob: 02fb7dcdc8ec0f0787a81fa1dc2714cdbe5cedae [file] [log] [blame]
Jun Mukaia446ee7f2018-07-20 22:55:151// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/exo/text_input.h"
6
Jun Mukaicf1f0362019-02-07 00:39:337#include <algorithm>
8
9#include "base/strings/utf_string_conversions.h"
Jun Mukaia446ee7f2018-07-20 22:55:1510#include "components/exo/surface.h"
11#include "components/exo/wm_helper.h"
12#include "third_party/icu/source/common/unicode/uchar.h"
13#include "ui/aura/window.h"
14#include "ui/aura/window_tree_host.h"
15#include "ui/base/ime/input_method.h"
16#include "ui/events/event.h"
17#include "ui/keyboard/keyboard_controller.h"
18
19namespace exo {
20
21namespace {
22
23ui::InputMethod* GetInputMethod(aura::Window* window) {
24 if (!window || !window->GetHost())
25 return nullptr;
26 return window->GetHost()->GetInputMethod();
27}
28
29} // namespace
30
Jun Mukaicf1f0362019-02-07 00:39:3331size_t OffsetFromUTF8Offset(const base::StringPiece& text, uint32_t offset) {
32 return base::UTF8ToUTF16(text.substr(0, offset)).size();
33}
34
35size_t OffsetFromUTF16Offset(const base::StringPiece16& text, uint32_t offset) {
36 return base::UTF16ToUTF8(text.substr(0, offset)).size();
37}
38
Jun Mukaia446ee7f2018-07-20 22:55:1539TextInput::TextInput(std::unique_ptr<Delegate> delegate)
40 : delegate_(std::move(delegate)) {}
41
42TextInput::~TextInput() {
43 if (keyboard_controller_)
44 keyboard_controller_->RemoveObserver(this);
45 if (input_method_)
46 Deactivate();
47}
48
49void TextInput::Activate(Surface* surface) {
50 DLOG_IF(ERROR, window_) << "Already activated with " << window_;
51 DCHECK(surface);
52
53 window_ = surface->window();
54 AttachInputMethod();
55}
56
57void TextInput::Deactivate() {
58 DetachInputMethod();
59 window_ = nullptr;
60}
61
62void TextInput::ShowVirtualKeyboardIfEnabled() {
63 // Some clients may ask showing virtual keyboard before sending activation.
64 if (!input_method_) {
65 pending_vk_visible_ = true;
66 return;
67 }
68 input_method_->ShowVirtualKeyboardIfEnabled();
69}
70
71void TextInput::HideVirtualKeyboard() {
72 if (keyboard_controller_)
73 keyboard_controller_->HideKeyboardByUser();
74 pending_vk_visible_ = false;
75}
76
77void TextInput::Resync() {
78 if (input_method_)
79 input_method_->OnCaretBoundsChanged(this);
80}
81
82void TextInput::SetSurroundingText(const base::string16& text,
Jun Mukaicf1f0362019-02-07 00:39:3383 uint32_t cursor_pos,
84 uint32_t anchor) {
85 surrounding_text_ = text;
86 cursor_pos_ = gfx::Range(cursor_pos);
87 if (anchor < cursor_pos)
88 cursor_pos_->set_start(anchor);
89 else
90 cursor_pos_->set_end(anchor);
Jun Mukaia446ee7f2018-07-20 22:55:1591}
92
93void TextInput::SetTypeModeFlags(ui::TextInputType type,
94 ui::TextInputMode mode,
95 int flags,
96 bool should_do_learning) {
97 if (!input_method_)
98 return;
99 bool changed = (input_type_ != type);
100 input_type_ = type;
101 input_mode_ = mode;
102 flags_ = flags;
103 should_do_learning_ = should_do_learning;
104 if (changed)
105 input_method_->OnTextInputTypeChanged(this);
106}
107
108void TextInput::SetCaretBounds(const gfx::Rect& bounds) {
109 if (caret_bounds_ == bounds)
110 return;
111 caret_bounds_ = bounds;
112 if (!input_method_)
113 return;
114 input_method_->OnCaretBoundsChanged(this);
115}
116
117void TextInput::SetCompositionText(const ui::CompositionText& composition) {
118 composition_ = composition;
119 delegate_->SetCompositionText(composition);
120}
121
122void TextInput::ConfirmCompositionText() {
123 delegate_->Commit(composition_.text);
124}
125
126void TextInput::ClearCompositionText() {
127 composition_ = ui::CompositionText();
128 delegate_->SetCompositionText(composition_);
129}
130
131void TextInput::InsertText(const base::string16& text) {
132 delegate_->Commit(text);
133}
134
135void TextInput::InsertChar(const ui::KeyEvent& event) {
136 base::char16 ch = event.GetCharacter();
137 if (u_isprint(ch)) {
138 InsertText(base::string16(1, ch));
139 return;
140 }
141 delegate_->SendKey(event);
142}
143
144ui::TextInputType TextInput::GetTextInputType() const {
145 return input_type_;
146}
147
148ui::TextInputMode TextInput::GetTextInputMode() const {
149 return input_mode_;
150}
151
152base::i18n::TextDirection TextInput::GetTextDirection() const {
153 return direction_;
154}
155
156int TextInput::GetTextInputFlags() const {
157 return flags_;
158}
159
160bool TextInput::CanComposeInline() const {
161 return true;
162}
163
164gfx::Rect TextInput::GetCaretBounds() const {
165 return caret_bounds_ + window_->GetBoundsInScreen().OffsetFromOrigin();
166}
167
168bool TextInput::GetCompositionCharacterBounds(uint32_t index,
169 gfx::Rect* rect) const {
170 return false;
171}
172
173bool TextInput::HasCompositionText() const {
174 return !composition_.text.empty();
175}
176
177ui::TextInputClient::FocusReason TextInput::GetFocusReason() const {
178 NOTIMPLEMENTED_LOG_ONCE();
179 return ui::TextInputClient::FOCUS_REASON_OTHER;
180}
181
182bool TextInput::GetTextRange(gfx::Range* range) const {
Jun Mukaicf1f0362019-02-07 00:39:33183 if (!cursor_pos_)
184 return false;
185 range->set_start(0);
186 if (composition_.text.empty()) {
187 range->set_end(surrounding_text_.size());
188 } else {
189 range->set_end(surrounding_text_.size() - cursor_pos_->length() +
190 composition_.text.size());
191 }
192 return true;
Jun Mukaia446ee7f2018-07-20 22:55:15193}
194
195bool TextInput::GetCompositionTextRange(gfx::Range* range) const {
Jun Mukaicf1f0362019-02-07 00:39:33196 if (!cursor_pos_ || composition_.text.empty())
197 return false;
198
199 range->set_start(cursor_pos_->start());
200 range->set_end(cursor_pos_->start() + composition_.text.size());
201 return true;
Jun Mukaia446ee7f2018-07-20 22:55:15202}
203
Yuichiro Hanada65eaaf42018-12-14 07:18:38204bool TextInput::GetEditableSelectionRange(gfx::Range* range) const {
Jun Mukaicf1f0362019-02-07 00:39:33205 if (!cursor_pos_)
206 return false;
207 range->set_start(cursor_pos_->start());
208 range->set_end(cursor_pos_->end());
209 return true;
Jun Mukaia446ee7f2018-07-20 22:55:15210}
211
Yuichiro Hanada65eaaf42018-12-14 07:18:38212bool TextInput::SetEditableSelectionRange(const gfx::Range& range) {
Jun Mukaicf1f0362019-02-07 00:39:33213 if (surrounding_text_.size() < range.GetMax())
214 return false;
215 delegate_->SetCursor(
216 gfx::Range(OffsetFromUTF16Offset(surrounding_text_, range.start()),
217 OffsetFromUTF16Offset(surrounding_text_, range.end())));
218 return true;
Jun Mukaia446ee7f2018-07-20 22:55:15219}
220
221bool TextInput::DeleteRange(const gfx::Range& range) {
Jun Mukaicf1f0362019-02-07 00:39:33222 if (surrounding_text_.size() < range.GetMax())
223 return false;
224 delegate_->DeleteSurroundingText(
225 gfx::Range(OffsetFromUTF16Offset(surrounding_text_, range.start()),
226 OffsetFromUTF16Offset(surrounding_text_, range.end())));
227 return true;
Jun Mukaia446ee7f2018-07-20 22:55:15228}
229
230bool TextInput::GetTextFromRange(const gfx::Range& range,
231 base::string16* text) const {
Jun Mukaicf1f0362019-02-07 00:39:33232 gfx::Range text_range;
233 if (!GetTextRange(&text_range) || !text_range.Contains(range))
234 return false;
235 if (composition_.text.empty() || range.GetMax() <= cursor_pos_->GetMin()) {
236 text->assign(surrounding_text_, range.GetMin(), range.length());
237 return true;
238 }
239 size_t composition_end = cursor_pos_->GetMin() + composition_.text.size();
240 if (range.GetMin() >= composition_end) {
241 size_t start =
242 range.GetMin() - composition_.text.size() + cursor_pos_->length();
243 text->assign(surrounding_text_, start, range.length());
244 return true;
245 }
246
247 size_t start_in_composition = 0;
248 if (range.GetMin() <= cursor_pos_->GetMin()) {
249 text->assign(surrounding_text_, range.GetMin(),
250 cursor_pos_->GetMin() - range.GetMin());
251 } else {
252 start_in_composition = range.GetMin() - cursor_pos_->GetMin();
253 }
254 if (range.GetMax() <= composition_end) {
255 text->append(composition_.text, start_in_composition,
256 range.GetMax() - cursor_pos_->GetMin() - start_in_composition);
257 } else {
258 text->append(composition_.text, start_in_composition,
259 composition_.text.size() - start_in_composition);
260 text->append(surrounding_text_, cursor_pos_->GetMax(),
261 range.GetMax() - composition_end);
262 }
263 return true;
Jun Mukaia446ee7f2018-07-20 22:55:15264}
265
266void TextInput::OnInputMethodChanged() {
267 ui::InputMethod* input_method = GetInputMethod(window_);
268 if (input_method == input_method_)
269 return;
270 input_method_->DetachTextInputClient(this);
271 input_method_ = input_method;
272 input_method_->SetFocusedTextInputClient(this);
273}
274
275bool TextInput::ChangeTextDirectionAndLayoutAlignment(
276 base::i18n::TextDirection direction) {
277 if (direction == direction_)
278 return true;
279 direction_ = direction;
280 delegate_->OnTextDirectionChanged(direction_);
281 return true;
282}
283
Jun Mukaicf1f0362019-02-07 00:39:33284void TextInput::ExtendSelectionAndDelete(size_t before, size_t after) {
285 if (!cursor_pos_)
286 return;
287 uint32_t start =
288 (cursor_pos_->GetMin() < before) ? 0 : (cursor_pos_->GetMin() - before);
289 uint32_t end =
290 std::min(cursor_pos_->GetMax() + after, surrounding_text_.size());
291 delegate_->DeleteSurroundingText(
292 gfx::Range(OffsetFromUTF16Offset(surrounding_text_, start),
293 OffsetFromUTF16Offset(surrounding_text_, end)));
294}
Jun Mukaia446ee7f2018-07-20 22:55:15295
296void TextInput::EnsureCaretNotInRect(const gfx::Rect& rect) {}
297
298bool TextInput::IsTextEditCommandEnabled(ui::TextEditCommand command) const {
299 return false;
300}
301
302void TextInput::SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) {
303}
304
305ukm::SourceId TextInput::GetClientSourceForMetrics() const {
306 NOTIMPLEMENTED_LOG_ONCE();
307 return ukm::kInvalidSourceId;
308}
309
310bool TextInput::ShouldDoLearning() {
311 return should_do_learning_;
312}
313
314void TextInput::OnKeyboardVisibilityStateChanged(bool is_visible) {
315 delegate_->OnVirtualKeyboardVisibilityChanged(is_visible);
316}
317
318void TextInput::AttachInputMethod() {
319 DCHECK(!input_method_);
320
321 ui::InputMethod* input_method = GetInputMethod(window_);
322 if (!input_method) {
323 LOG(ERROR) << "input method not found";
324 return;
325 }
326
327 input_mode_ = ui::TEXT_INPUT_MODE_TEXT;
328 input_type_ = ui::TEXT_INPUT_TYPE_TEXT;
329 input_method_ = input_method;
330 input_method_->SetFocusedTextInputClient(this);
331 delegate_->Activated();
332
333 if (!keyboard_controller_ && keyboard::KeyboardController::HasInstance()) {
334 auto* keyboard_controller = keyboard::KeyboardController::Get();
Steven Bennetts137a18e2018-10-10 19:13:45335 if (keyboard_controller->IsEnabled()) {
Jun Mukaia446ee7f2018-07-20 22:55:15336 keyboard_controller_ = keyboard_controller;
337 keyboard_controller_->AddObserver(this);
338 }
339 }
340
341 if (pending_vk_visible_) {
342 input_method_->ShowVirtualKeyboardIfEnabled();
343 pending_vk_visible_ = false;
344 }
345}
346
347void TextInput::DetachInputMethod() {
348 if (!input_method_) {
349 DLOG(ERROR) << "input method already detached";
350 return;
351 }
352 input_mode_ = ui::TEXT_INPUT_MODE_DEFAULT;
353 input_type_ = ui::TEXT_INPUT_TYPE_NONE;
354 input_method_->DetachTextInputClient(this);
355 input_method_ = nullptr;
356 delegate_->Deactivated();
357}
358
359} // namespace exo