diff --git a/shell/platform/common/cpp/text_input_model.cc b/shell/platform/common/cpp/text_input_model.cc index da0c1fe1e5ed8..649baede20f51 100644 --- a/shell/platform/common/cpp/text_input_model.cc +++ b/shell/platform/common/cpp/text_input_model.cc @@ -1,7 +1,6 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// FLUTTER_NOLINT #include "flutter/shell/platform/common/cpp/text_input_model.h" @@ -39,22 +38,75 @@ void TextInputModel::SetText(const std::string& text) { utf16_converter; text_ = utf16_converter.from_bytes(text); selection_ = TextRange(0); + composing_range_ = TextRange(0); } bool TextInputModel::SetSelection(const TextRange& range) { - if (!text_range().Contains(range)) { + if (composing_ && !range.collapsed()) { + return false; + } + if (!editable_range().Contains(range)) { return false; } selection_ = range; return true; } +bool TextInputModel::SetComposingRange(const TextRange& range, + size_t cursor_offset) { + if (!composing_ || !text_range().Contains(range)) { + return false; + } + composing_range_ = range; + selection_ = TextRange(range.start() + cursor_offset); + return true; +} + +void TextInputModel::BeginComposing() { + composing_ = true; + composing_range_ = TextRange(selection_.start()); +} + +void TextInputModel::UpdateComposingText(const std::string& composing_text) { + std::wstring_convert, char16_t> + utf16_converter; + std::u16string text = utf16_converter.from_bytes(composing_text); + + // Preserve selection if we get a no-op update to the composing region. + if (text.length() == 0 && composing_range_.collapsed()) { + return; + } + DeleteSelected(); + text_.replace(composing_range_.start(), composing_range_.length(), text); + composing_range_.set_end(composing_range_.start() + text.length()); + selection_ = TextRange(composing_range_.end()); +} + +void TextInputModel::CommitComposing() { + // Preserve selection if no composing text was entered. + if (composing_range_.collapsed()) { + return; + } + composing_range_ = TextRange(composing_range_.end()); + selection_ = composing_range_; +} + +void TextInputModel::EndComposing() { + composing_ = false; + composing_range_ = TextRange(0); +} + bool TextInputModel::DeleteSelected() { if (selection_.collapsed()) { return false; } - text_.erase(selection_.start(), selection_.length()); - selection_ = TextRange(selection_.start()); + size_t start = selection_.start(); + text_.erase(start, selection_.length()); + selection_ = TextRange(start); + if (composing_) { + // This occurs only immediately after composing has begun with a selection. + composing_range_ = selection_; + } return true; } @@ -74,6 +126,12 @@ void TextInputModel::AddCodePoint(char32_t c) { void TextInputModel::AddText(const std::u16string& text) { DeleteSelected(); + if (composing_) { + // Delete the current composing text, set the cursor to composing start. + text_.erase(composing_range_.start(), composing_range_.length()); + selection_ = TextRange(composing_range_.start()); + composing_range_.set_end(composing_range_.start() + text.length()); + } size_t position = selection_.position(); text_.insert(position, text); selection_ = TextRange(position + text.length()); @@ -89,12 +147,15 @@ bool TextInputModel::Backspace() { if (DeleteSelected()) { return true; } - // If there's no selection, delete the preceding codepoint. + // There is no selection. Delete the preceding codepoint. size_t position = selection_.position(); - if (position != 0) { + if (position != editable_range().start()) { int count = IsTrailingSurrogate(text_.at(position - 1)) ? 2 : 1; text_.erase(position - count, count); selection_ = TextRange(position - count); + if (composing_) { + composing_range_.set_end(composing_range_.end() - count); + } return true; } return false; @@ -104,36 +165,40 @@ bool TextInputModel::Delete() { if (DeleteSelected()) { return true; } - // If there's no selection, delete the preceding codepoint. + // There is no selection. Delete the preceding codepoint. size_t position = selection_.position(); - if (position != text_.length()) { + if (position < editable_range().end()) { int count = IsLeadingSurrogate(text_.at(position)) ? 2 : 1; text_.erase(position, count); + if (composing_) { + composing_range_.set_end(composing_range_.end() - count); + } return true; } return false; } bool TextInputModel::DeleteSurrounding(int offset_from_cursor, int count) { + size_t max_pos = editable_range().end(); size_t start = selection_.extent(); if (offset_from_cursor < 0) { for (int i = 0; i < -offset_from_cursor; i++) { // If requested start is before the available text then reduce the // number of characters to delete. - if (start == 0) { + if (start == editable_range().start()) { count = i; break; } start -= IsTrailingSurrogate(text_.at(start - 1)) ? 2 : 1; } } else { - for (int i = 0; i < offset_from_cursor && start != text_.length(); i++) { + for (int i = 0; i < offset_from_cursor && start != max_pos; i++) { start += IsLeadingSurrogate(text_.at(start)) ? 2 : 1; } } auto end = start; - for (int i = 0; i < count && end != text_.length(); i++) { + for (int i = 0; i < count && end != max_pos; i++) { end += IsLeadingSurrogate(text_.at(start)) ? 2 : 1; } @@ -141,25 +206,33 @@ bool TextInputModel::DeleteSurrounding(int offset_from_cursor, int count) { return false; } - text_.erase(start, end - start); + auto deleted_length = end - start; + text_.erase(start, deleted_length); // Cursor moves only if deleted area is before it. selection_ = TextRange(offset_from_cursor <= 0 ? start : selection_.start()); + // Adjust composing range. + if (composing_) { + composing_range_.set_end(composing_range_.end() - deleted_length); + } return true; } bool TextInputModel::MoveCursorToBeginning() { - if (selection_.collapsed() && selection_.position() == 0) + size_t min_pos = editable_range().start(); + if (selection_.collapsed() && selection_.position() == min_pos) { return false; - selection_ = TextRange(0); + } + selection_ = TextRange(min_pos); return true; } bool TextInputModel::MoveCursorToEnd() { - size_t max_pos = text_.length(); - if (selection_.collapsed() && selection_.position() == max_pos) + size_t max_pos = editable_range().end(); + if (selection_.collapsed() && selection_.position() == max_pos) { return false; + } selection_ = TextRange(max_pos); return true; } @@ -172,7 +245,7 @@ bool TextInputModel::MoveCursorForward() { } // Otherwise, move the cursor forward. size_t position = selection_.position(); - if (position != text_.length()) { + if (position != editable_range().end()) { int count = IsLeadingSurrogate(text_.at(position)) ? 2 : 1; selection_ = TextRange(position + count); return true; @@ -188,7 +261,7 @@ bool TextInputModel::MoveCursorBack() { } // Otherwise, move the cursor backward. size_t position = selection_.position(); - if (position != 0) { + if (position != editable_range().start()) { int count = IsTrailingSurrogate(text_.at(position - 1)) ? 2 : 1; selection_ = TextRange(position - count); return true; diff --git a/shell/platform/common/cpp/text_input_model.h b/shell/platform/common/cpp/text_input_model.h index c49f786f0a9c0..a9560b7b1483d 100644 --- a/shell/platform/common/cpp/text_input_model.h +++ b/shell/platform/common/cpp/text_input_model.h @@ -28,8 +28,43 @@ class TextInputModel { // Attempts to set the text selection. // // Returns false if the selection is not within the bounds of the text. + // While in composing mode, the selection is restricted to the composing + // range; otherwise, it is restricted to the length of the text. bool SetSelection(const TextRange& range); + // Attempts to set the composing range. + // + // Returns false if the range or offset are out of range for the text, or if + // the offset is outside the composing range. + bool SetComposingRange(const TextRange& range, size_t cursor_offset); + + // Begins IME composing mode. + // + // Resets the composing base and extent to the selection start. The existing + // selection is preserved in case composing is aborted with no changes. Until + // |EndComposing| is called, any further changes to selection base and extent + // are restricted to the composing range. + void BeginComposing(); + + // Replaces the composing range with new text. + // + // If a selection of non-zero length exists, it is deleted if the composing + // text is non-empty. The composing range is adjusted to the length of + // |composing_text| and the selection base and offset are set to the end of + // the composing range. + void UpdateComposingText(const std::string& composing_text); + + // Commits composing range to the string. + // + // Causes the composing base and extent to be collapsed to the end of the + // range. + void CommitComposing(); + + // Ends IME composing mode. + // + // Collapses the composing base and offset to 0. + void EndComposing(); + // Adds a Unicode code point. // // Either appends after the cursor (when selection base and extent are the @@ -52,18 +87,21 @@ class TextInputModel { // Deletes either the selection, or one character ahead of the cursor. // // Deleting one character ahead of the cursor occurs when the selection base - // and extent are the same. + // and extent are the same. When composing is active, deletions are + // restricted to text between the composing base and extent. // // Returns true if any deletion actually occurred. bool Delete(); // Deletes text near the cursor. // - // A section is made starting at @offset code points past the cursor (negative - // values go before the cursor). @count code points are removed. The selection - // may go outside the bounds of the text and will result in only the part - // selection that covers the available text being deleted. The existing - // selection is ignored and removed after this operation. + // A section is made starting at |offset_from_cursor| code points past the + // cursor (negative values go before the cursor). |count| code points are + // removed. The selection may go outside the bounds of the available text and + // will result in only the part selection that covers the available text + // being deleted. The existing selection is ignored and removed after this + // operation. When composing is active, deletions are restricted to the + // composing range. // // Returns true if any deletion actually occurred. bool DeleteSurrounding(int offset_from_cursor, int count); @@ -71,7 +109,8 @@ class TextInputModel { // Deletes either the selection, or one character behind the cursor. // // Deleting one character behind the cursor occurs when the selection base - // and extent are the same. + // and extent are the same. When composing is active, deletions are + // restricted to the text between the composing base and extent. // // Returns true if any deletion actually occurred. bool Backspace(); @@ -79,21 +118,31 @@ class TextInputModel { // Attempts to move the cursor backward. // // Returns true if the cursor could be moved. If a selection is active, moves - // to the start of the selection. + // to the start of the selection. If composing is active, motion is + // restricted to the composing range. bool MoveCursorBack(); // Attempts to move the cursor forward. // // Returns true if the cursor could be moved. If a selection is active, moves - // to the end of the selection. + // to the end of the selection. If composing is active, motion is restricted + // to the composing range. bool MoveCursorForward(); // Attempts to move the cursor to the beginning. // + // If composing is active, the cursor is moved to the beginning of the + // composing range; otherwise, it is moved to the beginning of the text. If + // composing is active, motion is restricted to the composing range. + // // Returns true if the cursor could be moved. bool MoveCursorToBeginning(); - // Attempts to move the cursor to the back. + // Attempts to move the cursor to the end. + // + // If composing is active, the cursor is moved to the end of the composing + // range; otherwise, it is moved to the end of the text. If composing is + // active, motion is restricted to the composing range. // // Returns true if the cursor could be moved. bool MoveCursorToEnd(); @@ -108,6 +157,14 @@ class TextInputModel { // The current selection. TextRange selection() const { return selection_; } + // The composing range. + // + // If not in composing mode, returns a collapsed range at position 0. + TextRange composing_range() const { return composing_range_; } + + // Whether multi-step input composing mode is active. + bool composing() const { return composing_; } + private: // Deletes the current selection, if any. // @@ -115,11 +172,21 @@ class TextInputModel { // reset to the start of the selected range. bool DeleteSelected(); + // Returns the currently editable text range. + // + // In composing mode, returns the composing range; otherwise, returns a range + // covering the entire text. + TextRange editable_range() const { + return composing_ ? composing_range_ : text_range(); + } + // Returns a range covering the entire text. TextRange text_range() const { return TextRange(0, text_.length()); } std::u16string text_; TextRange selection_ = TextRange(0); + TextRange composing_range_ = TextRange(0); + bool composing_ = false; }; } // namespace flutter diff --git a/shell/platform/common/cpp/text_input_model_unittests.cc b/shell/platform/common/cpp/text_input_model_unittests.cc index 730f2b9905428..6ee80206295eb 100644 --- a/shell/platform/common/cpp/text_input_model_unittests.cc +++ b/shell/platform/common/cpp/text_input_model_unittests.cc @@ -52,6 +52,18 @@ TEST(TextInputModel, SetSelectionStart) { model->SetText("ABCDE"); EXPECT_TRUE(model->SetSelection(TextRange(0))); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetSelectionComposingStart) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->SetSelection(TextRange(1))); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -60,14 +72,38 @@ TEST(TextInputModel, SetSelectionMiddle) { model->SetText("ABCDE"); EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetSelectionComposingMiddle) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->SetSelection(TextRange(2))); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, SetSelectionEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); + EXPECT_TRUE(model->SetSelection(TextRange(5))); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetSelectionComposingEnd) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->SetSelection(TextRange(4))); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -76,6 +112,18 @@ TEST(TextInputModel, SetSelectionWthExtent) { model->SetText("ABCDE"); EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_EQ(model->selection(), TextRange(1, 4)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetSelectionWthExtentComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_FALSE(model->SetSelection(TextRange(1, 4))); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -84,6 +132,18 @@ TEST(TextInputModel, SetSelectionReverseExtent) { model->SetText("ABCDE"); EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_EQ(model->selection(), TextRange(4, 1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetSelectionReverseExtentComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_FALSE(model->SetSelection(TextRange(4, 1))); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -92,7 +152,255 @@ TEST(TextInputModel, SetSelectionOutsideString) { model->SetText("ABCDE"); EXPECT_FALSE(model->SetSelection(TextRange(4, 6))); EXPECT_FALSE(model->SetSelection(TextRange(5, 6))); - EXPECT_FALSE(model->SetSelection(TextRange(6, 6))); + EXPECT_FALSE(model->SetSelection(TextRange(6))); +} + +TEST(TextInputModel, SetSelectionOutsideComposingRange) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_FALSE(model->SetSelection(TextRange(0))); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_FALSE(model->SetSelection(TextRange(5))); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); +} + +TEST(TextInputModel, SetComposingRangeStart) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(0, 0), 0)); + EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetComposingRangeMiddle) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(2, 2), 0)); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(2)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetComposingRangeEnd) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(5, 5), 0)); + EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(5)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetComposingRangeWithExtent) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 3)); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetComposingRangeReverseExtent) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 3)); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, SetComposingRangeOutsideString) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_FALSE(model->SetComposingRange(TextRange(4, 6), 0)); + EXPECT_FALSE(model->SetComposingRange(TextRange(5, 6), 0)); + EXPECT_FALSE(model->SetComposingRange(TextRange(6, 6), 0)); +} + +// Composing sequence with no initial selection and no text input. +TEST(TextInputModel, CommitComposingNoTextWithNoSelection) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->SetSelection(TextRange(0)); + + // Verify no changes on BeginComposing. + model->BeginComposing(); + EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); + + // Verify no changes on CommitComposing. + model->CommitComposing(); + EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); + + // Verify no changes on CommitComposing. + model->EndComposing(); + EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +// Composing sequence with an initial selection and no text input. +TEST(TextInputModel, CommitComposingNoTextWithSelection) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->SetSelection(TextRange(1, 3)); + + // Verify no changes on BeginComposing. + model->BeginComposing(); + EXPECT_EQ(model->selection(), TextRange(1, 3)); + EXPECT_EQ(model->composing_range(), TextRange(1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); + + // Verify no changes on CommitComposing. + model->CommitComposing(); + EXPECT_EQ(model->selection(), TextRange(1, 3)); + EXPECT_EQ(model->composing_range(), TextRange(1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); + + // Verify no changes on CommitComposing. + model->EndComposing(); + EXPECT_EQ(model->selection(), TextRange(1, 3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +// Composing sequence with no initial selection. +TEST(TextInputModel, CommitComposingTextWithNoSelection) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->SetSelection(TextRange(1)); + + // Verify no changes on BeginComposing. + model->BeginComposing(); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); + + // Verify selection base, extent and composing extent increment as text is + // entered. Verify composing base does not change. + model->UpdateComposingText("つ"); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "AつBCDE"); + model->UpdateComposingText("つる"); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); + EXPECT_STREQ(model->GetText().c_str(), "AつるBCDE"); + + // Verify that cursor position is set to correct offset from composing base. + model->UpdateComposingText("鶴"); + EXPECT_TRUE(model->SetSelection(TextRange(1))); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴BCDE"); + + // Verify composing base is set to composing extent on commit. + model->CommitComposing(); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(2)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴BCDE"); + + // Verify that further text entry increments the selection base, extent and + // the composing extent. Verify that composing base does not change. + model->UpdateComposingText("が"); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(2, 3)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴がBCDE"); + + // Verify composing base is set to composing extent on commit. + model->CommitComposing(); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(3)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴がBCDE"); + + // Verify no changes on EndComposing. + model->EndComposing(); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴がBCDE"); +} + +// Composing sequence with an initial selection. +TEST(TextInputModel, CommitComposingTextWithSelection) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->SetSelection(TextRange(1, 3)); + + // Verify no changes on BeginComposing. + model->BeginComposing(); + EXPECT_EQ(model->selection(), TextRange(1, 3)); + EXPECT_EQ(model->composing_range(), TextRange(1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); + + // Verify selection is replaced and selection base, extent and composing + // extent increment to the position immediately after the composing text. + // Verify composing base does not change. + model->UpdateComposingText("つ"); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "AつDE"); + + // Verify that further text entry increments the selection base, extent and + // the composing extent. Verify that composing base does not change. + model->UpdateComposingText("つる"); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); + EXPECT_STREQ(model->GetText().c_str(), "AつるDE"); + + // Verify that cursor position is set to correct offset from composing base. + model->UpdateComposingText("鶴"); + EXPECT_TRUE(model->SetSelection(TextRange(1))); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴DE"); + + // Verify composing base is set to composing extent on commit. + model->CommitComposing(); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(2)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴DE"); + + // Verify that further text entry increments the selection base, extent and + // the composing extent. Verify that composing base does not change. + model->UpdateComposingText("が"); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(2, 3)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴がDE"); + + // Verify composing base is set to composing extent on commit. + model->CommitComposing(); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(3)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴がDE"); + + // Verify no changes on EndComposing. + model->EndComposing(); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "A鶴がDE"); +} + +TEST(TextInputModel, UpdateComposingRemovesLastComposingCharacter) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + model->SetComposingRange(TextRange(1, 2), 1); + model->UpdateComposingText(""); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1)); + model->SetText("ACDE"); } TEST(TextInputModel, AddCodePoint) { @@ -103,6 +411,7 @@ TEST(TextInputModel, AddCodePoint) { model->AddCodePoint('D'); model->AddCodePoint('E'); EXPECT_EQ(model->selection(), TextRange(6)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AB😄DE"); } @@ -112,6 +421,7 @@ TEST(TextInputModel, AddCodePointSelection) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); model->AddCodePoint('x'); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AxE"); } @@ -121,6 +431,7 @@ TEST(TextInputModel, AddCodePointReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); model->AddCodePoint('x'); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AxE"); } @@ -130,6 +441,7 @@ TEST(TextInputModel, AddCodePointSelectionWideCharacter) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); model->AddCodePoint(0x1f604); EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "A😄E"); } @@ -139,6 +451,7 @@ TEST(TextInputModel, AddCodePointReverseSelectionWideCharacter) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); model->AddCodePoint(0x1f604); EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "A😄E"); } @@ -148,6 +461,7 @@ TEST(TextInputModel, AddText) { model->AddText("😄"); model->AddText("FGHIJ"); EXPECT_EQ(model->selection(), TextRange(12)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE😄FGHIJ"); } @@ -157,6 +471,7 @@ TEST(TextInputModel, AddTextSelection) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); model->AddText("xy"); EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AxyE"); } @@ -166,6 +481,7 @@ TEST(TextInputModel, AddTextReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); model->AddText("xy"); EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AxyE"); } @@ -175,6 +491,7 @@ TEST(TextInputModel, AddTextSelectionWideCharacter) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); model->AddText(u"😄🙃"); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "A😄🙃E"); } @@ -184,42 +501,47 @@ TEST(TextInputModel, AddTextReverseSelectionWideCharacter) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); model->AddText(u"😄🙃"); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "A😄🙃E"); } TEST(TextInputModel, DeleteStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); + EXPECT_TRUE(model->SetSelection(TextRange(0))); ASSERT_TRUE(model->Delete()); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "BCDE"); } TEST(TextInputModel, DeleteMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); ASSERT_TRUE(model->Delete()); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABDE"); } TEST(TextInputModel, DeleteEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); + EXPECT_TRUE(model->SetSelection(TextRange(5))); ASSERT_FALSE(model->Delete()); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, DeleteWideCharacters) { auto model = std::make_unique(); model->SetText("😄🙃🤪🧐"); - EXPECT_TRUE(model->SetSelection(TextRange(4, 4))); + EXPECT_TRUE(model->SetSelection(TextRange(4))); ASSERT_TRUE(model->Delete()); EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "😄🙃🧐"); } @@ -229,6 +551,7 @@ TEST(TextInputModel, DeleteSelection) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); ASSERT_TRUE(model->Delete()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AE"); } @@ -238,96 +561,276 @@ TEST(TextInputModel, DeleteReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); ASSERT_TRUE(model->Delete()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AE"); } +TEST(TextInputModel, DeleteStartComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + ASSERT_TRUE(model->Delete()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); + EXPECT_STREQ(model->GetText().c_str(), "ACDE"); +} + +TEST(TextInputModel, DeleteStartReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 0)); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + ASSERT_TRUE(model->Delete()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(3, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ACDE"); +} + +TEST(TextInputModel, DeleteMiddleComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); + ASSERT_TRUE(model->Delete()); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); + EXPECT_STREQ(model->GetText().c_str(), "ABDE"); +} + +TEST(TextInputModel, DeleteMiddleReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 1)); + ASSERT_TRUE(model->Delete()); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(3, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABDE"); +} + +TEST(TextInputModel, DeleteEndComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 3)); + ASSERT_FALSE(model->Delete()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, DeleteEndReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 3)); + ASSERT_FALSE(model->Delete()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + TEST(TextInputModel, DeleteSurroundingAtCursor) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); + EXPECT_TRUE(model->DeleteSurrounding(0, 1)); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABDE"); +} + +TEST(TextInputModel, DeleteSurroundingAtCursorComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); EXPECT_TRUE(model->DeleteSurrounding(0, 1)); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); EXPECT_STREQ(model->GetText().c_str(), "ABDE"); } TEST(TextInputModel, DeleteSurroundingAtCursorAll) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->DeleteSurrounding(0, 3)); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AB"); } +TEST(TextInputModel, DeleteSurroundingAtCursorAllComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); + EXPECT_TRUE(model->DeleteSurrounding(0, 2)); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "ABE"); +} + TEST(TextInputModel, DeleteSurroundingAtCursorGreedy) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->DeleteSurrounding(0, 4)); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AB"); } +TEST(TextInputModel, DeleteSurroundingAtCursorGreedyComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); + EXPECT_TRUE(model->DeleteSurrounding(0, 4)); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "ABE"); +} + TEST(TextInputModel, DeleteSurroundingBeforeCursor) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->DeleteSurrounding(-1, 1)); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ACDE"); } +TEST(TextInputModel, DeleteSurroundingBeforeCursorComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 2)); + EXPECT_TRUE(model->DeleteSurrounding(-1, 1)); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); + EXPECT_STREQ(model->GetText().c_str(), "ABDE"); +} + TEST(TextInputModel, DeleteSurroundingBeforeCursorAll) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->DeleteSurrounding(-2, 2)); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "CDE"); } +TEST(TextInputModel, DeleteSurroundingBeforeCursorAllComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 2)); + EXPECT_TRUE(model->DeleteSurrounding(-2, 2)); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "ADE"); +} + TEST(TextInputModel, DeleteSurroundingBeforeCursorGreedy) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->DeleteSurrounding(-3, 3)); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "CDE"); } +TEST(TextInputModel, DeleteSurroundingBeforeCursorGreedyComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 2)); + EXPECT_TRUE(model->DeleteSurrounding(-3, 3)); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "ADE"); +} + TEST(TextInputModel, DeleteSurroundingAfterCursor) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->DeleteSurrounding(1, 1)); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCE"); } +TEST(TextInputModel, DeleteSurroundingAfterCursorComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->DeleteSurrounding(1, 1)); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); + EXPECT_STREQ(model->GetText().c_str(), "ABDE"); +} + TEST(TextInputModel, DeleteSurroundingAfterCursorAll) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->DeleteSurrounding(1, 2)); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABC"); } +TEST(TextInputModel, DeleteSurroundingAfterCursorAllComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->DeleteSurrounding(1, 2)); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "ABE"); +} + TEST(TextInputModel, DeleteSurroundingAfterCursorGreedy) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->DeleteSurrounding(1, 3)); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABC"); } +TEST(TextInputModel, DeleteSurroundingAfterCursorGreedyComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->DeleteSurrounding(1, 3)); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 2)); + EXPECT_STREQ(model->GetText().c_str(), "ABE"); +} + TEST(TextInputModel, DeleteSurroundingSelection) { auto model = std::make_unique(); model->SetText("ABCDE"); EXPECT_TRUE(model->SetSelection(TextRange(2, 3))); EXPECT_TRUE(model->DeleteSurrounding(0, 1)); EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCE"); } @@ -337,42 +840,47 @@ TEST(TextInputModel, DeleteSurroundingReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 3))); EXPECT_TRUE(model->DeleteSurrounding(0, 1)); EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCE"); } TEST(TextInputModel, BackspaceStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); + EXPECT_TRUE(model->SetSelection(TextRange(0))); ASSERT_FALSE(model->Backspace()); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, BackspaceMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); ASSERT_TRUE(model->Backspace()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ACDE"); } TEST(TextInputModel, BackspaceEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); + EXPECT_TRUE(model->SetSelection(TextRange(5))); ASSERT_TRUE(model->Backspace()); EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCD"); } TEST(TextInputModel, BackspaceWideCharacters) { auto model = std::make_unique(); model->SetText("😄🙃🤪🧐"); - EXPECT_TRUE(model->SetSelection(TextRange(4, 4))); + EXPECT_TRUE(model->SetSelection(TextRange(4))); ASSERT_TRUE(model->Backspace()); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "😄🤪🧐"); } @@ -382,6 +890,7 @@ TEST(TextInputModel, BackspaceSelection) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); ASSERT_TRUE(model->Delete()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AE"); } @@ -391,42 +900,113 @@ TEST(TextInputModel, BackspaceReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); ASSERT_TRUE(model->Delete()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "AE"); } +TEST(TextInputModel, BackspaceStartComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + ASSERT_FALSE(model->Backspace()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, BackspaceStartReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 0)); + ASSERT_FALSE(model->Backspace()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, BackspaceMiddleComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); + ASSERT_TRUE(model->Backspace()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); + EXPECT_STREQ(model->GetText().c_str(), "ACDE"); +} + +TEST(TextInputModel, BackspaceMiddleReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 1)); + ASSERT_TRUE(model->Backspace()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(3, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ACDE"); +} + +TEST(TextInputModel, BackspaceEndComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 3)); + ASSERT_TRUE(model->Backspace()); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(1, 3)); + EXPECT_STREQ(model->GetText().c_str(), "ABCE"); +} + +TEST(TextInputModel, BackspaceEndReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 3)); + ASSERT_TRUE(model->Backspace()); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(3, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCE"); +} + TEST(TextInputModel, MoveCursorForwardStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); + EXPECT_TRUE(model->SetSelection(TextRange(0))); EXPECT_TRUE(model->MoveCursorForward()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorForwardMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->MoveCursorForward()); EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorForwardEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); + EXPECT_TRUE(model->SetSelection(TextRange(5))); EXPECT_FALSE(model->MoveCursorForward()); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorForwardWideCharacters) { auto model = std::make_unique(); model->SetText("😄🙃🤪🧐"); - EXPECT_TRUE(model->SetSelection(TextRange(4, 4))); + EXPECT_TRUE(model->SetSelection(TextRange(4))); ASSERT_TRUE(model->MoveCursorForward()); EXPECT_EQ(model->selection(), TextRange(6)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "😄🙃🤪🧐"); } @@ -436,6 +1016,7 @@ TEST(TextInputModel, MoveCursorForwardSelection) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_TRUE(model->MoveCursorForward()); EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -445,42 +1026,113 @@ TEST(TextInputModel, MoveCursorForwardReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_TRUE(model->MoveCursorForward()); EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorForwardStartComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->MoveCursorForward()); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorForwardStartReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 0)); + EXPECT_TRUE(model->MoveCursorForward()); + EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorForwardMiddleComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); + EXPECT_TRUE(model->MoveCursorForward()); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorForwardMiddleReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 1)); + EXPECT_TRUE(model->MoveCursorForward()); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorForwardEndComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 3)); + EXPECT_FALSE(model->MoveCursorForward()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorForwardEndReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 3)); + EXPECT_FALSE(model->MoveCursorForward()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); + EXPECT_TRUE(model->SetSelection(TextRange(0))); EXPECT_FALSE(model->MoveCursorBack()); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->MoveCursorBack()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); + EXPECT_TRUE(model->SetSelection(TextRange(5))); EXPECT_TRUE(model->MoveCursorBack()); EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorBackWideCharacters) { auto model = std::make_unique(); model->SetText("😄🙃🤪🧐"); - EXPECT_TRUE(model->SetSelection(TextRange(4, 4))); + EXPECT_TRUE(model->SetSelection(TextRange(4))); ASSERT_TRUE(model->MoveCursorBack()); EXPECT_EQ(model->selection(), TextRange(2)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "😄🙃🤪🧐"); } @@ -490,6 +1142,7 @@ TEST(TextInputModel, MoveCursorBackSelection) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_TRUE(model->MoveCursorBack()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -499,33 +1152,105 @@ TEST(TextInputModel, MoveCursorBackReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_TRUE(model->MoveCursorBack()); EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorBackStartComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->SetSelection(TextRange(1))); + EXPECT_FALSE(model->MoveCursorBack()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorBackStartReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 0)); + EXPECT_TRUE(model->SetSelection(TextRange(1))); + EXPECT_FALSE(model->MoveCursorBack()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorBackMiddleComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); + EXPECT_TRUE(model->MoveCursorBack()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorBackMiddleReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 1)); + EXPECT_TRUE(model->MoveCursorBack()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorBackEndComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 3)); + EXPECT_TRUE(model->MoveCursorBack()); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorBackEndReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 3)); + EXPECT_TRUE(model->MoveCursorBack()); + EXPECT_EQ(model->selection(), TextRange(3)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToBeginningStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); + EXPECT_TRUE(model->SetSelection(TextRange(0))); EXPECT_FALSE(model->MoveCursorToBeginning()); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToBeginningMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->MoveCursorToBeginning()); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToBeginningEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); + EXPECT_TRUE(model->SetSelection(TextRange(5))); EXPECT_TRUE(model->MoveCursorToBeginning()); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -535,6 +1260,7 @@ TEST(TextInputModel, MoveCursorToBeginningSelection) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_TRUE(model->MoveCursorToBeginning()); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -544,33 +1270,103 @@ TEST(TextInputModel, MoveCursorToBeginningReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_TRUE(model->MoveCursorToBeginning()); EXPECT_EQ(model->selection(), TextRange(0)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToBeginningStartComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_FALSE(model->MoveCursorToBeginning()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToBeginningStartReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 0)); + EXPECT_FALSE(model->MoveCursorToBeginning()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToBeginningMiddleComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); + EXPECT_TRUE(model->MoveCursorToBeginning()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToBeginningMiddleReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 1)); + EXPECT_TRUE(model->MoveCursorToBeginning()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToBeginningEndComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 3)); + EXPECT_TRUE(model->MoveCursorToBeginning()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToBeginningEndReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 3)); + EXPECT_TRUE(model->MoveCursorToBeginning()); + EXPECT_EQ(model->selection(), TextRange(1)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToEndStart) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(0, 0))); + EXPECT_TRUE(model->SetSelection(TextRange(0))); EXPECT_TRUE(model->MoveCursorToEnd()); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToEndMiddle) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(2, 2))); + EXPECT_TRUE(model->SetSelection(TextRange(2))); EXPECT_TRUE(model->MoveCursorToEnd()); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } TEST(TextInputModel, MoveCursorToEndEnd) { auto model = std::make_unique(); model->SetText("ABCDE"); - EXPECT_TRUE(model->SetSelection(TextRange(5, 5))); + EXPECT_TRUE(model->SetSelection(TextRange(5))); EXPECT_FALSE(model->MoveCursorToEnd()); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -580,6 +1376,7 @@ TEST(TextInputModel, MoveCursorToEndSelection) { EXPECT_TRUE(model->SetSelection(TextRange(1, 4))); EXPECT_TRUE(model->MoveCursorToEnd()); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -589,6 +1386,73 @@ TEST(TextInputModel, MoveCursorToEndReverseSelection) { EXPECT_TRUE(model->SetSelection(TextRange(4, 1))); EXPECT_TRUE(model->MoveCursorToEnd()); EXPECT_EQ(model->selection(), TextRange(5)); + EXPECT_EQ(model->composing_range(), TextRange(0)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToEndStartComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->MoveCursorToEnd()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToEndStartReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 0)); + EXPECT_TRUE(model->MoveCursorToEnd()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToEndMiddleComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 1)); + EXPECT_TRUE(model->MoveCursorToEnd()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToEndMiddleReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 1)); + EXPECT_TRUE(model->MoveCursorToEnd()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToEndEndComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(1, 4), 3)); + EXPECT_FALSE(model->MoveCursorToEnd()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(1, 4)); + EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); +} + +TEST(TextInputModel, MoveCursorToEndEndReverseComposing) { + auto model = std::make_unique(); + model->SetText("ABCDE"); + model->BeginComposing(); + EXPECT_TRUE(model->SetComposingRange(TextRange(4, 1), 3)); + EXPECT_FALSE(model->MoveCursorToEnd()); + EXPECT_EQ(model->selection(), TextRange(4)); + EXPECT_EQ(model->composing_range(), TextRange(4, 1)); EXPECT_STREQ(model->GetText().c_str(), "ABCDE"); } @@ -596,7 +1460,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(TextRange(0, 0))); + EXPECT_TRUE(model->SetSelection(TextRange(0))); EXPECT_EQ(model->GetCursorOffset(), 0); EXPECT_TRUE(model->MoveCursorForward()); EXPECT_EQ(model->GetCursorOffset(), 1); diff --git a/shell/platform/common/cpp/text_range.h b/shell/platform/common/cpp/text_range.h index 60482da5158ac..d951c45f98e6e 100644 --- a/shell/platform/common/cpp/text_range.h +++ b/shell/platform/common/cpp/text_range.h @@ -21,19 +21,43 @@ class TextRange { virtual ~TextRange() = default; - // Returns the base position of the range. + // The base position of the range. size_t base() const { return base_; } - // Returns the extent position of the range. + // Sets the base position of the range. + void set_base(size_t pos) { base_ = pos; } + + // The extent position of the range. size_t extent() const { return extent_; } - // Returns the lesser of the base and extent positions. + // Sets the extent position of the range. + void set_extent(size_t pos) { extent_ = pos; } + + // The lesser of the base and extent positions. size_t start() const { return std::min(base_, extent_); } - // Returns the greater of the base and extent positions. + // Sets the start position of the range. + void set_start(size_t pos) { + if (base_ <= extent_) { + base_ = pos; + } else { + extent_ = pos; + } + } + + // The greater of the base and extent positions. size_t end() const { return std::max(base_, extent_); } - // Returns the position of a collapsed range. + // Sets the end position of the range. + void set_end(size_t pos) { + if (base_ <= extent_) { + extent_ = pos; + } else { + base_ = pos; + } + } + + // The position of a collapsed range. // // Asserts that the range is of length 0. size_t position() const { @@ -41,7 +65,7 @@ class TextRange { return extent_; } - // Returns the length of the range. + // The length of the range. size_t length() const { return end() - start(); } // Returns true if the range is of length 0. diff --git a/shell/platform/common/cpp/text_range_unittests.cc b/shell/platform/common/cpp/text_range_unittests.cc index eaf30d935a515..9aecf34309b98 100644 --- a/shell/platform/common/cpp/text_range_unittests.cc +++ b/shell/platform/common/cpp/text_range_unittests.cc @@ -50,6 +50,62 @@ TEST(TextRange, TextRangeFromReversedRange) { EXPECT_FALSE(range.collapsed()); } +TEST(TextRange, SetBase) { + TextRange range(3, 7); + range.set_base(4); + EXPECT_EQ(range.base(), size_t(4)); + EXPECT_EQ(range.extent(), size_t(7)); +} + +TEST(TextRange, SetBaseReversed) { + TextRange range(7, 3); + range.set_base(5); + EXPECT_EQ(range.base(), size_t(5)); + EXPECT_EQ(range.extent(), size_t(3)); +} + +TEST(TextRange, SetExtent) { + TextRange range(3, 7); + range.set_extent(6); + EXPECT_EQ(range.base(), size_t(3)); + EXPECT_EQ(range.extent(), size_t(6)); +} + +TEST(TextRange, SetExtentReversed) { + TextRange range(7, 3); + range.set_extent(4); + EXPECT_EQ(range.base(), size_t(7)); + EXPECT_EQ(range.extent(), size_t(4)); +} + +TEST(TextRange, SetStart) { + TextRange range(3, 7); + range.set_start(5); + EXPECT_EQ(range.base(), size_t(5)); + EXPECT_EQ(range.extent(), size_t(7)); +} + +TEST(TextRange, SetStartReversed) { + TextRange range(7, 3); + range.set_start(5); + EXPECT_EQ(range.base(), size_t(7)); + EXPECT_EQ(range.extent(), size_t(5)); +} + +TEST(TextRange, SetEnd) { + TextRange range(3, 7); + range.set_end(6); + EXPECT_EQ(range.base(), size_t(3)); + EXPECT_EQ(range.extent(), size_t(6)); +} + +TEST(TextRange, SetEndReversed) { + TextRange range(7, 3); + range.set_end(5); + EXPECT_EQ(range.base(), size_t(5)); + EXPECT_EQ(range.extent(), size_t(3)); +} + TEST(TextRange, ContainsPreStartPosition) { TextRange range(2, 6); EXPECT_FALSE(range.Contains(1));