summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/gfx/render_text.cc69
-rw-r--r--ui/gfx/render_text.h19
-rw-r--r--ui/gfx/render_text_linux.cc38
-rw-r--r--ui/gfx/render_text_linux.h20
-rw-r--r--ui/gfx/render_text_unittest.cc337
-rw-r--r--ui/gfx/render_text_win.cc61
-rw-r--r--ui/gfx/render_text_win.h12
7 files changed, 114 insertions, 442 deletions
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 6d15e5c..1111842 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -247,12 +247,6 @@ bool RenderText::MoveCursorTo(const SelectionModel& selection_model) {
sel.set_caret_pos(end.caret_pos());
sel.set_caret_placement(end.caret_placement());
}
-
- if (!IsCursorablePosition(sel.selection_start()) ||
- !IsCursorablePosition(sel.selection_end()) ||
- !IsCursorablePosition(sel.caret_pos()))
- return false;
-
bool changed = !sel.Equals(selection_model_);
SetSelectionModel(sel);
return changed;
@@ -287,28 +281,41 @@ void RenderText::SelectAll() {
SetSelectionModel(sel);
}
+// TODO(xji): it does not work for languages do not use space as word breaker,
+// such as Chinese. Should use BreakIterator.
void RenderText::SelectWord() {
+ size_t selection_start = GetSelectionStart();
size_t cursor_position = GetCursorPosition();
+ // First we setup selection_start_ and selection_end_. There are so many cases
+ // because we try to emulate what select-word looks like in a gtk textfield.
+ // See associated testcase for different cases.
+ if (cursor_position > 0 && cursor_position < text().length()) {
+ if (u_isalnum(text()[cursor_position])) {
+ selection_start = cursor_position;
+ cursor_position++;
+ } else
+ selection_start = cursor_position - 1;
+ } else if (cursor_position == 0) {
+ selection_start = cursor_position;
+ if (text().length() > 0)
+ cursor_position++;
+ } else {
+ selection_start = cursor_position - 1;
+ }
- base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
- bool success = iter.Init();
- DCHECK(success);
- if (!success)
- return;
-
- size_t selection_start = cursor_position;
- for (; selection_start != 0; --selection_start) {
- if (iter.IsStartOfWord(selection_start) ||
- iter.IsEndOfWord(selection_start))
+ // Now we move selection_start_ to beginning of selection. Selection boundary
+ // is defined as the position where we have alpha-num character on one side
+ // and non-alpha-num char on the other side.
+ for (; selection_start > 0; selection_start--) {
+ if (IsPositionAtWordSelectionBoundary(selection_start))
break;
}
- if (selection_start == cursor_position)
- ++cursor_position;
-
- for (; cursor_position < text().length(); ++cursor_position) {
- if (iter.IsEndOfWord(cursor_position) ||
- iter.IsStartOfWord(cursor_position))
+ // Now we move selection_end_ to end of selection. Selection boundary
+ // is defined as the position where we have alpha-num character on one side
+ // and non-alpha-num char on the other side.
+ for (; cursor_position < text().length(); cursor_position++) {
+ if (IsPositionAtWordSelectionBoundary(cursor_position))
break;
}
@@ -455,10 +462,6 @@ const Rect& RenderText::GetUpdatedCursorBounds() {
return cursor_bounds_;
}
-size_t RenderText::GetIndexOfNextGrapheme(size_t position) {
- return IndexOfAdjacentGrapheme(position, true);
-}
-
RenderText::RenderText()
: text_(),
selection_model_(),
@@ -554,7 +557,8 @@ SelectionModel RenderText::RightEndSelectionModel() {
}
size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) {
- return IndexOfAdjacentGrapheme(position, false);
+ // TODO(msw): Handle complex script.
+ return std::max(static_cast<long>(position - 1), static_cast<long>(0));
}
std::vector<Rect> RenderText::GetSubstringBounds(size_t from, size_t to) {
@@ -628,10 +632,13 @@ void RenderText::MoveCursorTo(size_t position, bool select) {
SelectionModel::CaretPlacement placement = (caret_pos == cursor) ?
SelectionModel::LEADING : SelectionModel::TRAILING;
size_t selection_start = select ? GetSelectionStart() : cursor;
- if (IsCursorablePosition(cursor)) {
- SelectionModel sel(selection_start, cursor, caret_pos, placement);
- SetSelectionModel(sel);
- }
+ SelectionModel sel(selection_start, cursor, caret_pos, placement);
+ SetSelectionModel(sel);
+}
+
+bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) {
+ return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) ||
+ (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos]));
}
void RenderText::UpdateCachedBoundsAndOffset() {
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 4a1f9a9..82eab91 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -193,9 +193,7 @@ class UI_EXPORT RenderText {
// Set the selection_model_ to the value of |selection|.
// The selection model components are modified if invalid.
// Returns true if the cursor position or selection range changed.
- // If |selectin_start_| or |selection_end_| or |caret_pos_| in
- // |selection_model| is not a cursorable position (not on grapheme boundary),
- // it is a NO-OP and returns false.
+ // TODO(xji): need to check the cursor is set at grapheme boundary.
bool MoveCursorTo(const SelectionModel& selection_model);
// Move the cursor to the position associated with the clicked point.
@@ -256,9 +254,6 @@ class UI_EXPORT RenderText {
// Subsequent text, cursor, or bounds changes may invalidate returned values.
const Rect& GetUpdatedCursorBounds();
- // Get the logical index of the grapheme following the argument |position|.
- virtual size_t GetIndexOfNextGrapheme(size_t position);
-
protected:
RenderText();
@@ -293,10 +288,6 @@ class UI_EXPORT RenderText {
// TODO(msw) Re-evaluate this function's necessity and signature.
virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to);
- // Return true if cursor can appear in front of the character at |position|,
- // which means it is a grapheme boundary or the first character in the text.
- virtual bool IsCursorablePosition(size_t position) = 0;
-
// Apply composition style (underline) to composition range and selection
// style (foreground) to selection range.
void ApplyCompositionAndSelectionStyles(StyleRanges* style_ranges) const;
@@ -314,20 +305,16 @@ class UI_EXPORT RenderText {
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust);
- // Return an index belonging to the |next| or previous logical grapheme.
- // The return value is bounded by 0 and the text length, inclusive.
- virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) = 0;
-
// Sets the selection model, the argument is assumed to be valid.
void SetSelectionModel(const SelectionModel& selection_model);
// Set the cursor to |position|, with the caret trailing the previous
// grapheme, or if there is no previous grapheme, leading the cursor position.
// If |select| is false, the selection start is moved to the same position.
- // If the |position| is not a cursorable position (not on grapheme boundary),
- // it is a NO-OP.
void MoveCursorTo(size_t position, bool select);
+ bool IsPositionAtWordSelectionBoundary(size_t pos);
+
// Update the cached bounds and display offset to ensure that the current
// cursor is within the visible display area.
void UpdateCachedBoundsAndOffset();
diff --git a/ui/gfx/render_text_linux.cc b/ui/gfx/render_text_linux.cc
index d5ac9c1..1e5e865 100644
--- a/ui/gfx/render_text_linux.cc
+++ b/ui/gfx/render_text_linux.cc
@@ -123,19 +123,13 @@ void RenderTextLinux::Draw(Canvas* canvas) {
}
SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) {
+ // TODO(xji): when points outside of text, return HOME/END position.
PangoLayout* layout = EnsureLayout();
if (text().empty())
return SelectionModel(0, 0, SelectionModel::LEADING);
Point p(ToTextPoint(point));
-
- // When the point is outside of text, return HOME/END position.
- if (p.x() < 0)
- return LeftEndSelectionModel();
- else if (p.x() > GetStringWidth())
- return RightEndSelectionModel();
-
int caret_pos, trailing;
pango_layout_xy_to_index(layout, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE,
&caret_pos, &trailing);
@@ -218,7 +212,7 @@ SelectionModel RenderTextLinux::LeftEndSelectionModel() {
return SelectionModel(text().length(), caret, SelectionModel::LEADING);
} else { // RTL.
size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length,
- false);
+ PREVIOUS);
return SelectionModel(text().length(), caret, SelectionModel::TRAILING);
}
}
@@ -233,7 +227,7 @@ SelectionModel RenderTextLinux::RightEndSelectionModel() {
PangoItem* item = last_visual_run->item;
if (item->analysis.level % 2 == 0) { // LTR.
size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length,
- false);
+ PREVIOUS);
return SelectionModel(text().length(), caret, SelectionModel::TRAILING);
} else { // RTL.
size_t caret = Utf8IndexToUtf16Index(item->offset);
@@ -244,18 +238,16 @@ SelectionModel RenderTextLinux::RightEndSelectionModel() {
return SelectionModel(0, 0, SelectionModel::LEADING);
}
-bool RenderTextLinux::IsCursorablePosition(size_t position) {
- if (position == 0 && text().empty())
- return true;
-
+size_t RenderTextLinux::GetIndexOfPreviousGrapheme(size_t position) {
EnsureLayout();
- return (position >= 0 && position < static_cast<size_t>(num_log_attrs_) &&
- log_attrs_[position].is_cursor_position);
+ size_t index = Utf16IndexToUtf8Index(position);
+ return Utf16IndexOfAdjacentGrapheme(index, PREVIOUS);
}
-size_t RenderTextLinux::IndexOfAdjacentGrapheme(size_t index, bool next) {
+size_t RenderTextLinux::GetIndexOfNextGrapheme(size_t position) {
EnsureLayout();
- return Utf16IndexOfAdjacentGrapheme(Utf16IndexToUtf8Index(index), next);
+ size_t index = Utf16IndexToUtf8Index(position);
+ return Utf16IndexOfAdjacentGrapheme(index, NEXT);
}
GSList* RenderTextLinux::GetRunContainingPosition(size_t position) const {
@@ -274,12 +266,12 @@ GSList* RenderTextLinux::GetRunContainingPosition(size_t position) const {
size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme(
size_t utf8_index_of_current_grapheme,
- bool next) const {
+ RelativeLogicalPosition pos) const {
const char* ch = layout_text_ + utf8_index_of_current_grapheme;
int char_offset = static_cast<int>(g_utf8_pointer_to_offset(layout_text_,
ch));
int start_char_offset = char_offset;
- if (!next) {
+ if (pos == PREVIOUS) {
if (char_offset > 0) {
do {
--char_offset;
@@ -300,23 +292,23 @@ size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme(
size_t RenderTextLinux::Utf16IndexOfAdjacentGrapheme(
size_t utf8_index_of_current_grapheme,
- bool next) const {
+ RelativeLogicalPosition pos) const {
size_t utf8_index = Utf8IndexOfAdjacentGrapheme(
- utf8_index_of_current_grapheme, next);
+ utf8_index_of_current_grapheme, pos);
return Utf8IndexToUtf16Index(utf8_index);
}
SelectionModel RenderTextLinux::FirstSelectionModelInsideRun(
const PangoItem* item) const {
size_t caret = Utf8IndexToUtf16Index(item->offset);
- size_t cursor = Utf16IndexOfAdjacentGrapheme(item->offset, true);
+ size_t cursor = Utf16IndexOfAdjacentGrapheme(item->offset, NEXT);
return SelectionModel(cursor, caret, SelectionModel::TRAILING);
}
SelectionModel RenderTextLinux::LastSelectionModelInsideRun(
const PangoItem* item) const {
size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length,
- false);
+ PREVIOUS);
return SelectionModel(caret, caret, SelectionModel::LEADING);
}
diff --git a/ui/gfx/render_text_linux.h b/ui/gfx/render_text_linux.h
index 641e261..5d67b41 100644
--- a/ui/gfx/render_text_linux.h
+++ b/ui/gfx/render_text_linux.h
@@ -39,22 +39,28 @@ class RenderTextLinux : public RenderText {
BreakType break_type) OVERRIDE;
virtual SelectionModel LeftEndSelectionModel() OVERRIDE;
virtual SelectionModel RightEndSelectionModel() OVERRIDE;
- virtual bool IsCursorablePosition(size_t position) OVERRIDE;
+ virtual size_t GetIndexOfPreviousGrapheme(size_t position) OVERRIDE;
private:
- virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) OVERRIDE;
+ enum RelativeLogicalPosition {
+ PREVIOUS,
+ NEXT
+ };
+
+ // Get the logical start index of the next grapheme after |position|.
+ size_t GetIndexOfNextGrapheme(size_t position);
// Returns the run that contains |position|. Return NULL if not found.
GSList* GetRunContainingPosition(size_t position) const;
// Given |utf8_index_of_current_grapheme|, returns the UTF8 or UTF16 index of
- // next grapheme in the text if |next| is true, otherwise, returns the index
- // of previous grapheme. Returns 0 if there is no previous grapheme, and
- // returns the |text_| length if there is no next grapheme.
+ // next grapheme in the text if |pos| is NEXT, otherwise, returns the index of
+ // previous grapheme. Returns 0 if there is no previous grapheme, and returns
+ // the |text_| length if there is no next grapheme.
size_t Utf8IndexOfAdjacentGrapheme(size_t utf8_index_of_current_grapheme,
- bool next) const;
+ RelativeLogicalPosition pos) const;
size_t Utf16IndexOfAdjacentGrapheme(size_t utf8_index_of_current_grapheme,
- bool next) const;
+ RelativeLogicalPosition pos) const;
// Given a |run|, returns the SelectionModel that contains the logical first
// or last caret position inside (not at a boundary of) the run.
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 6898537..26389df 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -15,14 +15,14 @@ class RenderTextTest : public testing::Test {
TEST_F(RenderTextTest, DefaultStyle) {
// Defaults to empty text with no styles.
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
+ scoped_ptr<gfx::RenderText> render_text(gfx::RenderText::CreateRenderText());
EXPECT_TRUE(render_text->text().empty());
EXPECT_TRUE(render_text->style_ranges().empty());
// Test that the built-in default style is applied for new text.
render_text->SetText(ASCIIToUTF16("abc"));
EXPECT_EQ(1U, render_text->style_ranges().size());
- StyleRange style;
+ gfx::StyleRange style;
EXPECT_EQ(style.font.GetFontName(),
render_text->style_ranges()[0].font.GetFontName());
EXPECT_EQ(style.foreground, render_text->style_ranges()[0].foreground);
@@ -38,8 +38,8 @@ TEST_F(RenderTextTest, DefaultStyle) {
TEST_F(RenderTextTest, CustomDefaultStyle) {
// Test a custom default style.
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
- StyleRange color;
+ scoped_ptr<gfx::RenderText> render_text(gfx::RenderText::CreateRenderText());
+ gfx::StyleRange color;
color.foreground = SK_ColorRED;
render_text->set_default_style(color);
render_text->SetText(ASCIIToUTF16("abc"));
@@ -54,7 +54,7 @@ TEST_F(RenderTextTest, CustomDefaultStyle) {
EXPECT_EQ(color.foreground, render_text->style_ranges()[0].foreground);
// Test ApplyDefaultStyle after setting a new default.
- StyleRange strike;
+ gfx::StyleRange strike;
strike.strike = true;
render_text->set_default_style(strike);
render_text->ApplyDefaultStyle();
@@ -64,24 +64,24 @@ TEST_F(RenderTextTest, CustomDefaultStyle) {
}
TEST_F(RenderTextTest, ApplyStyleRange) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
+ scoped_ptr<gfx::RenderText> render_text(gfx::RenderText::CreateRenderText());
render_text->SetText(ASCIIToUTF16("01234"));
EXPECT_EQ(1U, render_text->style_ranges().size());
// Test ApplyStyleRange (no-op on empty range).
- StyleRange empty;
+ gfx::StyleRange empty;
empty.range = ui::Range(1, 1);
render_text->ApplyStyleRange(empty);
EXPECT_EQ(1U, render_text->style_ranges().size());
// Test ApplyStyleRange (no-op on invalid range).
- StyleRange invalid;
+ gfx::StyleRange invalid;
invalid.range = ui::Range::InvalidRange();
render_text->ApplyStyleRange(invalid);
EXPECT_EQ(1U, render_text->style_ranges().size());
// Apply a style with a range contained by an existing range.
- StyleRange underline;
+ gfx::StyleRange underline;
underline.underline = true;
underline.range = ui::Range(2, 3);
render_text->ApplyStyleRange(underline);
@@ -94,7 +94,7 @@ TEST_F(RenderTextTest, ApplyStyleRange) {
EXPECT_FALSE(render_text->style_ranges()[2].underline);
// Apply a style with a range equal to another range.
- StyleRange color;
+ gfx::StyleRange color;
color.foreground = SK_ColorWHITE;
color.range = ui::Range(2, 3);
render_text->ApplyStyleRange(color);
@@ -111,7 +111,7 @@ TEST_F(RenderTextTest, ApplyStyleRange) {
// Apply a style with a range containing an existing range.
// This new style also overlaps portions of neighboring ranges.
- StyleRange strike;
+ gfx::StyleRange strike;
strike.strike = true;
strike.range = ui::Range(1, 4);
render_text->ApplyStyleRange(strike);
@@ -124,7 +124,7 @@ TEST_F(RenderTextTest, ApplyStyleRange) {
EXPECT_FALSE(render_text->style_ranges()[2].strike);
// Apply a style overlapping all ranges.
- StyleRange strike_underline;
+ gfx::StyleRange strike_underline;
strike_underline.strike = true;
strike_underline.underline = true;
strike_underline.range = ui::Range(0, render_text->text().length());
@@ -144,7 +144,7 @@ TEST_F(RenderTextTest, ApplyStyleRange) {
TEST_F(RenderTextTest, StyleRangesAdjust) {
// Test that style ranges adjust to the text size.
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
+ scoped_ptr<gfx::RenderText> render_text(gfx::RenderText::CreateRenderText());
render_text->SetText(ASCIIToUTF16("abcdef"));
EXPECT_EQ(1U, render_text->style_ranges().size());
EXPECT_EQ(ui::Range(0, 6), render_text->style_ranges()[0].range);
@@ -155,7 +155,7 @@ TEST_F(RenderTextTest, StyleRangesAdjust) {
EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
// Test that the last range extends to the length of longer text.
- StyleRange strike;
+ gfx::StyleRange strike;
strike.strike = true;
strike.range = ui::Range(2, 3);
render_text->ApplyStyleRange(strike);
@@ -178,313 +178,4 @@ TEST_F(RenderTextTest, StyleRangesAdjust) {
EXPECT_FALSE(render_text->style_ranges()[0].strike);
}
-void RunMoveCursorLeftRightTest(RenderText* render_text,
- const std::vector<SelectionModel>& expected,
- bool move_right) {
- for (int i = 0; i < static_cast<int>(expected.size()); ++i) {
- SelectionModel sel = expected[i];
- EXPECT_TRUE(render_text->selection_model().Equals(sel));
- if (move_right)
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- else
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- }
-
- SelectionModel sel = expected[expected.size() - 1];
- if (move_right)
- render_text->MoveCursorRight(LINE_BREAK, false);
- else
- render_text->MoveCursorLeft(LINE_BREAK, false);
- EXPECT_TRUE(render_text->selection_model().Equals(sel));
-}
-
-TEST_F(RenderTextTest, MoveCursorLeftRightInLtr) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
-
- // Pure LTR.
- render_text->SetText(ASCIIToUTF16("abc"));
- // |expected| saves the expected SelectionModel when moving cursor from left
- // to right.
- std::vector<SelectionModel> expected;
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 1, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- // The last element is to test the clamped line ends.
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- RunMoveCursorLeftRightTest(render_text.get(), expected, true);
-
- expected.clear();
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 1, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- RunMoveCursorLeftRightTest(render_text.get(), expected, false);
-}
-
-TEST_F(RenderTextTest, MoveCursorLeftRightInLtrRtl) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
- // LTR-RTL
- render_text->SetText(WideToUTF16(L"abc\x05d0\x05d1\x05d2"));
- // The last one is the expected END position.
- std::vector<SelectionModel> expected;
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 1, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(5, 5, SelectionModel::LEADING));
- expected.push_back(SelectionModel(4, 4, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 3, SelectionModel::LEADING));
- expected.push_back(SelectionModel(6, 3, SelectionModel::LEADING));
- expected.push_back(SelectionModel(6, 3, SelectionModel::LEADING));
- RunMoveCursorLeftRightTest(render_text.get(), expected, true);
-
- expected.clear();
- expected.push_back(SelectionModel(6, 3, SelectionModel::LEADING));
- expected.push_back(SelectionModel(4, 3, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(5, 4, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(6, 5, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 1, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- RunMoveCursorLeftRightTest(render_text.get(), expected, false);
-}
-
-TEST_F(RenderTextTest, MoveCursorLeftRightInLtrRtlLtr) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
- // LTR-RTL-LTR.
- render_text->SetText(WideToUTF16(L"a"L"\x05d1"L"b"));
- std::vector<SelectionModel> expected;
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(1, 1, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- RunMoveCursorLeftRightTest(render_text.get(), expected, true);
-
- expected.clear();
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(2, 1, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- RunMoveCursorLeftRightTest(render_text.get(), expected, false);
-}
-
-TEST_F(RenderTextTest, MoveCursorLeftRightInRtl) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
- // Pure RTL.
- render_text->SetText(WideToUTF16(L"\x05d0\x05d1\x05d2"));
- render_text->MoveCursorRight(LINE_BREAK, false);
- std::vector<SelectionModel> expected;
-
-#if defined(OS_LINUX)
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
-#else
- expected.push_back(SelectionModel(3, 0, SelectionModel::LEADING));
-#endif
- expected.push_back(SelectionModel(1, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 1, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
-#if defined(OS_LINUX)
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
-#else
- expected.push_back(SelectionModel(0, 2, SelectionModel::TRAILING));
- // TODO(xji): expected (0, 2, TRAILING), actual (3, 0, LEADING).
- // cursor moves from leftmost to rightmost.
- // expected.push_back(SelectionModel(0, 2, SelectionModel::TRAILING));
-#endif
- RunMoveCursorLeftRightTest(render_text.get(), expected, false);
-
- expected.clear();
-
-#if defined(OS_LINUX)
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
-#else
- expected.push_back(SelectionModel(0, 2, SelectionModel::TRAILING));
-#endif
- expected.push_back(SelectionModel(2, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 1, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
-#if defined(OS_LINUX)
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
-#else
- expected.push_back(SelectionModel(3, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 0, SelectionModel::LEADING));
-#endif
- RunMoveCursorLeftRightTest(render_text.get(), expected, true);
-}
-
-TEST_F(RenderTextTest, MoveCursorLeftRightInRtlLtr) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
- // RTL-LTR
- render_text->SetText(WideToUTF16(L"\x05d0\x05d1\x05d2"L"abc"));
- render_text->MoveCursorRight(LINE_BREAK, false);
- std::vector<SelectionModel> expected;
-#if defined(OS_LINUX)
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 1, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(5, 5, SelectionModel::LEADING));
- expected.push_back(SelectionModel(4, 4, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 3, SelectionModel::LEADING));
- expected.push_back(SelectionModel(6, 3, SelectionModel::LEADING));
- expected.push_back(SelectionModel(6, 3, SelectionModel::LEADING));
-#else
- expected.push_back(SelectionModel(6, 5, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(5, 5, SelectionModel::LEADING));
- expected.push_back(SelectionModel(4, 4, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 3, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 1, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(0, 2, SelectionModel::TRAILING));
- // TODO(xji): expected (0, 2, TRAILING), actual (3, 0, LEADING).
- // cursor moves from leftmost to middle.
- // expected.push_back(SelectionModel(0, 2, SelectionModel::TRAILING));
-#endif
-
- RunMoveCursorLeftRightTest(render_text.get(), expected, false);
-
- expected.clear();
-#if defined(OS_LINUX)
- expected.push_back(SelectionModel(6, 3, SelectionModel::LEADING));
- expected.push_back(SelectionModel(4, 3, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(5, 4, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(6, 5, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 1, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
-#else
- expected.push_back(SelectionModel(0, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 1, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(4, 3, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(5, 4, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(6, 5, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(6, 5, SelectionModel::TRAILING));
-#endif
- RunMoveCursorLeftRightTest(render_text.get(), expected, true);
-}
-
-TEST_F(RenderTextTest, MoveCursorLeftRightInRtlLtrRtl) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
- // RTL-LTR-RTL.
- render_text->SetText(WideToUTF16(L"\x05d0"L"a"L"\x05d1"));
- render_text->MoveCursorRight(LINE_BREAK, false);
- std::vector<SelectionModel> expected;
-#if defined(OS_LINUX)
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(1, 1, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
-#else
- expected.push_back(SelectionModel(3, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(1, 1, SelectionModel::LEADING));
- expected.push_back(SelectionModel(1, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::TRAILING));
- // TODO(xji): expected (0, 0, TRAILING), actual (2, 1, LEADING).
- // cursor moves from leftmost to middle.
- // expected.push_back(SelectionModel(0, 0, SelectionModel::TRAILING));
-#endif
- RunMoveCursorLeftRightTest(render_text.get(), expected, false);
-
- expected.clear();
-#if defined(OS_LINUX)
- expected.push_back(SelectionModel(3, 2, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(2, 1, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
-#else
- expected.push_back(SelectionModel(0, 0, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
- expected.push_back(SelectionModel(2, 1, SelectionModel::TRAILING));
- expected.push_back(SelectionModel(2, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::LEADING));
- expected.push_back(SelectionModel(3, 2, SelectionModel::LEADING));
-#endif
- RunMoveCursorLeftRightTest(render_text.get(), expected, true);
-}
-
-// TODO(xji): temporarily disable in platform Win since the complex script
-// characters turned into empty square due to font regression. So, not able
-// to test 2 characters belong to the same grapheme.
-#if defined(OS_LINUX)
-TEST_F(RenderTextTest, MoveCursorLeftRight_ComplexScript) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
-
- render_text->SetText(WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915"));
- EXPECT_EQ(0U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(2U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(5U, render_text->GetCursorPosition());
-
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- EXPECT_EQ(2U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- EXPECT_EQ(0U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- EXPECT_EQ(0U, render_text->GetCursorPosition());
-}
-#endif
-
-TEST_F(RenderTextTest, MoveCursorLeftRightWithSelection) {
- scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
- render_text->SetText(WideToUTF16(L"abc\x05d0\x05d1\x05d2"));
- // Left arrow on select ranging (6, 4).
- render_text->MoveCursorRight(LINE_BREAK, false);
- EXPECT_EQ(6U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- EXPECT_EQ(6U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, true);
- EXPECT_EQ(6U, render_text->GetSelectionStart());
- EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, true);
- EXPECT_EQ(6U, render_text->GetSelectionStart());
- EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- EXPECT_EQ(6U, render_text->GetCursorPosition());
-
- // Right arrow on select ranging (4, 6).
- render_text->MoveCursorLeft(LINE_BREAK, false);
- EXPECT_EQ(0U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(1U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(2U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(3U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, true);
- EXPECT_EQ(4U, render_text->GetSelectionStart());
- EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, true);
- EXPECT_EQ(4U, render_text->GetSelectionStart());
- EXPECT_EQ(6U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- EXPECT_EQ(4U, render_text->GetCursorPosition());
-}
-
} // namespace gfx
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index f6b983d..c2c9acf 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -237,6 +237,10 @@ SelectionModel RenderTextWin::RightEndSelectionModel() {
return SelectionModel(cursor, caret, placement);
}
+size_t RenderTextWin::GetIndexOfPreviousGrapheme(size_t position) {
+ return IndexOfAdjacentGrapheme(position, false);
+}
+
std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
ui::Range range(from, to);
DCHECK(ui::Range(0, text().length()).Contains(range));
@@ -291,41 +295,6 @@ std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
return bounds;
}
-bool RenderTextWin::IsCursorablePosition(size_t position) {
- if (position == 0 || position == text().length())
- return true;
-
- size_t run_index = GetRunContainingPosition(position);
- if (run_index >= runs_.size())
- return false;
-
- internal::TextRun* run = runs_[run_index];
- size_t start = run->range.start();
- if (position == start)
- return true;
- return run->logical_clusters[position - start] !=
- run->logical_clusters[position - start - 1];
-}
-
-size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) {
- size_t run_index = GetRunContainingPosition(index);
- internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL;
- long start = run ? run->range.start() : 0;
- long length = run ? run->range.length() : text().length();
- long ch = index - start;
- WORD cluster = run ? run->logical_clusters[ch] : 0;
-
- if (!next) {
- do {
- ch--;
- } while (ch >= 0 && run && run->logical_clusters[ch] == cluster);
- } else {
- while (ch < length && run && run->logical_clusters[ch] == cluster)
- ch++;
- }
- return std::max(static_cast<long>(std::min(ch, length) + start), 0L);
-}
-
void RenderTextWin::ItemizeLogicalText() {
text_is_dirty_ = false;
STLDeleteContainerPointers(runs_.begin(), runs_.end());
@@ -492,16 +461,34 @@ size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
return run;
}
+size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) const {
+ size_t run_index = GetRunContainingPosition(index);
+ internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL;
+ long start = run ? run->range.start() : 0;
+ long length = run ? run->range.length() : text().length();
+ long ch = index - start;
+ WORD cluster = run ? run->logical_clusters[ch] : 0;
+
+ if (!next) {
+ do {
+ ch--;
+ } while (ch >= 0 && run && run->logical_clusters[ch] == cluster);
+ } else {
+ while (ch < length && run && run->logical_clusters[ch] == cluster)
+ ch++;
+ }
+ return std::max(static_cast<long>(std::min(ch, length) + start), 0L);
+}
SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
- internal::TextRun* run) {
+ internal::TextRun* run) const {
size_t caret = run->range.start();
size_t cursor = IndexOfAdjacentGrapheme(caret, true);
return SelectionModel(cursor, caret, SelectionModel::TRAILING);
}
SelectionModel RenderTextWin::LastSelectionModelInsideRun(
- internal::TextRun* run) {
+ internal::TextRun* run) const {
size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false);
return SelectionModel(caret, caret, SelectionModel::LEADING);
}
diff --git a/ui/gfx/render_text_win.h b/ui/gfx/render_text_win.h
index 53eea69..d812f50 100644
--- a/ui/gfx/render_text_win.h
+++ b/ui/gfx/render_text_win.h
@@ -72,12 +72,10 @@ class RenderTextWin : public RenderText {
BreakType break_type) OVERRIDE;
virtual SelectionModel LeftEndSelectionModel() OVERRIDE;
virtual SelectionModel RightEndSelectionModel() OVERRIDE;
+ virtual size_t GetIndexOfPreviousGrapheme(size_t position) OVERRIDE;
virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to) OVERRIDE;
- virtual bool IsCursorablePosition(size_t position) OVERRIDE;
private:
- virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) OVERRIDE;
-
void ItemizeLogicalText();
void LayoutVisualText(HDC hdc);
@@ -86,11 +84,15 @@ class RenderTextWin : public RenderText {
size_t GetRunContainingPosition(size_t position) const;
size_t GetRunContainingPoint(const Point& point) const;
+ // Return an index belonging to the |next| or previous logical grapheme.
+ // The return value is bounded by 0 and the text length, inclusive.
+ size_t IndexOfAdjacentGrapheme(size_t index, bool next) const;
+
// Given a |run|, returns the SelectionModel that contains the logical first
// or last caret position inside (not at a boundary of) the run.
// The returned value represents a cursor/caret position without a selection.
- SelectionModel FirstSelectionModelInsideRun(internal::TextRun*);
- SelectionModel LastSelectionModelInsideRun(internal::TextRun*);
+ SelectionModel FirstSelectionModelInsideRun(internal::TextRun*) const;
+ SelectionModel LastSelectionModelInsideRun(internal::TextRun*) const;
// Get the selection model visually left/right of |selection| by one grapheme.
// The returned value represents a cursor/caret position without a selection.