From 4c65ee274d35e9185860a3a704cb76a923d5fc9f Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 14 Oct 2020 17:55:30 -0700 Subject: [PATCH] Migrate TextInputPlugin API to TextRange Replaces selection_base() and selection_extent() with selection() and SetSelection(int, int) with SetSelection(range). This also adds the following convenience methods to TextRange: * reversed() * Contains(size_t position) * Contains(const TextRange& range) as well as operator== for use in unit tests. When Flutter migrates to C++20, we can replace that method with a default declaration. --- shell/platform/common/cpp/text_input_model.cc | 7 +- shell/platform/common/cpp/text_input_model.h | 14 +- .../common/cpp/text_input_model_unittests.cc | 316 +++++++----------- shell/platform/common/cpp/text_range.h | 17 + .../common/cpp/text_range_unittests.cc | 119 +++++++ shell/platform/glfw/text_input_plugin.cc | 8 +- shell/platform/linux/fl_text_input_plugin.cc | 13 +- shell/platform/windows/text_input_plugin.cc | 8 +- 8 files changed, 287 insertions(+), 215 deletions(-) diff --git a/shell/platform/common/cpp/text_input_model.cc b/shell/platform/common/cpp/text_input_model.cc index 1ef5b6f3883e9..da0c1fe1e5ed8 100644 --- a/shell/platform/common/cpp/text_input_model.cc +++ b/shell/platform/common/cpp/text_input_model.cc @@ -41,12 +41,11 @@ void TextInputModel::SetText(const std::string& text) { selection_ = TextRange(0); } -bool TextInputModel::SetSelection(size_t base, size_t extent) { - size_t max_pos = text_.length(); - if (base > max_pos || extent > max_pos) { +bool TextInputModel::SetSelection(const TextRange& range) { + if (!text_range().Contains(range)) { return false; } - selection_ = TextRange(base, extent); + selection_ = range; return true; } diff --git a/shell/platform/common/cpp/text_input_model.h b/shell/platform/common/cpp/text_input_model.h index c5753f85e1218..c49f786f0a9c0 100644 --- a/shell/platform/common/cpp/text_input_model.h +++ b/shell/platform/common/cpp/text_input_model.h @@ -27,8 +27,8 @@ class TextInputModel { // Attempts to set the text selection. // - // Returns false if the base or extent are out of bounds. - bool SetSelection(size_t base, size_t extent); + // Returns false if the selection is not within the bounds of the text. + bool SetSelection(const TextRange& range); // Adds a Unicode code point. // @@ -105,11 +105,8 @@ class TextInputModel { // GetText(). int GetCursorOffset() const; - // The position where the selection starts. - int selection_base() const { return selection_.base(); } - - // The position of the cursor. - int selection_extent() const { return selection_.extent(); } + // The current selection. + TextRange selection() const { return selection_; } private: // Deletes the current selection, if any. @@ -118,6 +115,9 @@ class TextInputModel { // reset to the start of the selected range. bool DeleteSelected(); + // Returns a range covering the entire text. + TextRange text_range() const { return TextRange(0, text_.length()); } + std::u16string text_; TextRange selection_ = TextRange(0); }; diff --git a/shell/platform/common/cpp/text_input_model_unittests.cc b/shell/platform/common/cpp/text_input_model_unittests.cc index 86c4771cd5246..730f2b9905428 100644 --- a/shell/platform/common/cpp/text_input_model_unittests.cc +++ b/shell/platform/common/cpp/text_input_model_unittests.cc @@ -41,65 +41,58 @@ TEST(TextInputModel, SetTextReplaceText) { TEST(TextInputModel, SetTextResetsSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(3, 3)); - EXPECT_EQ(model->selection_base(), 3); - EXPECT_EQ(model->selection_extent(), 3); + EXPECT_TRUE(model->SetSelection(TextRange(3))); + EXPECT_EQ(model->selection(), TextRange(3)); model->SetText("FGHJI"); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); } TEST(TextInputModel, SetSelectionStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(0, 0)); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_TRUE(model->SetSelection(TextRange(0))); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, SetSelectionMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_TRUE(model->SetSelection(TextRange(2))); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, SetSelectionEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(5, 5)); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, SetSelectionWthExtent) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 4); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); + EXPECT_EQ(model->selection(), TextRange(1, 4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, SetSelectionReverseExtent) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); - EXPECT_EQ(model->selection_base(), 4); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); + EXPECT_EQ(model->selection(), TextRange(4, 1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, SetSelectionOutsideString) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_FALSE(model->SetSelection(4, 6)); - EXPECT_FALSE(model->SetSelection(5, 6)); - EXPECT_FALSE(model->SetSelection(6, 6)); + EXPECT_FALSE(model->SetSelection(TextRange(4, 6))); + EXPECT_FALSE(model->SetSelection(TextRange(5, 6))); + EXPECT_FALSE(model->SetSelection(TextRange(6, 6))); } TEST(TextInputModel, AddCodePoint) { @@ -109,48 +102,43 @@ TEST(TextInputModel, AddCodePoint) { model->AddCodePoint(0x1f604); model->AddCodePoint('D'); model->AddCodePoint('E'); - EXPECT_EQ(model->selection_base(), 6); - EXPECT_EQ(model->selection_extent(), 6); + EXPECT_EQ(model->selection(), TextRange(6)); EXPECT_STREQ(model->GetText().c_str(), "ABπŸ˜„DE"); } TEST(TextInputModel, AddCodePointSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); model->AddCodePoint('x'); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "AxE"); } TEST(TextInputModel, AddCodePointReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); model->AddCodePoint('x'); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "AxE"); } TEST(TextInputModel, AddCodePointSelectionWideCharacter) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); model->AddCodePoint(0x1f604); - EXPECT_EQ(model->selection_base(), 3); - EXPECT_EQ(model->selection_extent(), 3); + EXPECT_EQ(model->selection(), TextRange(3)); EXPECT_STREQ(model->GetText().c_str(), "AπŸ˜„E"); } TEST(TextInputModel, AddCodePointReverseSelectionWideCharacter) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); model->AddCodePoint(0x1f604); - EXPECT_EQ(model->selection_base(), 3); - EXPECT_EQ(model->selection_extent(), 3); + EXPECT_EQ(model->selection(), TextRange(3)); EXPECT_STREQ(model->GetText().c_str(), "AπŸ˜„E"); } @@ -159,498 +147,448 @@ TEST(TextInputModel, AddText) { model->AddText(u"ABCDE"); model->AddText("πŸ˜„"); model->AddText("FGHIJ"); - EXPECT_EQ(model->selection_base(), 12); - EXPECT_EQ(model->selection_extent(), 12); + EXPECT_EQ(model->selection(), TextRange(12)); EXPECT_STREQ(model->GetText().c_str(), "ABCDEπŸ˜„FGHIJ"); } TEST(TextInputModel, AddTextSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); model->AddText("xy"); - EXPECT_EQ(model->selection_base(), 3); - EXPECT_EQ(model->selection_extent(), 3); + EXPECT_EQ(model->selection(), TextRange(3)); EXPECT_STREQ(model->GetText().c_str(), "AxyE"); } TEST(TextInputModel, AddTextReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); model->AddText("xy"); - EXPECT_EQ(model->selection_base(), 3); - EXPECT_EQ(model->selection_extent(), 3); + EXPECT_EQ(model->selection(), TextRange(3)); EXPECT_STREQ(model->GetText().c_str(), "AxyE"); } TEST(TextInputModel, AddTextSelectionWideCharacter) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); model->AddText(u"πŸ˜„πŸ™ƒ"); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "AπŸ˜„πŸ™ƒE"); } TEST(TextInputModel, AddTextReverseSelectionWideCharacter) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); model->AddText(u"πŸ˜„πŸ™ƒ"); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "AπŸ˜„πŸ™ƒE"); } TEST(TextInputModel, DeleteStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(0, 0)); + EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); ASSERT_TRUE(model->Delete()); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "BCDE"); } TEST(TextInputModel, DeleteMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); ASSERT_TRUE(model->Delete()); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "ABDE"); } TEST(TextInputModel, DeleteEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(5, 5)); + EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); ASSERT_FALSE(model->Delete()); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, DeleteWideCharacters) { auto model = std::make_unique(); model->SetText("πŸ˜„πŸ™ƒπŸ€ͺ🧐"); - EXPECT_TRUE(model->SetSelection(4, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 4))); ASSERT_TRUE(model->Delete()); - EXPECT_EQ(model->selection_base(), 4); - EXPECT_EQ(model->selection_extent(), 4); + EXPECT_EQ(model->selection(), TextRange(4)); EXPECT_STREQ(model->GetText().c_str(), "πŸ˜„πŸ™ƒπŸ§"); } TEST(TextInputModel, DeleteSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); ASSERT_TRUE(model->Delete()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "AE"); } TEST(TextInputModel, DeleteReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); ASSERT_TRUE(model->Delete()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "AE"); } TEST(TextInputModel, DeleteSurroundingAtCursor) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(0, 1)); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "ABDE"); } TEST(TextInputModel, DeleteSurroundingAtCursorAll) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(0, 3)); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "AB"); } TEST(TextInputModel, DeleteSurroundingAtCursorGreedy) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(0, 4)); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "AB"); } TEST(TextInputModel, DeleteSurroundingBeforeCursor) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(-1, 1)); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "ACDE"); } TEST(TextInputModel, DeleteSurroundingBeforeCursorAll) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(-2, 2)); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "CDE"); } TEST(TextInputModel, DeleteSurroundingBeforeCursorGreedy) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(-3, 3)); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "CDE"); } TEST(TextInputModel, DeleteSurroundingAfterCursor) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(1, 1)); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "ABCE"); } TEST(TextInputModel, DeleteSurroundingAfterCursorAll) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(1, 2)); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "ABC"); } TEST(TextInputModel, DeleteSurroundingAfterCursorGreedy) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->DeleteSurrounding(1, 3)); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "ABC"); } TEST(TextInputModel, DeleteSurroundingSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 3)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 3))); EXPECT_TRUE(model->DeleteSurrounding(0, 1)); - EXPECT_EQ(model->selection_base(), 3); - EXPECT_EQ(model->selection_extent(), 3); + EXPECT_EQ(model->selection(), TextRange(3)); EXPECT_STREQ(model->GetText().c_str(), "ABCE"); } TEST(TextInputModel, DeleteSurroundingReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 3)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 3))); EXPECT_TRUE(model->DeleteSurrounding(0, 1)); - EXPECT_EQ(model->selection_base(), 3); - EXPECT_EQ(model->selection_extent(), 3); + EXPECT_EQ(model->selection(), TextRange(3)); EXPECT_STREQ(model->GetText().c_str(), "ABCE"); } TEST(TextInputModel, BackspaceStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(0, 0)); + EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); ASSERT_FALSE(model->Backspace()); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, BackspaceMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); ASSERT_TRUE(model->Backspace()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "ACDE"); } TEST(TextInputModel, BackspaceEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(5, 5)); + EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); ASSERT_TRUE(model->Backspace()); - EXPECT_EQ(model->selection_base(), 4); - EXPECT_EQ(model->selection_extent(), 4); + EXPECT_EQ(model->selection(), TextRange(4)); EXPECT_STREQ(model->GetText().c_str(), "ABCD"); } TEST(TextInputModel, BackspaceWideCharacters) { auto model = std::make_unique(); model->SetText("πŸ˜„πŸ™ƒπŸ€ͺ🧐"); - EXPECT_TRUE(model->SetSelection(4, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 4))); ASSERT_TRUE(model->Backspace()); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "πŸ˜„πŸ€ͺ🧐"); } TEST(TextInputModel, BackspaceSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); ASSERT_TRUE(model->Delete()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "AE"); } TEST(TextInputModel, BackspaceReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); ASSERT_TRUE(model->Delete()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "AE"); } TEST(TextInputModel, MoveCursorForwardStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(0, 0)); + EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); EXPECT_TRUE(model->MoveCursorForward()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorForwardMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->MoveCursorForward()); - EXPECT_EQ(model->selection_base(), 3); - EXPECT_EQ(model->selection_extent(), 3); + EXPECT_EQ(model->selection(), TextRange(3)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorForwardEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(5, 5)); + EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); EXPECT_FALSE(model->MoveCursorForward()); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorForwardWideCharacters) { auto model = std::make_unique(); model->SetText("πŸ˜„πŸ™ƒπŸ€ͺ🧐"); - EXPECT_TRUE(model->SetSelection(4, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 4))); ASSERT_TRUE(model->MoveCursorForward()); - EXPECT_EQ(model->selection_base(), 6); - EXPECT_EQ(model->selection_extent(), 6); + EXPECT_EQ(model->selection(), TextRange(6)); EXPECT_STREQ(model->GetText().c_str(), "πŸ˜„πŸ™ƒπŸ€ͺ🧐"); } TEST(TextInputModel, MoveCursorForwardSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_TRUE(model->MoveCursorForward()); - EXPECT_EQ(model->selection_base(), 4); - EXPECT_EQ(model->selection_extent(), 4); + EXPECT_EQ(model->selection(), TextRange(4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorForwardReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_TRUE(model->MoveCursorForward()); - EXPECT_EQ(model->selection_base(), 4); - EXPECT_EQ(model->selection_extent(), 4); + EXPECT_EQ(model->selection(), TextRange(4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(0, 0)); + EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); EXPECT_FALSE(model->MoveCursorBack()); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->MoveCursorBack()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(5, 5)); + EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); EXPECT_TRUE(model->MoveCursorBack()); - EXPECT_EQ(model->selection_base(), 4); - EXPECT_EQ(model->selection_extent(), 4); + EXPECT_EQ(model->selection(), TextRange(4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackWideCharacters) { auto model = std::make_unique(); model->SetText("πŸ˜„πŸ™ƒπŸ€ͺ🧐"); - EXPECT_TRUE(model->SetSelection(4, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 4))); ASSERT_TRUE(model->MoveCursorBack()); - EXPECT_EQ(model->selection_base(), 2); - EXPECT_EQ(model->selection_extent(), 2); + EXPECT_EQ(model->selection(), TextRange(2)); EXPECT_STREQ(model->GetText().c_str(), "πŸ˜„πŸ™ƒπŸ€ͺ🧐"); } TEST(TextInputModel, MoveCursorBackSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_TRUE(model->MoveCursorBack()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_TRUE(model->MoveCursorBack()); - EXPECT_EQ(model->selection_base(), 1); - EXPECT_EQ(model->selection_extent(), 1); + EXPECT_EQ(model->selection(), TextRange(1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToBeginningStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(0, 0)); + EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); EXPECT_FALSE(model->MoveCursorToBeginning()); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToBeginningMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->MoveCursorToBeginning()); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToBeginningEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(5, 5)); + EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); EXPECT_TRUE(model->MoveCursorToBeginning()); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToBeginningSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_TRUE(model->MoveCursorToBeginning()); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToBeginningReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_TRUE(model->MoveCursorToBeginning()); - EXPECT_EQ(model->selection_base(), 0); - EXPECT_EQ(model->selection_extent(), 0); + EXPECT_EQ(model->selection(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToEndStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(0, 0)); + EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); EXPECT_TRUE(model->MoveCursorToEnd()); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToEndMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(2, 2)); + EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); EXPECT_TRUE(model->MoveCursorToEnd()); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToEndEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(5, 5)); + EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); EXPECT_FALSE(model->MoveCursorToEnd()); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToEndSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_TRUE(model->MoveCursorToEnd()); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToEndReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_TRUE(model->MoveCursorToEnd()); - EXPECT_EQ(model->selection_base(), 5); - EXPECT_EQ(model->selection_extent(), 5); + EXPECT_EQ(model->selection(), TextRange(5)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -658,7 +596,7 @@ TEST(TextInputModel, GetCursorOffset) { auto model = std::make_unique(); // These characters take 1, 2, 3 and 4 bytes in UTF-8. model->SetText("$Β’β‚¬πˆ"); - EXPECT_TRUE(model->SetSelection(0, 0)); + EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); EXPECT_EQ(model->GetCursorOffset(), 0); EXPECT_TRUE(model->MoveCursorForward()); EXPECT_EQ(model->GetCursorOffset(), 1); @@ -673,14 +611,14 @@ TEST(TextInputModel, GetCursorOffset) { TEST(TextInputModel, GetCursorOffsetSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(1, 4)); + EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_EQ(model->GetCursorOffset(), 4); } TEST(TextInputModel, GetCursorOffsetReverseSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(4, 1)); + EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_EQ(model->GetCursorOffset(), 1); } diff --git a/shell/platform/common/cpp/text_range.h b/shell/platform/common/cpp/text_range.h index 6524a26e2594a..60482da5158ac 100644 --- a/shell/platform/common/cpp/text_range.h +++ b/shell/platform/common/cpp/text_range.h @@ -47,6 +47,23 @@ class TextRange { // Returns true if the range is of length 0. bool collapsed() const { return base_ == extent_; } + // Returns true if the base is greater than the extent. + bool reversed() const { return base_ > extent_; } + + // Returns true if |position| is contained within the range. + bool Contains(size_t position) const { + return position >= start() && position <= end(); + } + + // Returns true if |range| is contained within the range. + bool Contains(const TextRange& range) const { + return range.start() >= start() && range.end() <= end(); + } + + bool operator==(const TextRange& other) const { + return base_ == other.base_ && extent_ == other.extent_; + } + private: size_t base_; size_t extent_; diff --git a/shell/platform/common/cpp/text_range_unittests.cc b/shell/platform/common/cpp/text_range_unittests.cc index cdeff77a8ceea..8fcf8fed20534 100644 --- a/shell/platform/common/cpp/text_range_unittests.cc +++ b/shell/platform/common/cpp/text_range_unittests.cc @@ -50,4 +50,123 @@ TEST(TextRange, TextRangeFromReversedRange) { EXPECT_FALSE(range.collapsed()); } +TEST(TextRange, ContainsPreStartPosition) { + TextRange range(2, 6); + EXPECT_FALSE(range.Contains(1)); +} + +TEST(TextRange, ContainsStartPosition) { + TextRange range(2, 6); + EXPECT_TRUE(range.Contains(2)); +} + +TEST(TextRange, ContainsMiddlePosition) { + TextRange range(2, 6); + EXPECT_TRUE(range.Contains(3)); + EXPECT_TRUE(range.Contains(4)); +} + +TEST(TextRange, ContainsEndPosition) { + TextRange range(2, 6); + EXPECT_TRUE(range.Contains(6)); +} + +TEST(TextRange, ContainsPostEndPosition) { + TextRange range(2, 6); + EXPECT_FALSE(range.Contains(7)); +} + +TEST(TextRange, ContainsPreStartPositionReversed) { + TextRange range(6, 2); + EXPECT_FALSE(range.Contains(1)); +} + +TEST(TextRange, ContainsStartPositionReversed) { + TextRange range(6, 2); + EXPECT_TRUE(range.Contains(2)); +} + +TEST(TextRange, ContainsMiddlePositionReversed) { + TextRange range(6, 2); + EXPECT_TRUE(range.Contains(3)); + EXPECT_TRUE(range.Contains(4)); +} + +TEST(TextRange, ContainsEndPositionReversed) { + TextRange range(6, 2); + EXPECT_TRUE(range.Contains(6)); +} + +TEST(TextRange, ContainsPostEndPositionReversed) { + TextRange range(6, 2); + EXPECT_FALSE(range.Contains(7)); +} + +TEST(TextRange, ContainsRangePreStartPosition) { + TextRange range(2, 6); + EXPECT_FALSE(range.Contains(TextRange(0, 1))); +} + +TEST(TextRange, ContainsRangeStartPosition) { + TextRange range(2, 6); + EXPECT_TRUE(range.Contains(TextRange(2))); +} + +TEST(TextRange, ContainsRangeMiddlePosition) { + TextRange range(2, 6); + EXPECT_TRUE(range.Contains(TextRange(3, 4))); + EXPECT_TRUE(range.Contains(TextRange(4, 5))); +} + +TEST(TextRange, ContainsRangeEndPosition) { + TextRange range(2, 6); + EXPECT_TRUE(range.Contains(TextRange(6))); +} + +TEST(TextRange, ContainsRangePostEndPosition) { + TextRange range(2, 6); + EXPECT_FALSE(range.Contains(TextRange(6, 7))); +} + +TEST(TextRange, ContainsRangePreStartPositionReversed) { + TextRange range(6, 2); + EXPECT_FALSE(range.Contains(TextRange(0, 1))); +} + +TEST(TextRange, ContainsRangeStartPositionReversed) { + TextRange range(6, 2); + EXPECT_TRUE(range.Contains(TextRange(2))); +} + +TEST(TextRange, ContainsRangeMiddlePositionReversed) { + TextRange range(6, 2); + EXPECT_TRUE(range.Contains(TextRange(3, 4))); + EXPECT_TRUE(range.Contains(TextRange(4, 5))); +} + +TEST(TextRange, ContainsRangeEndPositionReversed) { + TextRange range(6, 2); + EXPECT_TRUE(range.Contains(TextRange(5))); +} + +TEST(TextRange, ContainsRangePostEndPositionReversed) { + TextRange range(6, 2); + EXPECT_FALSE(range.Contains(TextRange(6, 7))); +} + +TEST(TextRange, ReversedForwardRange) { + TextRange range(2, 6); + EXPECT_FALSE(range.reversed()); +} + +TEST(TextRange, ReversedCollapsedRange) { + TextRange range(2, 2); + EXPECT_FALSE(range.reversed()); +} + +TEST(TextRange, ReversedReversedRange) { + TextRange range(6, 2); + EXPECT_TRUE(range.reversed()); +} + } // namespace flutter diff --git a/shell/platform/glfw/text_input_plugin.cc b/shell/platform/glfw/text_input_plugin.cc index 7a7a88f0ccd57..7ebc29ecef6be 100644 --- a/shell/platform/glfw/text_input_plugin.cc +++ b/shell/platform/glfw/text_input_plugin.cc @@ -195,7 +195,7 @@ void TextInputPlugin::HandleMethodCall( base = extent = 0; } active_model_->SetText(text->value.GetString()); - active_model_->SetSelection(base, extent); + active_model_->SetSelection(TextRange(base, extent)); } else { result->NotImplemented(); return; @@ -210,14 +210,14 @@ void TextInputPlugin::SendStateUpdate(const TextInputModel& model) { auto& allocator = args->GetAllocator(); args->PushBack(client_id_, allocator); + TextRange selection = model.selection(); rapidjson::Value editing_state(rapidjson::kObjectType); editing_state.AddMember(kComposingBaseKey, -1, allocator); editing_state.AddMember(kComposingExtentKey, -1, allocator); editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream, allocator); - editing_state.AddMember(kSelectionBaseKey, model.selection_base(), allocator); - editing_state.AddMember(kSelectionExtentKey, model.selection_extent(), - allocator); + editing_state.AddMember(kSelectionBaseKey, selection.base(), allocator); + editing_state.AddMember(kSelectionExtentKey, selection.extent(), allocator); editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator); editing_state.AddMember( kTextKey, rapidjson::Value(model.GetText(), allocator).Move(), allocator); diff --git a/shell/platform/linux/fl_text_input_plugin.cc b/shell/platform/linux/fl_text_input_plugin.cc index ae9ed10a98897..11dc8489f1a21 100644 --- a/shell/platform/linux/fl_text_input_plugin.cc +++ b/shell/platform/linux/fl_text_input_plugin.cc @@ -90,15 +90,14 @@ static void update_editing_state(FlTextInputPlugin* self) { fl_value_append_take(args, fl_value_new_int(self->client_id)); g_autoptr(FlValue) value = fl_value_new_map(); + TextRange selection = self->text_model->selection(); fl_value_set_string_take( value, kTextKey, fl_value_new_string(self->text_model->GetText().c_str())); - fl_value_set_string_take( - value, kSelectionBaseKey, - fl_value_new_int(self->text_model->selection_base())); - fl_value_set_string_take( - value, kSelectionExtentKey, - fl_value_new_int(self->text_model->selection_extent())); + fl_value_set_string_take(value, kSelectionBaseKey, + fl_value_new_int(selection.base())); + fl_value_set_string_take(value, kSelectionExtentKey, + fl_value_new_int(selection.extent())); // The following keys are not implemented and set to default values. fl_value_set_string_take(value, kSelectionAffinityKey, @@ -219,7 +218,7 @@ static FlMethodResponse* set_editing_state(FlTextInputPlugin* self, } self->text_model->SetText(text); - self->text_model->SetSelection(selection_base, selection_extent); + self->text_model->SetSelection(TextRange(selection_base, selection_extent)); return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); } diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index 1687664b42fdd..e69caf504e364 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -196,7 +196,7 @@ void TextInputPlugin::HandleMethodCall( base = extent = 0; } active_model_->SetText(text->value.GetString()); - active_model_->SetSelection(base, extent); + active_model_->SetSelection(TextRange(base, extent)); } else { result->NotImplemented(); return; @@ -211,14 +211,14 @@ void TextInputPlugin::SendStateUpdate(const TextInputModel& model) { auto& allocator = args->GetAllocator(); args->PushBack(client_id_, allocator); + TextRange selection = model.selection(); rapidjson::Value editing_state(rapidjson::kObjectType); editing_state.AddMember(kComposingBaseKey, -1, allocator); editing_state.AddMember(kComposingExtentKey, -1, allocator); editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream, allocator); - editing_state.AddMember(kSelectionBaseKey, model.selection_base(), allocator); - editing_state.AddMember(kSelectionExtentKey, model.selection_extent(), - allocator); + editing_state.AddMember(kSelectionBaseKey, selection.base(), allocator); + editing_state.AddMember(kSelectionExtentKey, selection.extent(), allocator); editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator); editing_state.AddMember( kTextKey, rapidjson::Value(model.GetText(), allocator).Move(), allocator);