Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Unified Diff: ui/gfx/render_text_unittest.cc

Issue 2541313002: RenderTextHarfBuzz: Add support for multi line text selection. (Closed)
Patch Set: Tests Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/render_text_unittest.cc
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index c51ef30315f664206e9501ccc234b411236c93a4..408c06370f64ea479b4953daadabbfc3a66def3d 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -10,6 +10,7 @@
#include <algorithm>
#include <memory>
+#include <numeric>
#include "base/format_macros.h"
#include "base/i18n/break_iterator.h"
@@ -114,6 +115,10 @@ class RenderTextTestApi {
return render_text_->GetAlignmentOffset(line_number);
}
+ std::vector<Rect> GetSubstringBounds(const Range& range) {
+ return render_text_->GetSubstringBounds(range);
+ }
+
private:
RenderText* render_text_;
@@ -472,6 +477,12 @@ class RenderTextTest : public testing::Test,
}
#endif
+ Rect GetSelectionBoundsUnion() {
+ const std::vector<Rect> bounds =
+ test_api_->GetSubstringBounds(render_text_->selection());
+ return std::accumulate(bounds.begin(), bounds.end(), Rect(), UnionRects);
+ }
+
Canvas* canvas() { return &canvas_; }
TestSkiaTextRenderer* renderer() { return &renderer_; }
test::RenderTextTestApi* test_api() { return test_api_.get(); };
@@ -514,6 +525,10 @@ class RenderTextHarfBuzzTest : public RenderTextTest {
return GetRenderTextHarfBuzz()->ShapeRunWithFont(text, font, params, run);
}
+ int GetCursorYForTesting(int line_num = 0) {
+ return GetRenderText()->GetLineOffset(line_num).y() + 1;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(RenderTextHarfBuzzTest);
};
@@ -788,10 +803,14 @@ TEST_P(RenderTextHarfBuzzTest, ObscuredText) {
// FindCursorPosition() should not return positions between a surrogate pair.
render_text->SetDisplayRect(Rect(0, 0, 20, 20));
- EXPECT_EQ(render_text->FindCursorPosition(Point(0, 0)).caret_pos(), 0U);
- EXPECT_EQ(render_text->FindCursorPosition(Point(20, 0)).caret_pos(), 2U);
+ const int cursor_y = GetCursorYForTesting();
+ EXPECT_EQ(render_text->FindCursorPosition(Point(0, cursor_y)).caret_pos(),
+ 0U);
+ EXPECT_EQ(render_text->FindCursorPosition(Point(20, cursor_y)).caret_pos(),
+ 2U);
for (int x = -1; x <= 20; ++x) {
- SelectionModel selection = render_text->FindCursorPosition(Point(x, 0));
+ SelectionModel selection =
+ render_text->FindCursorPosition(Point(x, cursor_y));
EXPECT_TRUE(selection.caret_pos() == 0U || selection.caret_pos() == 2U);
}
@@ -1783,7 +1802,44 @@ TEST_P(RenderTextHarfBuzzTest, FindCursorPosition) {
const Range range(render_text->GetGlyphBounds(j));
// Test a point just inside the leading edge of the glyph bounds.
int x = range.is_reversed() ? range.GetMax() - 1 : range.GetMin() + 1;
- EXPECT_EQ(j, render_text->FindCursorPosition(Point(x, 0)).caret_pos());
+ EXPECT_EQ(
+ j, render_text->FindCursorPosition(Point(x, GetCursorYForTesting()))
+ .caret_pos());
+ }
+ }
+}
+
+// Tests that FindCursorPosition behaves correctly for multi-line text.
+TEST_P(RenderTextHarfBuzzTest, FindCursorPositionMultiline) {
msw 2016/12/13 03:04:56 q: should we have any multi-char grapheme test cas
karandeepb 2016/12/16 02:58:38 Thanks for pointing this out. The existing impleme
+ struct {
+ const wchar_t* const text;
+ bool is_ltr;
msw 2016/12/13 03:04:56 optional nit: check RenderText::GetDisplayTextDire
karandeepb 2016/12/16 02:58:38 Done.
+ } cases[] = {{L"abc def", true}, {L"\x5d0\x5d1\x5d2 \x5d3\x5d4\x5d5", false}};
+
+ SetGlyphWidth(5);
+ RenderText* render_text = GetRenderText();
+ render_text->SetDisplayRect(Rect(25, 1000));
+ render_text->SetMultiline(true);
+
+ for (size_t i = 0; i < arraysize(cases); i++) {
+ render_text->SetText(WideToUTF16(cases[i].text));
+ test_api()->EnsureLayout();
+ EXPECT_EQ(2u, test_api()->lines().size());
+
+ for (size_t j = 0; j < render_text->text().length(); ++j) {
+ SCOPED_TRACE(base::StringPrintf(
+ "Testing index %" PRIuS " for case %" PRIuS "", j, i));
+ render_text->SelectRange(Range(j, j + 1));
+
+ // Test a point inside the leading edge of the glyph bounds.
+ const Rect bounds = GetSelectionBoundsUnion();
+ const Point query(cases[i].is_ltr ? bounds.x() + 1 : bounds.right() - 1,
msw 2016/12/13 03:04:56 nit: name this cursor_position?
karandeepb 2016/12/16 02:58:38 Done.
+ bounds.y() + 1);
+
+ const SelectionModel model_for_query =
msw 2016/12/13 03:04:56 nit: name this model?
karandeepb 2016/12/16 02:58:38 Done.
+ render_text->FindCursorPosition(query);
+ EXPECT_EQ(j, model_for_query.caret_pos());
+ EXPECT_EQ(CURSOR_FORWARD, model_for_query.caret_affinity());
}
}
}
@@ -2863,12 +2919,12 @@ TEST_P(RenderTextHarfBuzzTest, Multiline_Newline) {
// Ranges of the characters on each line preceding the newline.
const Range line_char_ranges[3];
} kTestStrings[] = {
- {L"abc\ndef", 2ul, { Range(0, 3), Range(4, 7), Range::InvalidRange() } },
- {L"a \n b ", 2ul, { Range(0, 2), Range(3, 6), Range::InvalidRange() } },
- {L"ab\n", 2ul, { Range(0, 2), Range(), Range::InvalidRange() } },
- {L"a\n\nb", 3ul, { Range(0, 1), Range(), Range(3, 4) } },
- {L"\nab", 2ul, { Range(), Range(1, 3), Range::InvalidRange() } },
- {L"\n", 2ul, { Range(), Range(), Range::InvalidRange() } },
+ {L"abc\ndef", 2ul, {Range(0, 3), Range(4, 7), Range::InvalidRange()}},
+ {L"a \n b ", 2ul, {Range(0, 2), Range(3, 6), Range::InvalidRange()}},
+ {L"ab\n", 2ul, {Range(0, 2), Range(), Range::InvalidRange()}},
+ {L"a\n\nb", 3ul, {Range(0, 1), Range(2, 3), Range(3, 4)}},
+ {L"\nab", 2ul, {Range(0, 1), Range(1, 3), Range::InvalidRange()}},
+ {L"\n", 2ul, {Range(0, 1), Range(), Range::InvalidRange()}},
};
RenderText* render_text = GetRenderText();
@@ -3898,6 +3954,7 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_LTR) {
render_text->ApplyStyle(ITALIC, true, Range(3, 8));
render_text->ApplyStyle(DIAGONAL_STRIKE, true, Range(5, 7));
render_text->ApplyStyle(STRIKE, true, Range(1, 7));
+ const int cursor_y = GetCursorYForTesting();
const std::vector<RenderText::FontSpan> font_spans =
render_text->GetFontSpansForTesting();
@@ -3931,14 +3988,14 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_LTR) {
{
SCOPED_TRACE(base::StringPrintf("Query to the left of text bounds"));
EXPECT_TRUE(render_text->GetDecoratedWordAtPoint(
- Point(-5, 5), &decorated_word, &baseline_point));
+ Point(-5, cursor_y), &decorated_word, &baseline_point));
VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word);
EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point));
}
{
SCOPED_TRACE(base::StringPrintf("Query to the right of text bounds"));
EXPECT_TRUE(render_text->GetDecoratedWordAtPoint(
- Point(105, 5), &decorated_word, &baseline_point));
+ Point(105, cursor_y), &decorated_word, &baseline_point));
VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word);
EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point));
}
@@ -3982,6 +4039,7 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_RTL) {
render_text->ApplyStyle(ITALIC, true, Range(0, 3));
render_text->ApplyStyle(DIAGONAL_STRIKE, true, Range(0, 2));
render_text->ApplyStyle(STRIKE, true, Range(2, 5));
+ const int cursor_y = GetCursorYForTesting();
const std::vector<RenderText::FontSpan> font_spans =
render_text->GetFontSpansForTesting();
@@ -4014,14 +4072,14 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_RTL) {
{
SCOPED_TRACE(base::StringPrintf("Query to the left of text bounds"));
EXPECT_TRUE(render_text->GetDecoratedWordAtPoint(
- Point(-5, 5), &decorated_word, &baseline_point));
+ Point(-5, cursor_y), &decorated_word, &baseline_point));
VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word);
EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point));
}
{
SCOPED_TRACE(base::StringPrintf("Query to the right of text bounds"));
EXPECT_TRUE(render_text->GetDecoratedWordAtPoint(
- Point(105, 5), &decorated_word, &baseline_point));
+ Point(105, cursor_y), &decorated_word, &baseline_point));
VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word);
EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point));
}
@@ -4076,6 +4134,82 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_Return) {
&baseline_point));
}
+// Tests text selection made at end points of individual lines of multiline
+// text.
+TEST_P(RenderTextHarfBuzzTest, LineEndSelections) {
+ const wchar_t* const ltr = L"abc\n\ndef";
+ const wchar_t* const rtl = L"\x5d0\x5d1\x5d2\n\n\x5d3\x5d4\x5d5";
+ const int left_x = -100;
+ const int right_x = 100;
+ struct {
+ const wchar_t* const text;
+ const int line_num;
+ const int x;
+ const wchar_t* const selected_text;
+ } cases[] = {
+ {ltr, 1, left_x, L"abc\n"},
+ {ltr, 1, right_x, L"abc\n\n"},
+ {ltr, 2, left_x, L"abc\n\n"},
+ {ltr, 2, right_x, ltr},
+
+ {rtl, 1, left_x, L"\x5d0\x5d1\x5d2\n\n"},
+ {rtl, 1, right_x, L"\x5d0\x5d1\x5d2\n"},
+ {rtl, 2, left_x, rtl},
+ {rtl, 2, right_x, L"\x5d0\x5d1\x5d2\n\n"},
+ };
+
+ RenderText* render_text = GetRenderText();
+ render_text->SetMultiline(true);
+ render_text->SetDisplayRect(Rect(200, 1000));
+
+ for (size_t i = 0; i < arraysize(cases); i++) {
+ SCOPED_TRACE(base::StringPrintf("Testing case %" PRIuS "", i));
+ render_text->SetText(WideToUTF16(cases[i].text));
+ test_api()->EnsureLayout();
+
+ EXPECT_EQ(3u, test_api()->lines().size());
+ // Position the cursor at the logical beginning of text.
+ render_text->SelectRange(Range(0));
+
+ render_text->MoveCursorTo(
+ Point(cases[i].x, GetCursorYForTesting(cases[i].line_num)), true);
+ EXPECT_EQ(WideToUTF16(cases[i].selected_text),
+ GetSelectedText(render_text));
+ }
+}
+
+// Tests that GetSubstringBounds returns the correct bounds for multiline text.
+TEST_P(RenderTextHarfBuzzTest, GetSubstringBoundsMultiline) {
+ RenderText* render_text = GetRenderText();
+ render_text->SetMultiline(true);
+ render_text->SetDisplayRect(Rect(200, 1000));
+ render_text->SetText(WideToUTF16(L"abc\n\ndef"));
+ test_api()->EnsureLayout();
+
+ const std::vector<Range> line_char_range = {Range(0, 3), Range(4, 5),
+ Range(5, 8)};
+
+ // Test bounds for individual lines.
+ EXPECT_EQ(3u, test_api()->lines().size());
+ Rect expected_total_bounds;
+ for (size_t i = 0; i < test_api()->lines().size(); i++) {
+ SCOPED_TRACE(base::StringPrintf("Testing bounds for line %" PRIuS "", i));
+ const internal::Line& line = test_api()->lines()[i];
+ const Size line_size(std::ceil(line.size.width()),
+ std::ceil(line.size.height()));
+ const Rect expected_line_bounds =
+ render_text->GetLineOffset(i) + Rect(line_size);
+ expected_total_bounds.Union(expected_line_bounds);
+
+ render_text->SelectRange(line_char_range[i]);
+ EXPECT_EQ(expected_line_bounds, GetSelectionBoundsUnion());
+ }
+
+ // Test complete bounds.
+ render_text->SelectAll(false);
+ EXPECT_EQ(expected_total_bounds, GetSelectionBoundsUnion());
+}
+
// Prefix for test instantiations intentionally left blank since each test
// fixture class has a single parameterization.
#if defined(OS_MACOSX)

Powered by Google App Engine
This is Rietveld 408576698