Flutter macOS Embedder
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 
6 
7 #include <algorithm>
8 #include <string>
9 
10 #include "flutter/fml/string_conversion.h"
11 
12 namespace flutter {
13 
14 namespace {
15 
16 // Returns true if |code_point| is a leading surrogate of a surrogate pair.
17 bool 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.
21 bool IsTrailingSurrogate(char32_t code_point) {
22  return (code_point & 0xFFFFFC00) == 0xDC00;
23 }
24 
25 } // namespace
26 
28 
30 
31 bool TextInputModel::SetText(const std::string& text,
32  const TextRange& selection,
33  const TextRange& composing_range) {
34  text_ = fml::Utf8ToUtf16(text);
35  if (!text_range().Contains(selection) ||
36  !text_range().Contains(composing_range)) {
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 
72 void TextInputModel::UpdateComposingText(const std::u16string& text) {
73  // Preserve selection if we get a no-op update to the composing region.
74  if (text.length() == 0 && composing_range_.collapsed()) {
75  return;
76  }
77  DeleteSelected();
78  text_.replace(composing_range_.start(), composing_range_.length(), text);
79  composing_range_.set_end(composing_range_.start() + text.length());
80  selection_ = TextRange(composing_range_.end());
81 }
82 
83 void TextInputModel::UpdateComposingText(const std::string& text) {
84  UpdateComposingText(fml::Utf8ToUtf16(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  AddText(fml::Utf8ToUtf16(text));
144 }
145 
147  if (DeleteSelected()) {
148  return true;
149  }
150  // There is no selection. Delete the preceding codepoint.
151  size_t position = selection_.position();
152  if (position != editable_range().start()) {
153  int count = IsTrailingSurrogate(text_.at(position - 1)) ? 2 : 1;
154  text_.erase(position - count, count);
155  selection_ = TextRange(position - count);
156  if (composing_) {
157  composing_range_.set_end(composing_range_.end() - count);
158  }
159  return true;
160  }
161  return false;
162 }
163 
165  if (DeleteSelected()) {
166  return true;
167  }
168  // There is no selection. Delete the preceding codepoint.
169  size_t position = selection_.position();
170  if (position < editable_range().end()) {
171  int count = IsLeadingSurrogate(text_.at(position)) ? 2 : 1;
172  text_.erase(position, count);
173  if (composing_) {
174  composing_range_.set_end(composing_range_.end() - count);
175  }
176  return true;
177  }
178  return false;
179 }
180 
181 bool TextInputModel::DeleteSurrounding(int offset_from_cursor, int count) {
182  size_t max_pos = editable_range().end();
183  size_t start = selection_.extent();
184  if (offset_from_cursor < 0) {
185  for (int i = 0; i < -offset_from_cursor; i++) {
186  // If requested start is before the available text then reduce the
187  // number of characters to delete.
188  if (start == editable_range().start()) {
189  count = i;
190  break;
191  }
192  start -= IsTrailingSurrogate(text_.at(start - 1)) ? 2 : 1;
193  }
194  } else {
195  for (int i = 0; i < offset_from_cursor && start != max_pos; i++) {
196  start += IsLeadingSurrogate(text_.at(start)) ? 2 : 1;
197  }
198  }
199 
200  auto end = start;
201  for (int i = 0; i < count && end != max_pos; i++) {
202  end += IsLeadingSurrogate(text_.at(start)) ? 2 : 1;
203  }
204 
205  if (start == end) {
206  return false;
207  }
208 
209  auto deleted_length = end - start;
210  text_.erase(start, deleted_length);
211 
212  // Cursor moves only if deleted area is before it.
213  selection_ = TextRange(offset_from_cursor <= 0 ? start : selection_.start());
214 
215  // Adjust composing range.
216  if (composing_) {
217  composing_range_.set_end(composing_range_.end() - deleted_length);
218  }
219  return true;
220 }
221 
223  size_t min_pos = editable_range().start();
224  if (selection_.collapsed() && selection_.position() == min_pos) {
225  return false;
226  }
227  selection_ = TextRange(min_pos);
228  return true;
229 }
230 
232  size_t max_pos = editable_range().end();
233  if (selection_.collapsed() && selection_.position() == max_pos) {
234  return false;
235  }
236  selection_ = TextRange(max_pos);
237  return true;
238 }
239 
241  size_t min_pos = editable_range().start();
242  if (selection_.collapsed() && selection_.position() == min_pos) {
243  return false;
244  }
245  selection_ = TextRange(selection_.base(), min_pos);
246  return true;
247 }
248 
250  size_t max_pos = editable_range().end();
251  if (selection_.collapsed() && selection_.position() == max_pos) {
252  return false;
253  }
254  selection_ = TextRange(selection_.base(), max_pos);
255  return true;
256 }
257 
259  // If there's a selection, move to the end of the selection.
260  if (!selection_.collapsed()) {
261  selection_ = TextRange(selection_.end());
262  return true;
263  }
264  // Otherwise, move the cursor forward.
265  size_t position = selection_.position();
266  if (position != editable_range().end()) {
267  int count = IsLeadingSurrogate(text_.at(position)) ? 2 : 1;
268  selection_ = TextRange(position + count);
269  return true;
270  }
271  return false;
272 }
273 
275  // If there's a selection, move to the beginning of the selection.
276  if (!selection_.collapsed()) {
277  selection_ = TextRange(selection_.start());
278  return true;
279  }
280  // Otherwise, move the cursor backward.
281  size_t position = selection_.position();
282  if (position != editable_range().start()) {
283  int count = IsTrailingSurrogate(text_.at(position - 1)) ? 2 : 1;
284  selection_ = TextRange(position - count);
285  return true;
286  }
287  return false;
288 }
289 
290 std::string TextInputModel::GetText() const {
291  return fml::Utf16ToUtf8(text_);
292 }
293 
295  // Measure the length of the current text up to the selection extent.
296  // There is probably a much more efficient way of doing this.
297  auto leading_text = text_.substr(0, selection_.extent());
298  return fml::Utf16ToUtf8(leading_text).size();
299 }
300 
301 } // namespace flutter
flutter::TextInputModel::MoveCursorToBeginning
bool MoveCursorToBeginning()
Definition: text_input_model.cc:222
flutter::TextInputModel::SetText
bool SetText(const std::string &text, const TextRange &selection=TextRange(0), const TextRange &composing_range=TextRange(0))
Definition: text_input_model.cc:31
flutter::TextRange::end
size_t end() const
Definition: text_range.h:54
flutter::TextInputModel::text_range
TextRange text_range() const
Definition: text_input_model.h:193
flutter::TextInputModel::SelectToEnd
bool SelectToEnd()
Definition: text_input_model.cc:249
flutter::TextInputModel::composing_range
TextRange composing_range() const
Definition: text_input_model.h:201
flutter::TextRange::position
size_t position() const
Definition: text_range.h:68
flutter::TextInputModel::selection
TextRange selection() const
Definition: text_input_model.h:196
flutter::TextInputModel::BeginComposing
void BeginComposing()
Definition: text_input_model.cc:67
flutter::TextInputModel::~TextInputModel
virtual ~TextInputModel()
flutter::TextInputModel::DeleteSurrounding
bool DeleteSurrounding(int offset_from_cursor, int count)
Definition: text_input_model.cc:181
text_input_model.h
flutter::TextRange
Definition: text_range.h:19
flutter::TextRange::base
size_t base() const
Definition: text_range.h:30
flutter::TextInputModel::TextInputModel
TextInputModel()
flutter::TextInputModel::UpdateComposingText
void UpdateComposingText(const std::u16string &text)
Definition: text_input_model.cc:72
flutter
Definition: AccessibilityBridgeMac.h:16
flutter::TextInputModel::Backspace
bool Backspace()
Definition: text_input_model.cc:146
flutter::TextInputModel::GetText
std::string GetText() const
Definition: text_input_model.cc:290
flutter::TextInputModel::SetSelection
bool SetSelection(const TextRange &range)
Definition: text_input_model.cc:46
flutter::TextInputModel::SelectToBeginning
bool SelectToBeginning()
Definition: text_input_model.cc:240
flutter::TextRange::collapsed
bool collapsed() const
Definition: text_range.h:77
flutter::TextInputModel::SetComposingRange
bool SetComposingRange(const TextRange &range, size_t cursor_offset)
Definition: text_input_model.cc:57
flutter::TextInputModel::MoveCursorForward
bool MoveCursorForward()
Definition: text_input_model.cc:258
flutter::TextInputModel::GetCursorOffset
int GetCursorOffset() const
Definition: text_input_model.cc:294
flutter::TextInputModel::CommitComposing
void CommitComposing()
Definition: text_input_model.cc:87
flutter::TextRange::extent
size_t extent() const
Definition: text_range.h:36
flutter::TextInputModel::EndComposing
void EndComposing()
Definition: text_input_model.cc:96
flutter::TextInputModel::MoveCursorToEnd
bool MoveCursorToEnd()
Definition: text_input_model.cc:231
flutter::TextRange::start
size_t start() const
Definition: text_range.h:42
flutter::TextRange::length
size_t length() const
Definition: text_range.h:74
flutter::TextInputModel::MoveCursorBack
bool MoveCursorBack()
Definition: text_input_model.cc:274
flutter::TextRange::set_end
void set_end(size_t pos)
Definition: text_range.h:57
flutter::TextInputModel::AddText
void AddText(const std::u16string &text)
Definition: text_input_model.cc:129
flutter::TextInputModel::Delete
bool Delete()
Definition: text_input_model.cc:164
flutter::TextInputModel::AddCodePoint
void AddCodePoint(char32_t c)
Definition: text_input_model.cc:115