Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 10119bc

Browse files
committed
Add FlTextInputPlugin
1 parent 3c0ef39 commit 10119bc

File tree

8 files changed

+523
-5
lines changed

8 files changed

+523
-5
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec.cc
12261226
FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec_test.cc
12271227
FILE: ../../../flutter/shell/platform/linux/fl_string_codec.cc
12281228
FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc
1229+
FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.cc
1230+
FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.h
12291231
FILE: ../../../flutter/shell/platform/linux/fl_value.cc
12301232
FILE: ../../../flutter/shell/platform/linux/fl_value_test.cc
12311233
FILE: ../../../flutter/shell/platform/linux/fl_view.cc

shell/platform/common/cpp/text_input_model.cc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ void TextInputModel::AddText(const std::u16string& text) {
8585
selection_base_ = selection_extent_;
8686
}
8787

88+
void TextInputModel::AddText(const std::string& text) {
89+
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>
90+
utf16_converter;
91+
AddText(utf16_converter.from_bytes(text));
92+
}
93+
8894
bool TextInputModel::Backspace() {
8995
if (selection_base_ != selection_extent_) {
9096
DeleteSelected();
@@ -113,6 +119,42 @@ bool TextInputModel::Delete() {
113119
return false;
114120
}
115121

122+
bool TextInputModel::DeleteSurrounding(int offset, int n_chars) {
123+
auto start = selection_base_;
124+
if (offset < 0) {
125+
for (int i = 0; i < -offset; i++) {
126+
// If requested start is before the available text then reduce the
127+
// number of characters to delete
128+
if (start == text_.begin()) {
129+
n_chars = i;
130+
break;
131+
}
132+
start -= IsTrailingSurrogate(*(start - 1)) ? 2 : 1;
133+
}
134+
} else {
135+
for (int i = 0; i < offset && start != text_.end(); i++)
136+
start += IsLeadingSurrogate(*start) ? 2 : 1;
137+
}
138+
139+
auto end = start;
140+
for (int i = 0; i < n_chars && end != text_.end(); i++)
141+
end += IsLeadingSurrogate(*start) ? 2 : 1;
142+
143+
if (start == end)
144+
return false;
145+
146+
auto new_base = text_.erase(start, end);
147+
148+
// Cursor moves only if deleted area is before it
149+
if (offset <= 0)
150+
selection_base_ = new_base;
151+
152+
// Clear selection
153+
selection_extent_ = selection_base_;
154+
155+
return true;
156+
}
157+
116158
bool TextInputModel::MoveCursorToBeginning() {
117159
if (selection_base_ == text_.begin() && selection_extent_ == text_.begin())
118160
return false;
@@ -172,4 +214,13 @@ std::string TextInputModel::GetText() const {
172214
return utf8_converter.to_bytes(text_);
173215
}
174216

217+
int TextInputModel::GetCursorOffset() const {
218+
// Measure the length of the current text up to the cursor
219+
// There is probably a much more efficient way of doing this
220+
auto leading_text = text_.substr(0, selection_base_ - text_.begin());
221+
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>
222+
utf8_converter;
223+
return utf8_converter.to_bytes(leading_text).size();
224+
}
225+
175226
} // namespace flutter

shell/platform/common/cpp/text_input_model.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,18 @@ class TextInputModel {
3333
// code point.
3434
void AddCodePoint(char32_t c);
3535

36-
// Adds a UTF-16 text.
36+
// Adds UTF-16 text.
3737
//
3838
// Either appends after the cursor (when selection base and extent are the
3939
// same), or deletes the selected text, replacing it with the given text.
4040
void AddText(const std::u16string& text);
4141

42+
// Adds UTF-8 text.
43+
//
44+
// Either appends after the cursor (when selection base and extent are the
45+
// same), or deletes the selected text, replacing it with the given text.
46+
void AddText(const std::string& text);
47+
4248
// Deletes either the selection, or one character ahead of the cursor.
4349
//
4450
// Deleting one character ahead of the cursor occurs when the selection base
@@ -47,6 +53,13 @@ class TextInputModel {
4753
// Returns true if any deletion actually occurred.
4854
bool Delete();
4955

56+
// Deletes text near the cursor.
57+
//
58+
// The offset is the number of characters relative to the cursor.
59+
//
60+
// Returns true if any deletion actually occurred.
61+
bool DeleteSurrounding(int offset, int n_chars);
62+
5063
// Deletes either the selection, or one character behind the cursor.
5164
//
5265
// Deleting one character behind the cursor occurs when the selection base
@@ -77,9 +90,12 @@ class TextInputModel {
7790
// Returns true if the cursor could be moved.
7891
bool MoveCursorToEnd();
7992

80-
// Get the current text
93+
// Gets the current text
8194
std::string GetText() const;
8295

96+
// Gets the cursor position as a byte offset in string returned from GetText()
97+
int GetCursorOffset() const;
98+
8399
// The position of the cursor
84100
int selection_base() const {
85101
return static_cast<int>(selection_base_ - text_.begin());

shell/platform/common/cpp/text_input_model_unittests.cc

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ TEST(TextInputModel, AddCodePointSelectionWideCharacter) {
103103
TEST(TextInputModel, AddText) {
104104
auto model = std::make_unique<TextInputModel>("", "");
105105
model->AddText(u"ABCDE");
106-
model->AddText(u"😄");
107-
model->AddText(u"FGHIJ");
106+
model->AddText("😄");
107+
model->AddText("FGHIJ");
108108
EXPECT_EQ(model->selection_base(), 12);
109109
EXPECT_EQ(model->selection_extent(), 12);
110110
EXPECT_STREQ(model->GetText().c_str(), "ABCDE😄FGHIJ");
@@ -113,7 +113,7 @@ TEST(TextInputModel, AddText) {
113113
TEST(TextInputModel, AddTextSelection) {
114114
auto model = std::make_unique<TextInputModel>("", "");
115115
EXPECT_TRUE(model->SetEditingState(1, 4, "ABCDE"));
116-
model->AddText(u"xy");
116+
model->AddText("xy");
117117
EXPECT_EQ(model->selection_base(), 3);
118118
EXPECT_EQ(model->selection_extent(), 3);
119119
EXPECT_STREQ(model->GetText().c_str(), "AxyE");
@@ -173,6 +173,87 @@ TEST(TextInputModel, DeleteSelection) {
173173
EXPECT_STREQ(model->GetText().c_str(), "AE");
174174
}
175175

176+
TEST(TextInputModel, DeleteSurroundingAtCursor) {
177+
auto model = std::make_unique<TextInputModel>("", "");
178+
model->SetEditingState(2, 2, "ABCDE");
179+
EXPECT_TRUE(model->DeleteSurrounding(0, 1));
180+
EXPECT_EQ(model->selection_base(), 2);
181+
EXPECT_EQ(model->selection_extent(), 2);
182+
EXPECT_STREQ(model->GetText().c_str(), "ABDE");
183+
}
184+
185+
TEST(TextInputModel, DeleteSurroundingAtCursorAll) {
186+
auto model = std::make_unique<TextInputModel>("", "");
187+
model->SetEditingState(2, 2, "ABCDE");
188+
EXPECT_TRUE(model->DeleteSurrounding(0, 3));
189+
EXPECT_EQ(model->selection_base(), 2);
190+
EXPECT_EQ(model->selection_extent(), 2);
191+
EXPECT_STREQ(model->GetText().c_str(), "AB");
192+
}
193+
194+
TEST(TextInputModel, DeleteSurroundingAtCursorGreedy) {
195+
auto model = std::make_unique<TextInputModel>("", "");
196+
model->SetEditingState(2, 2, "ABCDE");
197+
EXPECT_TRUE(model->DeleteSurrounding(0, 4));
198+
EXPECT_EQ(model->selection_base(), 2);
199+
EXPECT_EQ(model->selection_extent(), 2);
200+
EXPECT_STREQ(model->GetText().c_str(), "AB");
201+
}
202+
203+
TEST(TextInputModel, DeleteSurroundingBeforeCursor) {
204+
auto model = std::make_unique<TextInputModel>("", "");
205+
model->SetEditingState(2, 2, "ABCDE");
206+
EXPECT_TRUE(model->DeleteSurrounding(-1, 1));
207+
EXPECT_EQ(model->selection_base(), 1);
208+
EXPECT_EQ(model->selection_extent(), 1);
209+
EXPECT_STREQ(model->GetText().c_str(), "ACDE");
210+
}
211+
212+
TEST(TextInputModel, DeleteSurroundingBeforeCursorAll) {
213+
auto model = std::make_unique<TextInputModel>("", "");
214+
model->SetEditingState(2, 2, "ABCDE");
215+
EXPECT_TRUE(model->DeleteSurrounding(-2, 2));
216+
EXPECT_EQ(model->selection_base(), 0);
217+
EXPECT_EQ(model->selection_extent(), 0);
218+
EXPECT_STREQ(model->GetText().c_str(), "CDE");
219+
}
220+
221+
TEST(TextInputModel, DeleteSurroundingBeforeCursorGreedy) {
222+
auto model = std::make_unique<TextInputModel>("", "");
223+
model->SetEditingState(2, 2, "ABCDE");
224+
EXPECT_TRUE(model->DeleteSurrounding(-3, 3));
225+
EXPECT_EQ(model->selection_base(), 0);
226+
EXPECT_EQ(model->selection_extent(), 0);
227+
EXPECT_STREQ(model->GetText().c_str(), "CDE");
228+
}
229+
230+
TEST(TextInputModel, DeleteSurroundingAfterCursor) {
231+
auto model = std::make_unique<TextInputModel>("", "");
232+
model->SetEditingState(2, 2, "ABCDE");
233+
EXPECT_TRUE(model->DeleteSurrounding(1, 1));
234+
EXPECT_EQ(model->selection_base(), 2);
235+
EXPECT_EQ(model->selection_extent(), 2);
236+
EXPECT_STREQ(model->GetText().c_str(), "ABCE");
237+
}
238+
239+
TEST(TextInputModel, DeleteSurroundingAfterCursorAll) {
240+
auto model = std::make_unique<TextInputModel>("", "");
241+
model->SetEditingState(2, 2, "ABCDE");
242+
EXPECT_TRUE(model->DeleteSurrounding(1, 2));
243+
EXPECT_EQ(model->selection_base(), 2);
244+
EXPECT_EQ(model->selection_extent(), 2);
245+
EXPECT_STREQ(model->GetText().c_str(), "ABC");
246+
}
247+
248+
TEST(TextInputModel, DeleteSurroundingAfterCursorGreedy) {
249+
auto model = std::make_unique<TextInputModel>("", "");
250+
model->SetEditingState(2, 2, "ABCDE");
251+
EXPECT_TRUE(model->DeleteSurrounding(1, 3));
252+
EXPECT_EQ(model->selection_base(), 2);
253+
EXPECT_EQ(model->selection_extent(), 2);
254+
EXPECT_STREQ(model->GetText().c_str(), "ABC");
255+
}
256+
176257
TEST(TextInputModel, BackspaceStart) {
177258
auto model = std::make_unique<TextInputModel>("", "");
178259
EXPECT_TRUE(model->SetEditingState(0, 0, "ABCDE"));
@@ -380,4 +461,19 @@ TEST(TextInputModel, MoveCursorToEndSelection) {
380461
EXPECT_STREQ(model->GetText().c_str(), "ABCDE");
381462
}
382463

464+
TEST(TextInputModel, GetCursorOffset) {
465+
auto model = std::make_unique<TextInputModel>("", "");
466+
// These characters take 1, 2, 3 and 4 bytes in UTF-8
467+
model->SetEditingState(0, 0, "$¢€𐍈");
468+
EXPECT_EQ(model->GetCursorOffset(), 0);
469+
EXPECT_TRUE(model->MoveCursorForward());
470+
EXPECT_EQ(model->GetCursorOffset(), 1);
471+
EXPECT_TRUE(model->MoveCursorForward());
472+
EXPECT_EQ(model->GetCursorOffset(), 3);
473+
EXPECT_TRUE(model->MoveCursorForward());
474+
EXPECT_EQ(model->GetCursorOffset(), 6);
475+
EXPECT_TRUE(model->MoveCursorForward());
476+
EXPECT_EQ(model->GetCursorOffset(), 10);
477+
}
478+
383479
} // namespace flutter

shell/platform/linux/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ source_set("flutter_linux") {
9090
"fl_standard_message_codec.cc",
9191
"fl_standard_method_codec.cc",
9292
"fl_string_codec.cc",
93+
"fl_text_input_plugin.cc",
9394
"fl_value.cc",
9495
"fl_view.cc",
9596
]
@@ -104,6 +105,7 @@ source_set("flutter_linux") {
104105
defines = [ "FLUTTER_LINUX_COMPILATION" ]
105106

106107
deps = [
108+
"//flutter/shell/platform/common/cpp:common_input_cpp",
107109
"//flutter/shell/platform/embedder:embedder_with_symbol_prefix",
108110
"//third_party/rapidjson",
109111
]

0 commit comments

Comments
 (0)