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