Flutter Engine
The Flutter Engine
text_input_model.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter 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 "flutter/shell/platform/common/text_input_model.h"
6
7#include <algorithm>
8#include <string>
9
10#include "flutter/fml/string_conversion.h"
11
12namespace flutter {
13
14namespace {
15
16// Returns true if |code_point| is a leading surrogate of a surrogate pair.
17bool IsLeadingSurrogate(char32_t code_point) {
18 return (code_point & 0xFFFFFC00) == 0xD800;
19}
20// Returns true if |code_point| is a trailing surrogate of a surrogate pair.
21bool IsTrailingSurrogate(char32_t code_point) {
22 return (code_point & 0xFFFFFC00) == 0xDC00;
23}
24
25} // namespace
26
28
30
31bool TextInputModel::SetText(const std::string& text,
32 const TextRange& selection,
33 const TextRange& composing_range) {
34 text_ = fml::Utf8ToUtf16(text);
37 return false;
38 }
39
40 selection_ = selection;
41 composing_range_ = composing_range;
42 composing_ = !composing_range.collapsed();
43 return true;
44}
45
47 if (composing_ && !range.collapsed()) {
48 return false;
49 }
50 if (!editable_range().Contains(range)) {
51 return false;
52 }
53 selection_ = range;
54 return true;
55}
56
58 size_t cursor_offset) {
59 if (!composing_ || !text_range().Contains(range)) {
60 return false;
61 }
62 composing_range_ = range;
63 selection_ = TextRange(range.start() + cursor_offset);
64 return true;
65}
66
68 composing_ = true;
69 composing_range_ = TextRange(selection_.start());
70}
71
72void TextInputModel::UpdateComposingText(const std::u16string& text,
73 const TextRange& selection) {
74 // Preserve selection if we get a no-op update to the composing region.
75 if (text.length() == 0 && composing_range_.collapsed()) {
76 return;
77 }
78 const TextRange& rangeToDelete =
79 composing_range_.collapsed() ? selection_ : composing_range_;
80 text_.replace(rangeToDelete.start(), rangeToDelete.length(), text);
81 composing_range_.set_end(composing_range_.start() + text.length());
82 selection_ = TextRange(selection.start() + composing_range_.start(),
83 selection.extent() + composing_range_.start());
84}
85
86void TextInputModel::UpdateComposingText(const std::u16string& text) {
88}
89
90void TextInputModel::UpdateComposingText(const std::string& text) {
92}
93
95 // Preserve selection if no composing text was entered.
96 if (composing_range_.collapsed()) {
97 return;
98 }
99 composing_range_ = TextRange(composing_range_.end());
100 selection_ = composing_range_;
101}
102
104 composing_ = false;
105 composing_range_ = TextRange(0);
106}
107
108bool TextInputModel::DeleteSelected() {
109 if (selection_.collapsed()) {
110 return false;
111 }
112 size_t start = selection_.start();
113 text_.erase(start, selection_.length());
114 selection_ = TextRange(start);
115 if (composing_) {
116 // This occurs only immediately after composing has begun with a selection.
117 composing_range_ = selection_;
118 }
119 return true;
120}
121
123 if (c <= 0xFFFF) {
124 AddText(std::u16string({static_cast<char16_t>(c)}));
125 } else {
126 char32_t to_decompose = c - 0x10000;
127 AddText(std::u16string({
128 // High surrogate.
129 static_cast<char16_t>((to_decompose >> 10) + 0xd800),
130 // Low surrogate.
131 static_cast<char16_t>((to_decompose % 0x400) + 0xdc00),
132 }));
133 }
134}
135
136void TextInputModel::AddText(const std::u16string& text) {
137 DeleteSelected();
138 if (composing_) {
139 // Delete the current composing text, set the cursor to composing start.
140 text_.erase(composing_range_.start(), composing_range_.length());
141 selection_ = TextRange(composing_range_.start());
142 composing_range_.set_end(composing_range_.start() + text.length());
143 }
144 size_t position = selection_.position();
145 text_.insert(position, text);
146 selection_ = TextRange(position + text.length());
147}
148
149void TextInputModel::AddText(const std::string& text) {
151}
152
154 if (DeleteSelected()) {
155 return true;
156 }
157 // There is no selection. Delete the preceding codepoint.
158 size_t position = selection_.position();
159 if (position != editable_range().start()) {
160 int count = IsTrailingSurrogate(text_.at(position - 1)) ? 2 : 1;
161 text_.erase(position - count, count);
162 selection_ = TextRange(position - count);
163 if (composing_) {
164 composing_range_.set_end(composing_range_.end() - count);
165 }
166 return true;
167 }
168 return false;
169}
170
172 if (DeleteSelected()) {
173 return true;
174 }
175 // There is no selection. Delete the preceding codepoint.
176 size_t position = selection_.position();
177 if (position < editable_range().end()) {
178 int count = IsLeadingSurrogate(text_.at(position)) ? 2 : 1;
179 text_.erase(position, count);
180 if (composing_) {
181 composing_range_.set_end(composing_range_.end() - count);
182 }
183 return true;
184 }
185 return false;
186}
187
188bool TextInputModel::DeleteSurrounding(int offset_from_cursor, int count) {
189 size_t max_pos = editable_range().end();
190 size_t start = selection_.extent();
191 if (offset_from_cursor < 0) {
192 for (int i = 0; i < -offset_from_cursor; i++) {
193 // If requested start is before the available text then reduce the
194 // number of characters to delete.
195 if (start == editable_range().start()) {
196 count = i;
197 break;
198 }
199 start -= IsTrailingSurrogate(text_.at(start - 1)) ? 2 : 1;
200 }
201 } else {
202 for (int i = 0; i < offset_from_cursor && start != max_pos; i++) {
203 start += IsLeadingSurrogate(text_.at(start)) ? 2 : 1;
204 }
205 }
206
207 auto end = start;
208 for (int i = 0; i < count && end != max_pos; i++) {
209 end += IsLeadingSurrogate(text_.at(start)) ? 2 : 1;
210 }
211
212 if (start == end) {
213 return false;
214 }
215
216 auto deleted_length = end - start;
217 text_.erase(start, deleted_length);
218
219 // Cursor moves only if deleted area is before it.
220 selection_ = TextRange(offset_from_cursor <= 0 ? start : selection_.start());
221
222 // Adjust composing range.
223 if (composing_) {
224 composing_range_.set_end(composing_range_.end() - deleted_length);
225 }
226 return true;
227}
228
230 size_t min_pos = editable_range().start();
231 if (selection_.collapsed() && selection_.position() == min_pos) {
232 return false;
233 }
234 selection_ = TextRange(min_pos);
235 return true;
236}
237
239 size_t max_pos = editable_range().end();
240 if (selection_.collapsed() && selection_.position() == max_pos) {
241 return false;
242 }
243 selection_ = TextRange(max_pos);
244 return true;
245}
246
248 size_t min_pos = editable_range().start();
249 if (selection_.collapsed() && selection_.position() == min_pos) {
250 return false;
251 }
252 selection_ = TextRange(selection_.base(), min_pos);
253 return true;
254}
255
257 size_t max_pos = editable_range().end();
258 if (selection_.collapsed() && selection_.position() == max_pos) {
259 return false;
260 }
261 selection_ = TextRange(selection_.base(), max_pos);
262 return true;
263}
264
266 // If there's a selection, move to the end of the selection.
267 if (!selection_.collapsed()) {
268 selection_ = TextRange(selection_.end());
269 return true;
270 }
271 // Otherwise, move the cursor forward.
272 size_t position = selection_.position();
273 if (position != editable_range().end()) {
274 int count = IsLeadingSurrogate(text_.at(position)) ? 2 : 1;
275 selection_ = TextRange(position + count);
276 return true;
277 }
278 return false;
279}
280
282 // If there's a selection, move to the beginning of the selection.
283 if (!selection_.collapsed()) {
284 selection_ = TextRange(selection_.start());
285 return true;
286 }
287 // Otherwise, move the cursor backward.
288 size_t position = selection_.position();
289 if (position != editable_range().start()) {
290 int count = IsTrailingSurrogate(text_.at(position - 1)) ? 2 : 1;
291 selection_ = TextRange(position - count);
292 return true;
293 }
294 return false;
295}
296
297std::string TextInputModel::GetText() const {
298 return fml::Utf16ToUtf8(text_);
299}
300
302 // Measure the length of the current text up to the selection extent.
303 // There is probably a much more efficient way of doing this.
304 auto leading_text = text_.substr(0, selection_.extent());
305 return fml::Utf16ToUtf8(leading_text).size();
306}
307
308} // namespace flutter
int count
Definition: FontMgrTest.cpp:50
TextRange selection() const
std::string GetText() const
bool DeleteSurrounding(int offset_from_cursor, int count)
void AddText(const std::u16string &text)
void AddCodePoint(char32_t c)
bool SetComposingRange(const TextRange &range, size_t cursor_offset)
TextRange text_range() const
TextRange composing_range() const
bool SetSelection(const TextRange &range)
bool SetText(const std::string &text, const TextRange &selection=TextRange(0), const TextRange &composing_range=TextRange(0))
void UpdateComposingText(const std::u16string &text, const TextRange &selection)
void set_end(size_t pos)
Definition: text_range.h:57
size_t position() const
Definition: text_range.h:68
size_t start() const
Definition: text_range.h:42
size_t base() const
Definition: text_range.h:30
bool collapsed() const
Definition: text_range.h:77
size_t length() const
Definition: text_range.h:74
size_t extent() const
Definition: text_range.h:36
size_t end() const
Definition: text_range.h:54
glong glong end
std::u16string text
bool Contains(const Container &container, const Value &value)
std::string Utf16ToUtf8(const std::u16string_view string)
std::u16string Utf8ToUtf16(const std::string_view string)
SkRange< size_t > TextRange
Definition: TextStyle.h:337