summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbenrg@chromium.org <benrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-21 01:27:28 +0000
committerbenrg@chromium.org <benrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-21 01:27:28 +0000
commitd66009e4f25eab259ce05315ff8b506cb52c24f6 (patch)
treedc854e251b329b0aa8cf95ae2378cd3561af33df
parentd4ab68006f402ef6e5ccf0fc3b075ab65b638f3e (diff)
downloadchromium_src-d66009e4f25eab259ce05315ff8b506cb52c24f6.zip
chromium_src-d66009e4f25eab259ce05315ff8b506cb52c24f6.tar.gz
chromium_src-d66009e4f25eab259ce05315ff8b506cb52c24f6.tar.bz2
Merge left and right cursor movement code in RenderText, and misc fixes
* Combine method pairs for left/right cursor motion in RenderText{,Linux,Win}, eliminating a lot of duplicate logic. The merged functions use new enums VisualTextDirection {VISUAL_LEFT, VISUAL_RIGHT} and LogicalTextDirection {LOGICAL_PREVIOUS, LOGICAL_NEXT}. * Make CalculateSubstringBounds and GetSelectionBounds return the result instead of taking it as an out pointer argument. * Remove Utf16IndexOfAdjacentGrapheme for clarity (it took a UTF-8 index as its argument) * Delete some unused obsolete methods in RenderText, remove some logging code, and fix some comments. The logic should be unchanged except that in the Backspace handler in NativeTextfieldViews, cursor_changed is now set to true only if the text changed (like Delete). Formerly it was always set to true, which appears to be a bug. BUG=none TEST=existing unit tests Review URL: http://codereview.chromium.org/8958024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@118580 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ui/gfx/render_text.cc184
-rw-r--r--ui/gfx/render_text.h100
-rw-r--r--ui/gfx/render_text_linux.cc439
-rw-r--r--ui/gfx/render_text_linux.h66
-rw-r--r--ui/gfx/render_text_unittest.cc124
-rw-r--r--ui/gfx/render_text_win.cc247
-rw-r--r--ui/gfx/render_text_win.h28
-rw-r--r--ui/views/controls/textfield/native_textfield_views.cc48
-rw-r--r--ui/views/controls/textfield/textfield_views_model.cc30
-rw-r--r--ui/views/controls/textfield/textfield_views_model.h7
-rw-r--r--ui/views/controls/textfield/textfield_views_model_unittest.cc222
11 files changed, 616 insertions, 879 deletions
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index ec744ca..bc62a66 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -18,6 +18,16 @@
namespace {
+// Color settings for text, backgrounds and cursor.
+// These are tentative, and should be derived from theme, system
+// settings and current settings.
+// TODO(oshima): Change this to match the standard chrome
+// before dogfooding textfield views.
+const SkColor kSelectedTextColor = SK_ColorWHITE;
+const SkColor kFocusedSelectionColor = SkColorSetRGB(30, 144, 255);
+const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY;
+const SkColor kCursorColor = SK_ColorBLACK;
+
#ifndef NDEBUG
// Check StyleRanges invariant conditions: sorted and non-overlapping ranges.
void CheckStyleRanges(const gfx::StyleRanges& style_ranges, size_t length) {
@@ -340,42 +350,26 @@ void RenderText::SetCursorPosition(size_t position) {
MoveCursorTo(position, false);
}
-void RenderText::MoveCursorLeft(BreakType break_type, bool select) {
- SelectionModel position(selection_model());
- position.set_selection_start(GetCursorPosition());
- // Cancelling a selection moves to the edge of the selection.
- if (break_type != LINE_BREAK && !EmptySelection() && !select) {
- // Use the selection start if it is left of the selection end.
- SelectionModel selection_start = GetSelectionModelForSelectionStart();
- if (GetCursorBounds(selection_start, true).x() <
- GetCursorBounds(position, true).x())
- position = selection_start;
- // For word breaks, use the nearest word boundary left of the selection.
- if (break_type == WORD_BREAK)
- position = GetLeftSelectionModel(position, break_type);
- } else {
- position = GetLeftSelectionModel(position, break_type);
- }
- if (select)
- position.set_selection_start(GetSelectionStart());
- MoveCursorTo(position);
-}
-
-void RenderText::MoveCursorRight(BreakType break_type, bool select) {
+void RenderText::MoveCursor(BreakType break_type,
+ VisualCursorDirection direction,
+ bool select) {
SelectionModel position(selection_model());
position.set_selection_start(GetCursorPosition());
// Cancelling a selection moves to the edge of the selection.
if (break_type != LINE_BREAK && !EmptySelection() && !select) {
- // Use the selection start if it is right of the selection end.
SelectionModel selection_start = GetSelectionModelForSelectionStart();
- if (GetCursorBounds(selection_start, true).x() >
- GetCursorBounds(position, true).x())
+ int start_x = GetCursorBounds(selection_start, true).x();
+ int cursor_x = GetCursorBounds(position, true).x();
+ // Use the selection start if it is left (when |direction| is CURSOR_LEFT)
+ // or right (when |direction| is CURSOR_RIGHT) of the selection end.
+ if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x)
position = selection_start;
- // For word breaks, use the nearest word boundary right of the selection.
+ // For word breaks, use the nearest word boundary in the appropriate
+ // |direction|.
if (break_type == WORD_BREAK)
- position = GetRightSelectionModel(position, break_type);
+ position = GetAdjacentSelectionModel(position, break_type, direction);
} else {
- position = GetRightSelectionModel(position, break_type);
+ position = GetAdjacentSelectionModel(position, break_type, direction);
}
if (select)
position.set_selection_start(GetSelectionStart());
@@ -395,10 +389,10 @@ bool RenderText::MoveCursorTo(const SelectionModel& model) {
sel.set_caret_pos(0);
sel.set_caret_placement(SelectionModel::LEADING);
} else if (sel.caret_pos() >= text_length) {
- SelectionModel end = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ?
- LeftEndSelectionModel() : RightEndSelectionModel();
- sel.set_caret_pos(end.caret_pos());
- sel.set_caret_placement(end.caret_placement());
+ SelectionModel end_selection =
+ EdgeSelectionModel(GetVisualDirectionOfLogicalEnd());
+ sel.set_caret_pos(end_selection.caret_pos());
+ sel.set_caret_placement(end_selection.caret_placement());
}
if (!IsCursorablePosition(sel.selection_start()) ||
@@ -429,14 +423,14 @@ bool RenderText::SelectRange(const ui::Range& range) {
size_t pos = end;
SelectionModel::CaretPlacement placement = SelectionModel::LEADING;
if (start < end) {
- pos = GetIndexOfPreviousGrapheme(end);
+ pos = IndexOfAdjacentGrapheme(end, CURSOR_BACKWARD);
DCHECK_LT(pos, end);
placement = SelectionModel::TRAILING;
} else if (end == text_length) {
- SelectionModel boundary = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ?
- LeftEndSelectionModel() : RightEndSelectionModel();
- pos = boundary.caret_pos();
- placement = boundary.caret_placement();
+ SelectionModel end_selection =
+ EdgeSelectionModel(GetVisualDirectionOfLogicalEnd());
+ pos = end_selection.caret_pos();
+ placement = end_selection.caret_placement();
}
SetSelectionModel(SelectionModel(start, end, pos, placement));
return true;
@@ -459,8 +453,8 @@ void RenderText::ClearSelection() {
}
void RenderText::SelectAll() {
- SelectionModel sel(RightEndSelectionModel());
- sel.set_selection_start(LeftEndSelectionModel().selection_start());
+ SelectionModel sel = EdgeSelectionModel(CURSOR_RIGHT);
+ sel.set_selection_start(EdgeSelectionModel(CURSOR_LEFT).selection_start());
SetSelectionModel(sel);
}
@@ -529,10 +523,9 @@ void RenderText::ApplyDefaultStyle() {
UpdateLayout();
}
-base::i18n::TextDirection RenderText::GetTextDirection() {
- if (base::i18n::IsRTL())
- return base::i18n::RIGHT_TO_LEFT;
- return base::i18n::LEFT_TO_RIGHT;
+VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
+ return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ?
+ CURSOR_RIGHT : CURSOR_LEFT;
}
void RenderText::Draw(Canvas* canvas) {
@@ -562,10 +555,6 @@ const Rect& RenderText::GetUpdatedCursorBounds() {
return cursor_bounds_;
}
-size_t RenderText::GetIndexOfNextGrapheme(size_t position) {
- return IndexOfAdjacentGrapheme(position, true);
-}
-
SelectionModel RenderText::GetSelectionModelForSelectionStart() {
size_t selection_start = GetSelectionStart();
size_t selection_end = GetCursorPosition();
@@ -574,9 +563,10 @@ SelectionModel RenderText::GetSelectionModelForSelectionStart() {
selection_start,
SelectionModel::LEADING);
else if (selection_start > selection_end)
- return SelectionModel(selection_start,
- GetIndexOfPreviousGrapheme(selection_start),
- SelectionModel::TRAILING);
+ return SelectionModel(
+ selection_start,
+ IndexOfAdjacentGrapheme(selection_start, CURSOR_BACKWARD),
+ SelectionModel::TRAILING);
return selection_model_;
}
@@ -595,77 +585,18 @@ const Point& RenderText::GetUpdatedDisplayOffset() {
return display_offset_;
}
-SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current,
- BreakType break_type) {
- if (break_type == LINE_BREAK)
- return LeftEndSelectionModel();
- size_t pos = std::max<int>(current.selection_end() - 1, 0);
- if (break_type == CHARACTER_BREAK)
- return SelectionModel(pos, pos, SelectionModel::LEADING);
+SelectionModel RenderText::GetAdjacentSelectionModel(
+ const SelectionModel& current,
+ BreakType break_type,
+ VisualCursorDirection direction) {
+ EnsureLayout();
- // Notes: We always iterate words from the beginning.
- // This is probably fast enough for our usage, but we may
- // want to modify WordIterator so that it can start from the
- // middle of string and advance backwards.
- base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
- bool success = iter.Init();
- DCHECK(success);
- if (!success)
- return current;
- while (iter.Advance()) {
- if (iter.IsWord()) {
- size_t begin = iter.pos() - iter.GetString().length();
- if (begin == current.selection_end()) {
- // The cursor is at the beginning of a word.
- // Move to previous word.
- break;
- } else if (iter.pos() >= current.selection_end()) {
- // The cursor is in the middle or at the end of a word.
- // Move to the top of current word.
- pos = begin;
- break;
- } else {
- pos = iter.pos() - iter.GetString().length();
- }
- }
- }
-
- return SelectionModel(pos, pos, SelectionModel::LEADING);
-}
-
-SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current,
- BreakType break_type) {
- if (text_.empty())
- return SelectionModel(0, 0, SelectionModel::LEADING);
- if (break_type == LINE_BREAK)
- return RightEndSelectionModel();
- size_t pos = std::min(current.selection_end() + 1, text().length());
+ if (break_type == LINE_BREAK || text().empty())
+ return EdgeSelectionModel(direction);
if (break_type == CHARACTER_BREAK)
- return SelectionModel(pos, pos, SelectionModel::LEADING);
-
- base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
- bool success = iter.Init();
- DCHECK(success);
- if (!success)
- return current;
- while (iter.Advance()) {
- pos = iter.pos();
- if (iter.IsWord() && pos > current.selection_end())
- break;
- }
- return SelectionModel(pos, pos, SelectionModel::LEADING);
-}
-
-SelectionModel RenderText::LeftEndSelectionModel() {
- return SelectionModel(0, 0, SelectionModel::LEADING);
-}
-
-SelectionModel RenderText::RightEndSelectionModel() {
- size_t cursor = text().length();
- size_t caret_pos = GetIndexOfPreviousGrapheme(cursor);
- SelectionModel::CaretPlacement placement = (caret_pos == cursor) ?
- SelectionModel::LEADING : SelectionModel::TRAILING;
- return SelectionModel(cursor, caret_pos, placement);
+ return AdjacentCharSelectionModel(current, direction);
+ DCHECK(break_type == WORD_BREAK);
+ return AdjacentWordSelectionModel(current, direction);
}
void RenderText::SetSelectionModel(const SelectionModel& model) {
@@ -680,10 +611,6 @@ void RenderText::SetSelectionModel(const SelectionModel& model) {
cached_bounds_and_offset_valid_ = false;
}
-size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) {
- return IndexOfAdjacentGrapheme(position, false);
-}
-
void RenderText::ApplyCompositionAndSelectionStyles(
StyleRanges* style_ranges) {
// TODO(msw): This pattern ought to be reconsidered; what about composition
@@ -717,7 +644,8 @@ void RenderText::ApplyCompositionAndSelectionStyles(
replacement_mode_style.foreground = kSelectedTextColor;
size_t cursor = GetCursorPosition();
replacement_mode_style.range.set_start(cursor);
- replacement_mode_style.range.set_end(GetIndexOfNextGrapheme(cursor));
+ replacement_mode_style.range.set_end(
+ IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD));
ApplyStyleRangeImpl(style_ranges, replacement_mode_style);
}
}
@@ -807,7 +735,7 @@ int RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
void RenderText::MoveCursorTo(size_t position, bool select) {
size_t cursor = std::min(position, text().length());
- size_t caret_pos = GetIndexOfPreviousGrapheme(cursor);
+ size_t caret_pos = IndexOfAdjacentGrapheme(cursor, CURSOR_BACKWARD);
SelectionModel::CaretPlacement placement = (caret_pos == cursor) ?
SelectionModel::LEADING : SelectionModel::TRAILING;
size_t selection_start = select ? GetSelectionStart() : cursor;
@@ -857,16 +785,14 @@ void RenderText::UpdateCachedBoundsAndOffset() {
}
void RenderText::DrawSelection(Canvas* canvas) {
- TRACE_EVENT0("gfx", "RenderText::DrawSelection");
- std::vector<Rect> sel;
- GetSubstringBounds(GetSelectionStart(), GetCursorPosition(), &sel);
+ std::vector<Rect> sel = GetSubstringBounds(
+ GetSelectionStart(), GetCursorPosition());
SkColor color = focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
canvas->FillRect(color, *i);
}
void RenderText::DrawCursor(Canvas* canvas) {
- TRACE_EVENT0("gfx", "RenderText::DrawCursor");
// Paint cursor. Replace cursor is drawn as rectangle for now.
// TODO(msw): Draw a better cursor with a better indication of association.
if (cursor_visible() && focused()) {
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 65057ab..e784f1d 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -57,16 +57,6 @@ class SkiaTextRenderer {
} // namespace internal
-// Color settings for text, backgrounds and cursor.
-// These are tentative, and should be derived from theme, system
-// settings and current settings.
-// TODO(oshima): Change this to match the standard chrome
-// before dogfooding textfield views.
-const SkColor kSelectedTextColor = SK_ColorWHITE;
-const SkColor kFocusedSelectionColor = SkColorSetRGB(30, 144, 255);
-const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY;
-const SkColor kCursorColor = SK_ColorBLACK;
-
// A visual style applicable to a range of text.
struct UI_EXPORT StyleRange {
StyleRange();
@@ -91,7 +81,22 @@ enum BreakType {
LINE_BREAK,
};
-// TODO(msw): Implement RenderText[Win|Linux] for Uniscribe/Pango BiDi...
+// VisualCursorDirection and LogicalCursorDirection represent directions of
+// motion of the cursor in BiDi text. The combinations that make sense are:
+//
+// base::i18n::TextDirection VisualCursorDirection LogicalCursorDirection
+// LEFT_TO_RIGHT CURSOR_LEFT CURSOR_BACKWARD
+// LEFT_TO_RIGHT CURSOR_RIGHT CURSOR_FORWARD
+// RIGHT_TO_LEFT CURSOR_RIGHT CURSOR_BACKWARD
+// RIGHT_TO_LEFT CURSOR_LEFT CURSOR_FORWARD
+enum VisualCursorDirection {
+ CURSOR_LEFT,
+ CURSOR_RIGHT
+};
+enum LogicalCursorDirection {
+ CURSOR_BACKWARD,
+ CURSOR_FORWARD
+};
// RenderText represents an abstract model of styled text and its corresponding
// visual layout. Support is built in for a cursor, a selection, simple styling,
@@ -144,8 +149,9 @@ class UI_EXPORT RenderText {
// Moves the cursor left or right. Cursor movement is visual, meaning that
// left and right are relative to screen, not the directionality of the text.
// If |select| is false, the selection start is moved to the same position.
- void MoveCursorLeft(BreakType break_type, bool select);
- void MoveCursorRight(BreakType break_type, bool select);
+ void MoveCursor(BreakType break_type,
+ VisualCursorDirection direction,
+ bool select);
// Set the selection_model_ to the value of |selection|.
// The selection model components are modified if invalid.
@@ -197,7 +203,13 @@ class UI_EXPORT RenderText {
// Apply |default_style_| over the entire text range.
void ApplyDefaultStyle();
- virtual base::i18n::TextDirection GetTextDirection();
+ // Returns the dominant direction of the current text.
+ virtual base::i18n::TextDirection GetTextDirection() = 0;
+
+ // Returns the visual movement direction corresponding to the logical end
+ // of the text, considering only the dominant direction returned by
+ // |GetTextDirection()|, not the direction of a particular run.
+ VisualCursorDirection GetVisualDirectionOfLogicalEnd();
// Get the width of the entire string.
virtual int GetStringWidth() = 0;
@@ -220,8 +232,14 @@ 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|.
- size_t GetIndexOfNextGrapheme(size_t position);
+ // Given an |index| in text(), return the next or previous grapheme boundary
+ // in logical order (that is, the nearest index for which
+ // |IsCursorablePosition(index)| returns true). The return value is in the
+ // range 0 to text().length() inclusive (the input is clamped if it is out of
+ // that range). Always moves by at least one character index unless the
+ // supplied index is already at the boundary of the string.
+ virtual size_t IndexOfAdjacentGrapheme(size_t index,
+ LogicalCursorDirection direction) = 0;
// Return a SelectionModel with the cursor at the current selection's start.
// The returned value represents a cursor/caret position without a selection.
@@ -240,28 +258,37 @@ class UI_EXPORT RenderText {
// Get the selection model that visually neighbors |position| by |break_type|.
// The returned value represents a cursor/caret position without a selection.
- virtual SelectionModel GetLeftSelectionModel(const SelectionModel& current,
- BreakType break_type);
- virtual SelectionModel GetRightSelectionModel(const SelectionModel& current,
- BreakType break_type);
+ SelectionModel GetAdjacentSelectionModel(const SelectionModel& current,
+ BreakType break_type,
+ VisualCursorDirection direction);
+
+ // Get the selection model visually left/right of |selection| by one grapheme.
+ // The returned value represents a cursor/caret position without a selection.
+ virtual SelectionModel AdjacentCharSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) = 0;
+
+ // Get the selection model visually left/right of |selection| by one word.
+ // The returned value represents a cursor/caret position without a selection.
+ virtual SelectionModel AdjacentWordSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) = 0;
// Get the SelectionModels corresponding to visual text ends.
// The returned value represents a cursor/caret position without a selection.
- virtual SelectionModel LeftEndSelectionModel();
- virtual SelectionModel RightEndSelectionModel();
+ virtual SelectionModel EdgeSelectionModel(
+ VisualCursorDirection direction) = 0;
// Sets the selection model, the argument is assumed to be valid.
virtual void SetSelectionModel(const SelectionModel& model);
// Get the visual bounds containing the logical substring within |from| to
- // |to| into |bounds|. If |from| equals to |to|, |bounds| is set as empty.
- // These bounds could be visually discontinuous if the substring is split by a
- // LTR/RTL level change. These bounds are in local coordinates, but may be
- // outside the visible region if the text is longer than the textfield.
- // Subsequent text, cursor, or bounds changes may invalidate returned values.
- virtual void GetSubstringBounds(size_t from,
- size_t to,
- std::vector<Rect>* bounds) = 0;
+ // |to|. If |from| equals |to|, the result is empty. These bounds could be
+ // visually discontinuous if the substring is split by a LTR/RTL level change.
+ // These bounds are in local coordinates, but may be outside the visible
+ // region if the text is longer than the textfield. Subsequent text, cursor,
+ // or bounds changes may invalidate returned values.
+ virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to) = 0;
// 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.
@@ -277,12 +304,6 @@ class UI_EXPORT RenderText {
// Draw the text.
virtual void DrawVisualText(Canvas* canvas) = 0;
- // Get the logical index of the grapheme preceding the argument |position|.
- // If |IsCursorablePosition(position)| is true, the result will be the start
- // of the previous grapheme, if any. Otherwise, the result will be the start
- // of the grapheme containing |position|.
- size_t GetIndexOfPreviousGrapheme(size_t position);
-
// Apply composition style (underline) to composition range and selection
// style (foreground) to selection range.
void ApplyCompositionAndSelectionStyles(StyleRanges* style_ranges);
@@ -314,13 +335,6 @@ class UI_EXPORT RenderText {
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, SelectionModels);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, OriginForSkiaDrawing);
- // Return an index belonging to the |next| or previous logical grapheme.
- // If |next| is false and |IsCursorablePosition(index)| is true, the result
- // will be the start of the previous grapheme, if any. Otherwise, the result
- // will be the start of the grapheme containing |index|.
- // The return value is bounded by 0 and the text length, inclusive.
- virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) = 0;
-
// 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.
diff --git a/ui/gfx/render_text_linux.cc b/ui/gfx/render_text_linux.cc
index fe3287c..1043d88 100644
--- a/ui/gfx/render_text_linux.cc
+++ b/ui/gfx/render_text_linux.cc
@@ -9,7 +9,6 @@
#include <string>
#include <vector>
-#include "base/debug/trace_event.h"
#include "base/i18n/break_iterator.h"
#include "base/logging.h"
#include "third_party/skia/include/core/SkTypeface.h"
@@ -19,11 +18,25 @@
#include "unicode/uchar.h"
#include "unicode/ustring.h"
+namespace gfx {
+
namespace {
-// Returns whether the given Pango item is Left to Right.
-bool IsRunLTR(const PangoItem* item) {
- return (item->analysis.level & 1) == 0;
+// Returns the preceding element in a GSList (O(n)).
+GSList* GSListPrevious(GSList* head, GSList* item) {
+ GSList* prev = NULL;
+ for (GSList* cur = head; cur != item; cur = cur->next) {
+ DCHECK(cur);
+ prev = cur;
+ }
+ return prev;
+}
+
+// Returns true if the given visual cursor |direction| is logically forward
+// motion in the given Pango |item|.
+bool IsForwardMotion(VisualCursorDirection direction, const PangoItem* item) {
+ bool rtl = item->analysis.level & 1;
+ return rtl == (direction == CURSOR_LEFT);
}
// Checks whether |range| contains |index|. This is not the same as calling
@@ -39,8 +52,6 @@ bool IndexInRange(const ui::Range& range, size_t index) {
// Since caret_pos is used internally, we could save utf8 index for caret_pos
// to avoid conversion.
-namespace gfx {
-
RenderTextLinux::RenderTextLinux()
: layout_(NULL),
current_line_(NULL),
@@ -84,9 +95,9 @@ SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) {
// When the point is outside of text, return HOME/END position.
if (p.x() < 0)
- return LeftEndSelectionModel();
+ return EdgeSelectionModel(CURSOR_LEFT);
else if (p.x() > GetStringWidth())
- return RightEndSelectionModel();
+ return EdgeSelectionModel(CURSOR_RIGHT);
int caret_pos, trailing;
pango_layout_xy_to_index(layout_, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE,
@@ -133,61 +144,110 @@ Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection,
return bounds;
}
-SelectionModel RenderTextLinux::GetLeftSelectionModel(
- const SelectionModel& current,
- BreakType break_type) {
- EnsureLayout();
+// Assume caret_pos in |current| is n, 'l' represents leading in
+// caret_placement and 't' represents trailing in caret_placement. Following
+// is the calculation from (caret_pos, caret_placement) in |current| to
+// (selection_end, caret_pos, caret_placement) when moving cursor left/right by
+// one grapheme (for simplicity, assume each grapheme is one character).
+// If n is in LTR (if moving left) or RTL (if moving right) run,
+// (n, t) --> (n, n, l).
+// (n, l) --> (n-1, n-1, l) if n is inside run (not at boundary).
+// (n, l) --> goto across run case if n is at run boundary.
+// Otherwise,
+// (n, l) --> (n+1, n, t).
+// (n, t) --> (n+2, n+1, t) if n is inside run.
+// (n, t) --> goto across run case if n is at run boundary.
+// If n is at run boundary, get its visually left/right run,
+// If left/right run is LTR/RTL run,
+// (n, t) --> (left/right run's end, left/right run's end, l).
+// Otherwise,
+// (n, t) --> (left/right run's begin + 1, left/right run's begin, t).
+SelectionModel RenderTextLinux::AdjacentCharSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) {
+ size_t caret = selection.caret_pos();
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
+ GSList* run = GetRunContainingPosition(caret);
+ DCHECK(run);
- if (break_type == LINE_BREAK || text().empty())
- return LeftEndSelectionModel();
- if (break_type == CHARACTER_BREAK)
- return LeftSelectionModel(current);
- DCHECK(break_type == WORD_BREAK);
- return LeftSelectionModelByWord(current);
-}
+ PangoLayoutRun* layout_run = reinterpret_cast<PangoLayoutRun*>(run->data);
+ PangoItem* item = layout_run->item;
+ size_t run_start = Utf8IndexToUtf16Index(item->offset);
+ size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length);
-SelectionModel RenderTextLinux::GetRightSelectionModel(
- const SelectionModel& current,
- BreakType break_type) {
- EnsureLayout();
+ if (!IsForwardMotion(direction, item)) {
+ if (caret_placement == SelectionModel::TRAILING)
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ else if (caret > run_start) {
+ caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ }
+ } else {
+ if (caret_placement == SelectionModel::LEADING) {
+ size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ } else if (selection.selection_end() < run_end) {
+ caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
+ size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ }
+ }
+
+ // The character is at the edge of its run; advance to the adjacent visual
+ // run.
+ // TODO(xji): Keep a vector of runs to avoid using a singly-linked list.
+ GSList* adjacent_run = (direction == CURSOR_RIGHT) ?
+ run->next : GSListPrevious(current_line_->runs, run);
+ if (!adjacent_run)
+ return EdgeSelectionModel(direction);
- if (break_type == LINE_BREAK || text().empty())
- return RightEndSelectionModel();
- if (break_type == CHARACTER_BREAK)
- return RightSelectionModel(current);
- DCHECK(break_type == WORD_BREAK);
- return RightSelectionModelByWord(current);
+ item = reinterpret_cast<PangoLayoutRun*>(adjacent_run->data)->item;
+ return IsForwardMotion(direction, item) ?
+ FirstSelectionModelInsideRun(item) : LastSelectionModelInsideRun(item);
}
-SelectionModel RenderTextLinux::LeftEndSelectionModel() {
- if (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) {
- if (current_line_->runs) {
- PangoLayoutRun* first_visual_run =
- reinterpret_cast<PangoLayoutRun*>(current_line_->runs->data);
- PangoItem* item = first_visual_run->item;
- if (IsRunLTR(item)) {
- size_t caret = Utf8IndexToUtf16Index(item->offset);
- return SelectionModel(text().length(), caret, SelectionModel::LEADING);
- } else { // RTL.
- size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length,
- false);
- return SelectionModel(text().length(), caret, SelectionModel::TRAILING);
- }
- }
+SelectionModel RenderTextLinux::AdjacentWordSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) {
+ base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
+ bool success = iter.Init();
+ DCHECK(success);
+ if (!success)
+ return selection;
+
+ SelectionModel end = EdgeSelectionModel(direction);
+ SelectionModel cur(selection);
+ while (!cur.Equals(end)) {
+ cur = AdjacentCharSelectionModel(cur, direction);
+ size_t caret = cur.caret_pos();
+ GSList* run = GetRunContainingPosition(caret);
+ DCHECK(run);
+ PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
+ size_t cursor = cur.selection_end();
+ if (IsForwardMotion(direction, item) ?
+ iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor))
+ return cur;
}
- return SelectionModel(0, 0, SelectionModel::LEADING);
-}
-SelectionModel RenderTextLinux::RightEndSelectionModel() {
- if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT) {
- PangoLayoutRun* last_visual_run = GetLastRun();
- if (last_visual_run) {
- PangoItem* item = last_visual_run->item;
- if (IsRunLTR(item)) {
- size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length,
- false);
+ return end;
+}
+
+SelectionModel RenderTextLinux::EdgeSelectionModel(
+ VisualCursorDirection direction) {
+ if (direction == GetVisualDirectionOfLogicalEnd()) {
+ // Advance to the logical end of the text.
+ GSList* run = current_line_->runs;
+ if (direction == CURSOR_RIGHT)
+ run = g_slist_last(run);
+ if (run) {
+ PangoLayoutRun* end_run = reinterpret_cast<PangoLayoutRun*>(run->data);
+ PangoItem* item = end_run->item;
+ if (IsForwardMotion(direction, item)) {
+ size_t caret = Utf8IndexToUtf16Index(
+ Utf8IndexOfAdjacentGrapheme(item->offset + item->length,
+ CURSOR_BACKWARD));
return SelectionModel(text().length(), caret, SelectionModel::TRAILING);
- } else { // RTL.
+ } else {
size_t caret = Utf8IndexToUtf16Index(item->offset);
return SelectionModel(text().length(), caret, SelectionModel::LEADING);
}
@@ -205,22 +265,19 @@ void RenderTextLinux::SetSelectionModel(const SelectionModel& model) {
RenderText::SetSelectionModel(model);
}
-void RenderTextLinux::GetSubstringBounds(size_t from,
- size_t to,
- std::vector<Rect>* bounds) {
+std::vector<Rect> RenderTextLinux::GetSubstringBounds(size_t from, size_t to) {
DCHECK(from <= text().length());
DCHECK(to <= text().length());
- bounds->clear();
if (from == to)
- return;
+ return std::vector<Rect>();
EnsureLayout();
if (from == GetSelectionStart() && to == GetCursorPosition())
- GetSelectionBounds(bounds);
+ return GetSelectionBounds();
else
- CalculateSubstringBounds(from, to, bounds);
+ return CalculateSubstringBounds(from, to);
}
bool RenderTextLinux::IsCursorablePosition(size_t position) {
@@ -300,7 +357,6 @@ void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) {
}
void RenderTextLinux::DrawVisualText(Canvas* canvas) {
- TRACE_EVENT0("gfx", "RenderTextLinux::DrawVisualText");
DCHECK(layout_);
Point offset(GetOriginForSkiaDrawing());
@@ -336,7 +392,7 @@ void RenderTextLinux::DrawVisualText(Canvas* canvas) {
size_t run_start = run->item->offset;
size_t first_glyph_byte_index = run_start + run->glyphs->log_clusters[0];
- size_t style_increment = IsRunLTR(run->item) ? 1 : -1;
+ size_t style_increment = IsForwardMotion(CURSOR_RIGHT, run->item) ? 1 : -1;
// Find the initial style for this run.
// TODO(asvitkine): Can we avoid looping here, e.g. by caching this per run?
@@ -351,14 +407,13 @@ void RenderTextLinux::DrawVisualText(Canvas* canvas) {
PangoFontDescription* native_font =
pango_font_describe(run->item->analysis.font);
- {
- TRACE_EVENT0("gfx", "RenderTextLinux::DrawVisualText SetFont");
- const char* family_name = pango_font_description_get_family(native_font);
- SkAutoTUnref<SkTypeface> typeface(
- SkTypeface::CreateFromName(family_name, SkTypeface::kNormal));
- renderer.SetTypeface(typeface.get());
- renderer.SetTextSize(GetPangoFontSizeInPixels(native_font));
- }
+
+ const char* family_name = pango_font_description_get_family(native_font);
+ SkAutoTUnref<SkTypeface> typeface(
+ SkTypeface::CreateFromName(family_name, SkTypeface::kNormal));
+ renderer.SetTypeface(typeface.get());
+ renderer.SetTextSize(GetPangoFontSizeInPixels(native_font));
+
pango_font_description_free(native_font);
SkScalar glyph_x = x;
@@ -418,11 +473,14 @@ void RenderTextLinux::DrawVisualText(Canvas* canvas) {
}
}
-size_t RenderTextLinux::IndexOfAdjacentGrapheme(size_t index, bool next) {
+size_t RenderTextLinux::IndexOfAdjacentGrapheme(
+ size_t index,
+ LogicalCursorDirection direction) {
if (index > text().length())
return text().length();
EnsureLayout();
- return Utf16IndexOfAdjacentGrapheme(Utf16IndexToUtf8Index(index), next);
+ return Utf8IndexToUtf16Index(
+ Utf8IndexOfAdjacentGrapheme(Utf16IndexToUtf8Index(index), direction));
}
GSList* RenderTextLinux::GetRunContainingPosition(size_t position) const {
@@ -441,18 +499,18 @@ GSList* RenderTextLinux::GetRunContainingPosition(size_t position) const {
size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme(
size_t utf8_index_of_current_grapheme,
- bool next) const {
+ LogicalCursorDirection direction) 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 (direction == CURSOR_BACKWARD) {
if (char_offset > 0) {
do {
--char_offset;
} while (char_offset > 0 && !log_attrs_[char_offset].is_cursor_position);
}
- } else {
+ } else { // direction == CURSOR_FORWARD
if (char_offset < num_log_attrs_ - 1) {
do {
++char_offset;
@@ -465,202 +523,21 @@ size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme(
return static_cast<size_t>(ch - layout_text_);
}
-size_t RenderTextLinux::Utf16IndexOfAdjacentGrapheme(
- size_t utf8_index_of_current_grapheme,
- bool next) const {
- size_t utf8_index = Utf8IndexOfAdjacentGrapheme(
- utf8_index_of_current_grapheme, next);
- 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 = Utf8IndexToUtf16Index(
+ Utf8IndexOfAdjacentGrapheme(item->offset, CURSOR_FORWARD));
return SelectionModel(cursor, caret, SelectionModel::TRAILING);
}
SelectionModel RenderTextLinux::LastSelectionModelInsideRun(
const PangoItem* item) const {
- size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length,
- false);
+ size_t caret = Utf8IndexToUtf16Index(Utf8IndexOfAdjacentGrapheme(
+ item->offset + item->length, CURSOR_BACKWARD));
return SelectionModel(caret, caret, SelectionModel::LEADING);
}
-// Assume caret_pos in |current| is n, 'l' represents leading in
-// caret_placement and 't' represents trailing in caret_placement. Following
-// is the calculation from (caret_pos, caret_placement) in |current| to
-// (selection_end, caret_pos, caret_placement) when moving cursor left by
-// one grapheme (for simplicity, assume each grapheme is one character).
-// If n is in LTR run,
-// (n, t) ---> (n, n, l).
-// (n, l) ---> (n-1, n-1, l) if n is inside run (not at boundary).
-// (n, l) ---> goto across run case if n is at run boundary.
-// If n is in RTL run,
-// (n, l) --> (n+1, n, t).
-// (n, t) --> (n+2, n+1, t) if n is inside run.
-// (n, t) --> goto across run case if n is at run boundary.
-// If n is at run boundary, get its visually left run,
-// If left run is LTR run,
-// (n, t) --> (left run's end, left run's end, l).
-// If left run is RTL run,
-// (n, t) --> (left run's begin + 1, left run's begin, t).
-SelectionModel RenderTextLinux::LeftSelectionModel(
- const SelectionModel& selection) {
- size_t caret = selection.caret_pos();
- SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
- GSList* run = GetRunContainingPosition(caret);
- DCHECK(run);
-
- PangoLayoutRun* layout_run = reinterpret_cast<PangoLayoutRun*>(run->data);
- PangoItem* item = layout_run->item;
- size_t run_start = Utf8IndexToUtf16Index(item->offset);
- size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length);
-
- if (IsRunLTR(item)) {
- if (caret_placement == SelectionModel::TRAILING)
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- else if (caret > run_start) {
- caret = GetIndexOfPreviousGrapheme(caret);
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- }
- } else { // RTL run.
- if (caret_placement == SelectionModel::LEADING) {
- size_t cursor = GetIndexOfNextGrapheme(caret);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- } else if (selection.selection_end() < run_end) {
- caret = GetIndexOfNextGrapheme(caret);
- size_t cursor = GetIndexOfNextGrapheme(caret);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- }
- }
-
- // The character is at the begin of its run; advance to the previous visual
- // run.
- PangoLayoutRun* prev_run = GetPreviousRun(layout_run);
- if (!prev_run)
- return LeftEndSelectionModel();
-
- item = prev_run->item;
- return IsRunLTR(item) ? LastSelectionModelInsideRun(item) :
- FirstSelectionModelInsideRun(item);
-}
-
-// Assume caret_pos in |current| is n, 'l' represents leading in
-// caret_placement and 't' represents trailing in caret_placement. Following
-// is the calculation from (caret_pos, caret_placement) in |current| to
-// (selection_end, caret_pos, caret_placement) when moving cursor right by
-// one grapheme (for simplicity, assume each grapheme is one character).
-// If n is in LTR run,
-// (n, l) ---> (n+1, n, t).
-// (n, t) ---> (n+2, n+1, t) if n is inside run (not at boundary).
-// (n, t) ---> goto across run case if n is at run boundary.
-// If n is in RTL run,
-// (n, t) --> (n, n, l).
-// (n, l) --> (n-1, n-1, l) if n is inside run.
-// (n, l) --> goto across run case if n is at run boundary.
-// If n is at run boundary, get its visually right run,
-// If right run is LTR run,
-// (n, t) --> (right run's begin + 1, right run's begin, t).
-// If right run is RTL run,
-// (n, t) --> (right run's end, right run's end, l).
-SelectionModel RenderTextLinux::RightSelectionModel(
- const SelectionModel& selection) {
- size_t caret = selection.caret_pos();
- SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
- GSList* run = GetRunContainingPosition(caret);
- DCHECK(run);
-
- PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
- size_t run_start = Utf8IndexToUtf16Index(item->offset);
- size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length);
-
- if (IsRunLTR(item)) {
- if (caret_placement == SelectionModel::LEADING) {
- size_t cursor = GetIndexOfNextGrapheme(caret);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- } else if (selection.selection_end() < run_end) {
- caret = GetIndexOfNextGrapheme(caret);
- size_t cursor = GetIndexOfNextGrapheme(caret);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- }
- } else { // RTL run.
- if (caret_placement == SelectionModel::TRAILING)
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- else if (caret > run_start) {
- caret = GetIndexOfPreviousGrapheme(caret);
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- }
- }
-
- // The character is at the end of its run; advance to the next visual run.
- GSList* next_run = run->next;
- if (!next_run)
- return RightEndSelectionModel();
-
- item = reinterpret_cast<PangoLayoutRun*>(next_run->data)->item;
- return IsRunLTR(item) ? FirstSelectionModelInsideRun(item) :
- LastSelectionModelInsideRun(item);
-}
-
-SelectionModel RenderTextLinux::LeftSelectionModelByWord(
- const SelectionModel& selection) {
- base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
- bool success = iter.Init();
- DCHECK(success);
- if (!success)
- return selection;
-
- SelectionModel left_end = LeftEndSelectionModel();
- SelectionModel left(selection);
- while (!left.Equals(left_end)) {
- left = LeftSelectionModel(left);
- size_t caret = left.caret_pos();
- GSList* run = GetRunContainingPosition(caret);
- DCHECK(run);
- PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
- size_t cursor = left.selection_end();
- if (IsRunLTR(item)) {
- if (iter.IsStartOfWord(cursor))
- return left;
- } else { // RTL run.
- if (iter.IsEndOfWord(cursor))
- return left;
- }
- }
-
- return left_end;
-}
-
-SelectionModel RenderTextLinux::RightSelectionModelByWord(
- const SelectionModel& selection) {
- base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
- bool success = iter.Init();
- DCHECK(success);
- if (!success)
- return selection;
-
- SelectionModel right_end = RightEndSelectionModel();
- SelectionModel right(selection);
- while (!right.Equals(right_end)) {
- right = RightSelectionModel(right);
- size_t caret = right.caret_pos();
- GSList* run = GetRunContainingPosition(caret);
- DCHECK(run);
- PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
- size_t cursor = right.selection_end();
- if (IsRunLTR(item)) {
- if (iter.IsEndOfWord(cursor))
- return right;
- } else { // RTL run.
- if (iter.IsStartOfWord(cursor))
- return right;
- }
- }
-
- return right_end;
-}
-
void RenderTextLinux::ResetLayout() {
// set_cached_bounds_and_offset_valid(false) is done in RenderText for every
// operation that triggers ResetLayout().
@@ -683,27 +560,6 @@ void RenderTextLinux::ResetLayout() {
layout_text_len_ = 0;
}
-// TODO(xji): Keep a vector of runs to avoid using a singly-linked list.
-PangoLayoutRun* RenderTextLinux::GetPreviousRun(PangoLayoutRun* run) const {
- GSList* current = current_line_->runs;
- GSList* prev = NULL;
- while (current) {
- if (reinterpret_cast<PangoLayoutRun*>(current->data) == run)
- return prev ? reinterpret_cast<PangoLayoutRun*>(prev->data) : NULL;
- prev = current;
- current = current->next;
- }
- return NULL;
-}
-
-PangoLayoutRun* RenderTextLinux::GetLastRun() const {
- GSList* current = current_line_->runs;
- while (current && current->next) {
- current = current->next;
- }
- return current ? reinterpret_cast<PangoLayoutRun*>(current->data) : NULL;
-}
-
size_t RenderTextLinux::Utf16IndexToUtf8Index(size_t index) const {
int32_t utf8_index = 0;
UErrorCode ec = U_ZERO_ERROR;
@@ -729,9 +585,8 @@ size_t RenderTextLinux::Utf8IndexToUtf16Index(size_t index) const {
return utf16_index;
}
-void RenderTextLinux::CalculateSubstringBounds(size_t from,
- size_t to,
- std::vector<Rect>* bounds) {
+std::vector<Rect> RenderTextLinux::CalculateSubstringBounds(size_t from,
+ size_t to) {
int* ranges;
int n_ranges;
size_t from_in_utf8 = Utf16IndexToUtf8Index(from);
@@ -748,21 +603,23 @@ void RenderTextLinux::CalculateSubstringBounds(size_t from,
int y = (display_rect().height() - height) / 2;
+ std::vector<Rect> bounds;
for (int i = 0; i < n_ranges; ++i) {
int x = PANGO_PIXELS(ranges[2 * i]);
int width = PANGO_PIXELS(ranges[2 * i + 1]) - x;
Rect rect(x, y, width, height);
rect.set_origin(ToViewPoint(rect.origin()));
- bounds->push_back(rect);
+ bounds.push_back(rect);
}
g_free(ranges);
+ return bounds;
}
-void RenderTextLinux::GetSelectionBounds(std::vector<Rect>* bounds) {
+std::vector<Rect> RenderTextLinux::GetSelectionBounds() {
if (selection_visual_bounds_.empty())
- CalculateSubstringBounds(GetSelectionStart(), GetCursorPosition(),
- &selection_visual_bounds_);
- *bounds = selection_visual_bounds_;
+ selection_visual_bounds_ =
+ CalculateSubstringBounds(GetSelectionStart(), GetCursorPosition());
+ return selection_visual_bounds_;
}
} // namespace gfx
diff --git a/ui/gfx/render_text_linux.h b/ui/gfx/render_text_linux.h
index 8b81a9a..79c87b0 100644
--- a/ui/gfx/render_text_linux.h
+++ b/ui/gfx/render_text_linux.h
@@ -28,15 +28,15 @@ class RenderTextLinux : public RenderText {
protected:
// Overridden from RenderText:
- virtual SelectionModel GetLeftSelectionModel(const SelectionModel& current,
- BreakType break_type) OVERRIDE;
- virtual SelectionModel GetRightSelectionModel(const SelectionModel& current,
- BreakType break_type) OVERRIDE;
- virtual SelectionModel LeftEndSelectionModel() OVERRIDE;
- virtual SelectionModel RightEndSelectionModel() OVERRIDE;
- virtual void GetSubstringBounds(size_t from,
- size_t to,
- std::vector<Rect>* bounds) OVERRIDE;
+ virtual SelectionModel AdjacentCharSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) OVERRIDE;
+ virtual SelectionModel AdjacentWordSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) OVERRIDE;
+ virtual SelectionModel EdgeSelectionModel(
+ VisualCursorDirection direction) OVERRIDE;
+ virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to) OVERRIDE;
virtual void SetSelectionModel(const SelectionModel& model) OVERRIDE;
virtual bool IsCursorablePosition(size_t position) OVERRIDE;
virtual void UpdateLayout() OVERRIDE;
@@ -44,19 +44,18 @@ class RenderTextLinux : public RenderText {
virtual void DrawVisualText(Canvas* canvas) OVERRIDE;
private:
- virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) OVERRIDE;
+ virtual size_t IndexOfAdjacentGrapheme(
+ size_t index,
+ LogicalCursorDirection direction) OVERRIDE;
// 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.
+ // Given |utf8_index_of_current_grapheme|, returns the UTF-8 index of the
+ // |next| or previous grapheme in logical order. Returns 0 if there is no
+ // previous grapheme, or the |text_| length if there is no next grapheme.
size_t Utf8IndexOfAdjacentGrapheme(size_t utf8_index_of_current_grapheme,
- bool next) const;
- size_t Utf16IndexOfAdjacentGrapheme(size_t utf8_index_of_current_grapheme,
- bool next) const;
+ LogicalCursorDirection direction) const;
// Given a |run|, returns the SelectionModel that contains the logical first
// or last caret position inside (not at a boundary of) the run.
@@ -64,42 +63,27 @@ class RenderTextLinux : public RenderText {
SelectionModel FirstSelectionModelInsideRun(const PangoItem* run) const;
SelectionModel LastSelectionModelInsideRun(const PangoItem* run) const;
- // Get the selection model visually left or right of |current| by one
- // grapheme.
- // The returned value represents a cursor/caret position without a selection.
- SelectionModel LeftSelectionModel(const SelectionModel& current);
- SelectionModel RightSelectionModel(const SelectionModel& current);
-
- // Get the selection model visually left or right of |current| by one word.
- // The returned value represents a cursor/caret position without a selection.
- SelectionModel LeftSelectionModelByWord(const SelectionModel& current);
- SelectionModel RightSelectionModelByWord(const SelectionModel& current);
-
// Unref |layout_| and |pango_line_|. Set them to NULL.
void ResetLayout();
// Setup pango attribute: foreground, background, font, strike.
void SetupPangoAttributes(PangoLayout* layout);
- // Returns |run|'s visually previous run.
- // The complexity is O(n) since it is a single-linked list.
- PangoLayoutRun* GetPreviousRun(PangoLayoutRun* run) const;
-
- // Returns the last run in |current_line_|.
- // The complexity is O(n) since it is a single-linked list.
- PangoLayoutRun* GetLastRun() const;
+ // Append one pango attribute |pango_attr| into pango attribute list |attrs|.
+ void AppendPangoAttribute(size_t start,
+ size_t end,
+ PangoAttribute* pango_attr,
+ PangoAttrList* attrs);
size_t Utf16IndexToUtf8Index(size_t index) const;
size_t Utf8IndexToUtf16Index(size_t index) const;
// Calculate the visual bounds containing the logical substring within |from|
- // to |to| into |bounds|.
- void CalculateSubstringBounds(size_t from,
- size_t to,
- std::vector<Rect>* bounds);
+ // to |to|.
+ std::vector<Rect> CalculateSubstringBounds(size_t from, size_t to);
- // Save the visual bounds of logical selection into |bounds|.
- void GetSelectionBounds(std::vector<Rect>* bounds);
+ // Get the visual bounds of the logical selection.
+ std::vector<Rect> GetSelectionBounds();
// Pango Layout.
PangoLayout* layout_;
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 63afca1..7b7bea44 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -288,16 +288,16 @@ void RunMoveCursorLeftRightTest(RenderText* render_text,
SelectionModel sel = expected[i];
EXPECT_TRUE(render_text->selection_model().Equals(sel));
if (move_right)
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
else
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
}
SelectionModel sel = expected[expected.size() - 1];
if (move_right)
- render_text->MoveCursorRight(LINE_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
else
- render_text->MoveCursorLeft(LINE_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
EXPECT_TRUE(render_text->selection_model().Equals(sel));
}
@@ -380,7 +380,7 @@ 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);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
std::vector<SelectionModel> expected;
#if defined(OS_LINUX)
@@ -424,7 +424,7 @@ 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);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
std::vector<SelectionModel> expected;
#if defined(OS_LINUX)
expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
@@ -479,7 +479,7 @@ 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);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
std::vector<SelectionModel> expected;
#if defined(OS_LINUX)
expected.push_back(SelectionModel(0, 0, SelectionModel::LEADING));
@@ -526,22 +526,22 @@ TEST_F(RenderTextTest, MoveCursorLeftRight_ComplexScript) {
render_text->SetText(WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915"));
EXPECT_EQ(0U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(2U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(2U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(0U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(0U, render_text->GetCursorPosition());
}
#endif
@@ -594,11 +594,13 @@ TEST_F(RenderTextTest, GraphemePositions) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
render_text->SetText(cases[i].text);
- size_t next = render_text->GetIndexOfNextGrapheme(cases[i].index);
+ size_t next = render_text->IndexOfAdjacentGrapheme(cases[i].index,
+ CURSOR_FORWARD);
EXPECT_EQ(cases[i].expected_next, next);
EXPECT_TRUE(render_text->IsCursorablePosition(next));
- size_t previous = render_text->GetIndexOfPreviousGrapheme(cases[i].index);
+ size_t previous = render_text->IndexOfAdjacentGrapheme(cases[i].index,
+ CURSOR_BACKWARD);
EXPECT_EQ(cases[i].expected_previous, previous);
EXPECT_TRUE(render_text->IsCursorablePosition(previous));
}
@@ -647,12 +649,12 @@ TEST_F(RenderTextTest, SelectionModels) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
render_text->SetText(cases[i].text);
- SelectionModel model = render_text->LeftEndSelectionModel();
+ SelectionModel model = render_text->EdgeSelectionModel(CURSOR_LEFT);
EXPECT_EQ(cases[i].expected_left_end_caret, model.caret_pos());
EXPECT_TRUE(render_text->IsCursorablePosition(model.caret_pos()));
EXPECT_EQ(cases[i].expected_left_end_placement, model.caret_placement());
- model = render_text->RightEndSelectionModel();
+ model = render_text->EdgeSelectionModel(CURSOR_RIGHT);
EXPECT_EQ(cases[i].expected_right_end_caret, model.caret_pos());
EXPECT_TRUE(render_text->IsCursorablePosition(model.caret_pos()));
EXPECT_EQ(cases[i].expected_right_end_placement, model.caret_placement());
@@ -663,43 +665,43 @@ 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);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(6U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(6U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, true);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, true);
EXPECT_EQ(6U, render_text->GetSelectionStart());
EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, true);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, true);
EXPECT_EQ(6U, render_text->GetSelectionStart());
EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(6U, render_text->GetCursorPosition());
// Right arrow on select ranging (4, 6).
- render_text->MoveCursorLeft(LINE_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(0U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(1U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(2U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(3U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(4U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, true);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, true);
EXPECT_EQ(4U, render_text->GetSelectionStart());
EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorLeft(CHARACTER_BREAK, true);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, true);
EXPECT_EQ(4U, render_text->GetSelectionStart());
EXPECT_EQ(6U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(4U, render_text->GetCursorPosition());
}
@@ -710,13 +712,13 @@ void MoveLeftRightByWordVerifier(RenderText* render_text,
render_text->SetText(WideToUTF16(str));
// Test moving by word from left ro right.
- render_text->MoveCursorLeft(LINE_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
bool first_word = true;
while (true) {
// First, test moving by word from a word break position, such as from
// "|abc def" to "abc| def".
SelectionModel start = render_text->selection_model();
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
SelectionModel end = render_text->selection_model();
if (end.Equals(start)) // reach the end.
break;
@@ -726,7 +728,7 @@ void MoveLeftRightByWordVerifier(RenderText* render_text,
first_word = false;
render_text->MoveCursorTo(start);
for (int j = 0; j < num_of_character_moves; ++j)
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_TRUE(render_text->selection_model().Equals(end));
// Then, test moving by word from positions inside the word, such as from
@@ -734,18 +736,18 @@ void MoveLeftRightByWordVerifier(RenderText* render_text,
for (int j = 1; j < num_of_character_moves; ++j) {
render_text->MoveCursorTo(start);
for (int k = 0; k < j; ++k)
- render_text->MoveCursorRight(CHARACTER_BREAK, false);
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
EXPECT_TRUE(render_text->selection_model().Equals(end));
}
}
// Test moving by word from right to left.
- render_text->MoveCursorRight(LINE_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
first_word = true;
while (true) {
SelectionModel start = render_text->selection_model();
- render_text->MoveCursorLeft(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
SelectionModel end = render_text->selection_model();
if (end.Equals(start)) // reach the end.
break;
@@ -754,14 +756,14 @@ void MoveLeftRightByWordVerifier(RenderText* render_text,
first_word = false;
render_text->MoveCursorTo(start);
for (int j = 0; j < num_of_character_moves; ++j)
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
EXPECT_TRUE(render_text->selection_model().Equals(end));
for (int j = 1; j < num_of_character_moves; ++j) {
render_text->MoveCursorTo(start);
for (int k = 0; k < j; ++k)
- render_text->MoveCursorLeft(CHARACTER_BREAK, false);
- render_text->MoveCursorLeft(WORD_BREAK, false);
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
EXPECT_TRUE(render_text->selection_model().Equals(end));
}
}
@@ -810,21 +812,21 @@ TEST_F(RenderTextTest, MoveLeftRightByWordInBidiText_TestEndOfText) {
// But since end of text is always treated as a word break, it returns
// position "ab|C".
// TODO(xji): Need to make it work as expected.
- render_text->MoveCursorRight(LINE_BREAK, false);
- render_text->MoveCursorLeft(WORD_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
// EXPECT_TRUE(render_text->selection_model().Equals(SelectionModel(0)));
// Moving the cursor by word from "|abC" to the right returns "abC|".
- render_text->MoveCursorLeft(LINE_BREAK, false);
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
EXPECT_TRUE(render_text->selection_model().Equals(
SelectionModel(3, 2, SelectionModel::LEADING)));
render_text->SetText(WideToUTF16(L"\x05E1\x05E2"L"a"));
// For logical text "BCa", moving the cursor by word from "aCB|" to the left
// returns "|aCB".
- render_text->MoveCursorRight(LINE_BREAK, false);
- render_text->MoveCursorLeft(WORD_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
EXPECT_TRUE(render_text->selection_model().Equals(
SelectionModel(3, 2, SelectionModel::LEADING)));
@@ -832,8 +834,8 @@ TEST_F(RenderTextTest, MoveLeftRightByWordInBidiText_TestEndOfText) {
// But since end of text is always treated as a word break, it returns
// position "a|CB".
// TODO(xji): Need to make it work as expected.
- render_text->MoveCursorLeft(LINE_BREAK, false);
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
// EXPECT_TRUE(render_text->selection_model().Equals(SelectionModel(0)));
}
@@ -841,28 +843,28 @@ TEST_F(RenderTextTest, MoveLeftRightByWordInTextWithMultiSpaces) {
scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
render_text->SetText(WideToUTF16(L"abc def"));
render_text->MoveCursorTo(SelectionModel(5));
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(11U, render_text->GetCursorPosition());
render_text->MoveCursorTo(SelectionModel(5));
- render_text->MoveCursorLeft(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(0U, render_text->GetCursorPosition());
}
TEST_F(RenderTextTest, MoveLeftRightByWordInChineseText) {
scoped_ptr<RenderText> render_text(RenderText::CreateRenderText());
render_text->SetText(WideToUTF16(L"\x6211\x4EEC\x53BB\x516C\x56ED\x73A9"));
- render_text->MoveCursorLeft(LINE_BREAK, false);
+ render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
EXPECT_EQ(0U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(2U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(3U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(5U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(6U, render_text->GetCursorPosition());
- render_text->MoveCursorRight(WORD_BREAK, false);
+ render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(6U, render_text->GetCursorPosition());
}
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index 459a29d..9896fb2 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "base/i18n/break_iterator.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/string_util.h"
@@ -129,13 +130,21 @@ RenderTextWin::RenderTextWin()
DCHECK(SUCCEEDED(hr));
script_control_.fMergeNeutralItems = true;
- MoveCursorTo(LeftEndSelectionModel());
+ MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT));
}
RenderTextWin::~RenderTextWin() {
STLDeleteContainerPointers(runs_.begin(), runs_.end());
}
+base::i18n::TextDirection RenderTextWin::GetTextDirection() {
+ // TODO(benrg): Code moved from RenderText::GetTextDirection. Needs to be
+ // replaced by a correct Windows implementation.
+ if (base::i18n::IsRTL())
+ return base::i18n::RIGHT_TO_LEFT;
+ return base::i18n::LEFT_TO_RIGHT;
+}
+
int RenderTextWin::GetStringWidth() {
EnsureLayout();
return string_width_;
@@ -150,7 +159,7 @@ SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
Point p(ToTextPoint(point));
size_t run_index = GetRunContainingPoint(p);
if (run_index == runs_.size())
- return (p.x() < 0) ? LeftEndSelectionModel() : RightEndSelectionModel();
+ return EdgeSelectionModel((p.x() < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
internal::TextRun* run = runs_[run_index];
int position = 0, trailing = 0;
@@ -226,82 +235,123 @@ Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection,
return rect;
}
-SelectionModel RenderTextWin::GetLeftSelectionModel(
- const SelectionModel& selection,
- BreakType break_type) {
- EnsureLayout();
-
- if (break_type == LINE_BREAK || text().empty())
- return LeftEndSelectionModel();
- if (break_type == CHARACTER_BREAK)
- return LeftSelectionModel(selection);
- // TODO(msw): Implement word breaking.
- return RenderText::GetLeftSelectionModel(selection, break_type);
-}
+SelectionModel RenderTextWin::AdjacentCharSelectionModel(
+ const SelectionModel& selection, VisualCursorDirection direction) {
+ DCHECK(!needs_layout_);
+ size_t caret = selection.caret_pos();
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
+ size_t run_index = GetRunContainingPosition(caret);
+ DCHECK(run_index < runs_.size());
+ internal::TextRun* run = runs_[run_index];
-SelectionModel RenderTextWin::GetRightSelectionModel(
- const SelectionModel& selection,
- BreakType break_type) {
- EnsureLayout();
+ bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT);
+ if (forward_motion) {
+ if (caret_placement == SelectionModel::LEADING) {
+ size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ } else if (selection.selection_end() < run->range.end()) {
+ caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
+ size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ }
+ } else {
+ if (caret_placement == SelectionModel::TRAILING)
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ else if (caret > run->range.start()) {
+ caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ }
+ }
- if (break_type == LINE_BREAK || text().empty())
- return RightEndSelectionModel();
- if (break_type == CHARACTER_BREAK)
- return RightSelectionModel(selection);
- // TODO(msw): Implement word breaking.
- return RenderText::GetRightSelectionModel(selection, break_type);
+ // The character is at the beginning/end of its run; go to the previous/next
+ // visual run.
+ size_t visual_index = logical_to_visual_[run_index];
+ if (visual_index == (direction == CURSOR_LEFT ? 0 : runs_.size() - 1))
+ return EdgeSelectionModel(direction);
+ internal::TextRun* adjacent = runs_[visual_to_logical_[
+ direction == CURSOR_LEFT ? visual_index - 1 : visual_index + 1]];
+ forward_motion = adjacent->script_analysis.fRTL == (direction == CURSOR_LEFT);
+ return forward_motion ? FirstSelectionModelInsideRun(adjacent) :
+ LastSelectionModelInsideRun(adjacent);
}
-SelectionModel RenderTextWin::LeftEndSelectionModel() {
- if (text().empty())
- return SelectionModel(0, 0, SelectionModel::LEADING);
-
- EnsureLayout();
- size_t cursor = base::i18n::IsRTL() ? text().length() : 0;
- internal::TextRun* run = runs_[visual_to_logical_[0]];
- size_t caret;
- SelectionModel::CaretPlacement placement;
- if (run->script_analysis.fRTL) {
- caret = IndexOfAdjacentGrapheme(run->range.end(), false);
- placement = SelectionModel::TRAILING;
- } else {
- caret = run->range.start();
- placement = SelectionModel::LEADING;
+// TODO(msw): Implement word breaking for Windows.
+SelectionModel RenderTextWin::AdjacentWordSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) {
+ base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
+ bool success = iter.Init();
+ DCHECK(success);
+ if (!success)
+ return selection;
+
+ size_t pos;
+ if (direction == CURSOR_RIGHT) {
+ pos = std::min(selection.selection_end() + 1, text().length());
+ while (iter.Advance()) {
+ pos = iter.pos();
+ if (iter.IsWord() && pos > selection.selection_end())
+ break;
+ }
+ } else { // direction == CURSOR_LEFT
+ // Notes: We always iterate words from the beginning.
+ // This is probably fast enough for our usage, but we may
+ // want to modify WordIterator so that it can start from the
+ // middle of string and advance backwards.
+ pos = std::max<int>(selection.selection_end() - 1, 0);
+ while (iter.Advance()) {
+ if (iter.IsWord()) {
+ size_t begin = iter.pos() - iter.GetString().length();
+ if (begin == selection.selection_end()) {
+ // The cursor is at the beginning of a word.
+ // Move to previous word.
+ break;
+ } else if (iter.pos() >= selection.selection_end()) {
+ // The cursor is in the middle or at the end of a word.
+ // Move to the top of current word.
+ pos = begin;
+ break;
+ } else {
+ pos = iter.pos() - iter.GetString().length();
+ }
+ }
+ }
}
- return SelectionModel(cursor, caret, placement);
+ return SelectionModel(pos, pos, SelectionModel::LEADING);
}
-SelectionModel RenderTextWin::RightEndSelectionModel() {
+SelectionModel RenderTextWin::EdgeSelectionModel(
+ VisualCursorDirection direction) {
if (text().empty())
return SelectionModel(0, 0, SelectionModel::LEADING);
EnsureLayout();
- size_t cursor = base::i18n::IsRTL() ? 0 : text().length();
- internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]];
+ size_t cursor = (direction == GetVisualDirectionOfLogicalEnd()) ?
+ text().length() : 0;
+ internal::TextRun* run = runs_[
+ visual_to_logical_[direction == CURSOR_RIGHT ? runs_.size() - 1 : 0]];
size_t caret;
SelectionModel::CaretPlacement placement;
- if (run->script_analysis.fRTL) {
+ if (run->script_analysis.fRTL == (direction == CURSOR_RIGHT)) {
caret = run->range.start();
placement = SelectionModel::LEADING;
} else {
- caret = IndexOfAdjacentGrapheme(run->range.end(), false);
+ caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD);
placement = SelectionModel::TRAILING;
}
return SelectionModel(cursor, caret, placement);
}
-void RenderTextWin::GetSubstringBounds(size_t from,
- size_t to,
- std::vector<Rect>* bounds) {
+std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
DCHECK(!needs_layout_);
ui::Range range(from, to);
DCHECK(ui::Range(0, text().length()).Contains(range));
Point display_offset(GetUpdatedDisplayOffset());
HRESULT hr = 0;
- bounds->clear();
+ std::vector<Rect> bounds;
if (from == to)
- return;
+ return bounds;
// Add a Rect for each run/selection intersection.
// TODO(msw): The bounds should probably not always be leading the range ends.
@@ -340,13 +390,14 @@ void RenderTextWin::GetSubstringBounds(size_t from,
rect.Offset(0, (display_rect().height() - rect.height()) / 2);
rect.set_origin(ToViewPoint(rect.origin()));
// Union this with the last rect if they're adjacent.
- if (!bounds->empty() && rect.SharesEdgeWith(bounds->back())) {
- rect = rect.Union(bounds->back());
- bounds->pop_back();
+ if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) {
+ rect = rect.Union(bounds.back());
+ bounds.pop_back();
}
- bounds->push_back(rect);
+ bounds.push_back(rect);
}
}
+ return bounds;
}
void RenderTextWin::SetSelectionModel(const SelectionModel& model) {
@@ -428,14 +479,16 @@ void RenderTextWin::DrawVisualText(Canvas* canvas) {
}
}
-size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) {
+size_t RenderTextWin::IndexOfAdjacentGrapheme(
+ size_t index,
+ LogicalCursorDirection direction) {
EnsureLayout();
if (text().empty())
return 0;
if (index >= text().length()) {
- if (next || index > text().length()) {
+ if (direction == CURSOR_FORWARD || index > text().length()) {
return text().length();
} else {
// The requested |index| is at the end of the text. Use the index of the
@@ -452,7 +505,7 @@ size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) {
size_t start = run->range.start();
size_t ch = index - start;
- if (!next) {
+ if (direction == CURSOR_BACKWARD) {
// If |ch| is the start of the run, use the preceding run, if any.
if (ch == 0) {
if (run_index == 0)
@@ -467,7 +520,7 @@ size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) {
do {
ch--;
} while (ch > 0 && run->logical_clusters[ch - 1] == cluster);
- } else {
+ } else { // direction == CURSOR_FORWARD
WORD cluster = run->logical_clusters[ch];
while (ch < run->range.length() && run->logical_clusters[ch] == cluster)
ch++;
@@ -668,90 +721,16 @@ size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
internal::TextRun* run) {
size_t caret = run->range.start();
- size_t cursor = IndexOfAdjacentGrapheme(caret, true);
+ size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
return SelectionModel(cursor, caret, SelectionModel::TRAILING);
}
SelectionModel RenderTextWin::LastSelectionModelInsideRun(
internal::TextRun* run) {
- size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false);
+ size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD);
return SelectionModel(caret, caret, SelectionModel::LEADING);
}
-SelectionModel RenderTextWin::LeftSelectionModel(
- const SelectionModel& selection) {
- DCHECK(!needs_layout_);
- size_t caret = selection.caret_pos();
- SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
- size_t run_index = GetRunContainingPosition(caret);
- DCHECK(run_index < runs_.size());
- internal::TextRun* run = runs_[run_index];
-
- // If the caret's associated character is in a LTR run.
- if (!run->script_analysis.fRTL) {
- if (caret_placement == SelectionModel::TRAILING)
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- else if (caret > run->range.start()) {
- caret = IndexOfAdjacentGrapheme(caret, false);
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- }
- } else { // The caret's associated character is in a RTL run.
- if (caret_placement == SelectionModel::LEADING) {
- size_t cursor = IndexOfAdjacentGrapheme(caret, true);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- } else if (selection.selection_end() < run->range.end()) {
- caret = IndexOfAdjacentGrapheme(caret, true);
- size_t cursor = IndexOfAdjacentGrapheme(caret, true);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- }
- }
-
- // The character is at the begin of its run; go to the previous visual run.
- size_t visual_index = logical_to_visual_[run_index];
- if (visual_index == 0)
- return LeftEndSelectionModel();
- internal::TextRun* prev = runs_[visual_to_logical_[visual_index - 1]];
- return prev->script_analysis.fRTL ? FirstSelectionModelInsideRun(prev) :
- LastSelectionModelInsideRun(prev);
-}
-
-SelectionModel RenderTextWin::RightSelectionModel(
- const SelectionModel& selection) {
- DCHECK(!needs_layout_);
- size_t caret = selection.caret_pos();
- SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
- size_t run_index = GetRunContainingPosition(caret);
- DCHECK(run_index < runs_.size());
- internal::TextRun* run = runs_[run_index];
-
- // If the caret's associated character is in a LTR run.
- if (!run->script_analysis.fRTL) {
- if (caret_placement == SelectionModel::LEADING) {
- size_t cursor = IndexOfAdjacentGrapheme(caret, true);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- } else if (selection.selection_end() < run->range.end()) {
- caret = IndexOfAdjacentGrapheme(caret, true);
- size_t cursor = IndexOfAdjacentGrapheme(caret, true);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- }
- } else { // The caret's associated character is in a RTL run.
- if (caret_placement == SelectionModel::TRAILING)
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- else if (caret > run->range.start()) {
- caret = IndexOfAdjacentGrapheme(caret, false);
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- }
- }
-
- // The character is at the end of its run; go to the next visual run.
- size_t visual_index = logical_to_visual_[run_index];
- if (visual_index == runs_.size() - 1)
- return RightEndSelectionModel();
- internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]];
- return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) :
- FirstSelectionModelInsideRun(next);
-}
-
RenderText* RenderText::CreateRenderText() {
return new RenderTextWin;
}
diff --git a/ui/gfx/render_text_win.h b/ui/gfx/render_text_win.h
index 4cbcb35..f3962d7 100644
--- a/ui/gfx/render_text_win.h
+++ b/ui/gfx/render_text_win.h
@@ -61,6 +61,7 @@ class RenderTextWin : public RenderText {
virtual ~RenderTextWin();
// Overridden from RenderText:
+ virtual base::i18n::TextDirection GetTextDirection() OVERRIDE;
virtual int GetStringWidth() OVERRIDE;
virtual SelectionModel FindCursorPosition(const Point& point) OVERRIDE;
virtual Rect GetCursorBounds(const SelectionModel& selection,
@@ -68,15 +69,15 @@ class RenderTextWin : public RenderText {
protected:
// Overridden from RenderText:
- virtual SelectionModel GetLeftSelectionModel(const SelectionModel& current,
- BreakType break_type) OVERRIDE;
- virtual SelectionModel GetRightSelectionModel(const SelectionModel& current,
- BreakType break_type) OVERRIDE;
- virtual SelectionModel LeftEndSelectionModel() OVERRIDE;
- virtual SelectionModel RightEndSelectionModel() OVERRIDE;
- virtual void GetSubstringBounds(size_t from,
- size_t to,
- std::vector<Rect>* bounds) OVERRIDE;
+ virtual SelectionModel AdjacentCharSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) OVERRIDE;
+ virtual SelectionModel AdjacentWordSelectionModel(
+ const SelectionModel& selection,
+ VisualCursorDirection direction) OVERRIDE;
+ virtual SelectionModel EdgeSelectionModel(
+ VisualCursorDirection direction) OVERRIDE;
+ virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to) OVERRIDE;
virtual void SetSelectionModel(const SelectionModel& model) OVERRIDE;
virtual bool IsCursorablePosition(size_t position) OVERRIDE;
virtual void UpdateLayout() OVERRIDE;
@@ -84,7 +85,9 @@ class RenderTextWin : public RenderText {
virtual void DrawVisualText(Canvas* canvas) OVERRIDE;
private:
- virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) OVERRIDE;
+ virtual size_t IndexOfAdjacentGrapheme(
+ size_t index,
+ LogicalCursorDirection direction) OVERRIDE;
void ItemizeLogicalText();
void LayoutVisualText();
@@ -100,11 +103,6 @@ class RenderTextWin : public RenderText {
SelectionModel FirstSelectionModelInsideRun(internal::TextRun* run);
SelectionModel LastSelectionModelInsideRun(internal::TextRun* run);
- // Get the selection model visually left/right of |selection| by one grapheme.
- // The returned value represents a cursor/caret position without a selection.
- SelectionModel LeftSelectionModel(const SelectionModel& selection);
- SelectionModel RightSelectionModel(const SelectionModel& selection);
-
// National Language Support native digit and digit substitution settings.
SCRIPT_DIGITSUBSTITUTE digit_substitute_;
diff --git a/ui/views/controls/textfield/native_textfield_views.cc b/ui/views/controls/textfield/native_textfield_views.cc
index a3beceb..5f2ac4f 100644
--- a/ui/views/controls/textfield/native_textfield_views.cc
+++ b/ui/views/controls/textfield/native_textfield_views.cc
@@ -870,62 +870,44 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
cursor_changed = text_changed = Paste();
break;
case ui::VKEY_RIGHT:
- model_->MoveCursorRight(
- control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, selection);
- cursor_changed = true;
- break;
case ui::VKEY_LEFT:
- model_->MoveCursorLeft(
- control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, selection);
+ model_->MoveCursor(
+ control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK,
+ (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT,
+ selection);
cursor_changed = true;
break;
case ui::VKEY_END:
case ui::VKEY_HOME:
if ((key_code == ui::VKEY_HOME) ==
(GetRenderText()->GetTextDirection() == base::i18n::RIGHT_TO_LEFT))
- model_->MoveCursorRight(gfx::LINE_BREAK, selection);
+ model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, selection);
else
- model_->MoveCursorLeft(gfx::LINE_BREAK, selection);
+ model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, selection);
cursor_changed = true;
break;
case ui::VKEY_BACK:
- if (!editable)
- break;
- if (!model_->HasSelection()) {
- if (selection && control) {
- // If both shift and control are pressed, then erase upto the
- // beginning of the buffer in ChromeOS. In windows, do nothing.
-#if defined(OS_WIN)
- break;
-#else
- model_->MoveCursorLeft(gfx::LINE_BREAK, true);
-#endif
- } else if (control) {
- // If only control is pressed, then erase the previous word.
- model_->MoveCursorLeft(gfx::WORD_BREAK, true);
- }
- }
- text_changed = model_->Backspace();
- cursor_changed = true;
- break;
case ui::VKEY_DELETE:
if (!editable)
break;
if (!model_->HasSelection()) {
+ gfx::VisualCursorDirection direction = (key_code == ui::VKEY_DELETE) ?
+ gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
if (selection && control) {
- // If both shift and control are pressed, then erase upto the
- // end of the buffer in ChromeOS. In windows, do nothing.
+ // If both shift and control are pressed, then erase up to the
+ // beginning/end of the buffer in ChromeOS. In windows, do nothing.
#if defined(OS_WIN)
break;
#else
- model_->MoveCursorRight(gfx::LINE_BREAK, true);
+ model_->MoveCursor(gfx::LINE_BREAK, direction, true);
#endif
} else if (control) {
- // If only control is pressed, then erase the next word.
- model_->MoveCursorRight(gfx::WORD_BREAK, true);
+ // If only control is pressed, then erase the previous/next word.
+ model_->MoveCursor(gfx::WORD_BREAK, direction, true);
}
}
- cursor_changed = text_changed = model_->Delete();
+ cursor_changed = text_changed = (key_code == ui::VKEY_BACK) ?
+ model_->Backspace() : model_->Delete();
break;
case ui::VKEY_INSERT:
GetRenderText()->ToggleInsertMode();
diff --git a/ui/views/controls/textfield/textfield_views_model.cc b/ui/views/controls/textfield/textfield_views_model.cc
index 7df402d..ab5a94b 100644
--- a/ui/views/controls/textfield/textfield_views_model.cc
+++ b/ui/views/controls/textfield/textfield_views_model.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -324,10 +324,9 @@ void TextfieldViewsModel::Append(const string16& text) {
if (HasCompositionText())
ConfirmCompositionText();
size_t save = GetCursorPosition();
- if (render_text_->GetTextDirection() == base::i18n::LEFT_TO_RIGHT)
- MoveCursorRight(gfx::LINE_BREAK, false);
- else
- MoveCursorLeft(gfx::LINE_BREAK, false);
+ MoveCursor(gfx::LINE_BREAK,
+ render_text_->GetVisualDirectionOfLogicalEnd(),
+ false);
InsertText(text);
render_text_->SetCursorPosition(save);
ClearSelection();
@@ -345,8 +344,8 @@ bool TextfieldViewsModel::Delete() {
}
if (GetText().length() > GetCursorPosition()) {
size_t cursor_position = GetCursorPosition();
- size_t next_grapheme_index =
- render_text_->GetIndexOfNextGrapheme(cursor_position);
+ size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme(
+ cursor_position, gfx::CURSOR_FORWARD);
ExecuteAndRecordDelete(cursor_position, next_grapheme_index, true);
return true;
}
@@ -375,18 +374,12 @@ size_t TextfieldViewsModel::GetCursorPosition() const {
return render_text_->GetCursorPosition();
}
-void TextfieldViewsModel::MoveCursorLeft(gfx::BreakType break_type,
- bool select) {
- if (HasCompositionText())
- ConfirmCompositionText();
- render_text_->MoveCursorLeft(break_type, select);
-}
-
-void TextfieldViewsModel::MoveCursorRight(gfx::BreakType break_type,
- bool select) {
+void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type,
+ gfx::VisualCursorDirection direction,
+ bool select) {
if (HasCompositionText())
ConfirmCompositionText();
- render_text_->MoveCursorRight(break_type, select);
+ render_text_->MoveCursor(break_type, direction, select);
}
bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& selection) {
@@ -674,7 +667,8 @@ void TextfieldViewsModel::ReplaceTextInternal(const string16& text,
const gfx::SelectionModel& model = render_text_->selection_model();
// When there is no selection, the default is to replace the next grapheme
// with |text|. So, need to find the index of next grapheme first.
- size_t next = render_text_->GetIndexOfNextGrapheme(cursor);
+ size_t next =
+ render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD);
if (next == model.selection_end())
render_text_->MoveCursorTo(model);
else
diff --git a/ui/views/controls/textfield/textfield_views_model.h b/ui/views/controls/textfield/textfield_views_model.h
index e3fd7e8..51a4ea4 100644
--- a/ui/views/controls/textfield/textfield_views_model.h
+++ b/ui/views/controls/textfield/textfield_views_model.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -127,8 +127,9 @@ class VIEWS_EXPORT TextfieldViewsModel {
// Moves the cursor, see RenderText for additional details.
// The current composition text will be confirmed.
- void MoveCursorLeft(gfx::BreakType break_type, bool select);
- void MoveCursorRight(gfx::BreakType break_type, bool select);
+ void MoveCursor(gfx::BreakType break_type,
+ gfx::VisualCursorDirection direction,
+ bool select);
// Moves the selection to the specified selection in |selection|.
// If there is composition text, it will be confirmed, which will update the
diff --git a/ui/views/controls/textfield/textfield_views_model_unittest.cc b/ui/views/controls/textfield/textfield_views_model_unittest.cc
index 579056d..39a1375b 100644
--- a/ui/views/controls/textfield/textfield_views_model_unittest.cc
+++ b/ui/views/controls/textfield/textfield_views_model_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -75,7 +75,7 @@ TEST_F(TextfieldViewsModelTest, EditString) {
EXPECT_STR_EQ("HILLWORLD", model.GetText());
// Insert "E" to make hello
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
model.InsertChar('E');
EXPECT_STR_EQ("HEILLWORLD", model.GetText());
// Replace "I" with "L"
@@ -94,11 +94,11 @@ TEST_F(TextfieldViewsModelTest, EditString) {
EXPECT_STR_EQ("HELLORLD", model.GetText());
// Move the cursor to start. backspace should fail.
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
EXPECT_FALSE(model.Backspace());
EXPECT_STR_EQ("HELLORLD", model.GetText());
// Move the cursor to the end. delete should fail.
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_FALSE(model.Delete());
EXPECT_STR_EQ("HELLORLD", model.GetText());
// but backspace should work.
@@ -236,9 +236,9 @@ TEST_F(TextfieldViewsModelTest, EditString_ComplexScript) {
// The first 2 characters are not strong directionality characters.
model.SetText(WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9\x05BC"));
#if defined(OS_WIN)
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
#else
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
#endif
EXPECT_TRUE(model.Backspace());
EXPECT_EQ(WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9"),
@@ -251,9 +251,9 @@ TEST_F(TextfieldViewsModelTest, EmptyString) {
EXPECT_EQ(string16(), model.GetSelectedText());
EXPECT_EQ(string16(), model.GetVisibleText());
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_EQ(0U, model.GetCursorPosition());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_EQ(string16(), model.GetSelectedText());
@@ -265,15 +265,15 @@ TEST_F(TextfieldViewsModelTest, EmptyString) {
TEST_F(TextfieldViewsModelTest, Selection) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO"));
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_STR_EQ("E", model.GetSelectedText());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_STR_EQ("EL", model.GetSelectedText());
- model.MoveCursorLeft(gfx::LINE_BREAK, true);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_STR_EQ("H", model.GetSelectedText());
- model.MoveCursorRight(gfx::LINE_BREAK, true);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_STR_EQ("ELLO", model.GetSelectedText());
model.ClearSelection();
EXPECT_EQ(string16(), model.GetSelectedText());
@@ -288,18 +288,18 @@ TEST_F(TextfieldViewsModelTest, Selection) {
// Select and move cursor
model.SelectRange(ui::Range(1U, 3U));
EXPECT_STR_EQ("EL", model.GetSelectedText());
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
EXPECT_EQ(1U, model.GetCursorPosition());
model.SelectRange(ui::Range(1U, 3U));
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_EQ(3U, model.GetCursorPosition());
// Select all and move cursor
model.SelectAll();
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
EXPECT_EQ(0U, model.GetCursorPosition());
model.SelectAll();
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_EQ(5U, model.GetCursorPosition());
}
@@ -316,26 +316,26 @@ TEST_F(TextfieldViewsModelTest, Selection_BidiWithNonSpacingMarks) {
#if defined(OS_LINUX)
model.Append(WideToUTF16(
L"abc\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8"L"def"));
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(2U, model.render_text()->GetSelectionStart());
EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_EQ(WideToUTF16(L"c"), model.GetSelectedText());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(2U, model.render_text()->GetSelectionStart());
EXPECT_EQ(7U, model.GetCursorPosition());
EXPECT_EQ(WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8"),
model.GetSelectedText());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(2U, model.render_text()->GetSelectionStart());
EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_EQ(WideToUTF16(L"c"), model.GetSelectedText());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(2U, model.render_text()->GetSelectionStart());
EXPECT_EQ(10U, model.GetCursorPosition());
EXPECT_EQ(WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8"L"d"),
@@ -352,36 +352,36 @@ TEST_F(TextfieldViewsModelTest, Selection_BidiWithNonSpacingMarks) {
// an RTL character.
model.SetText(WideToUTF16(L"a\x05E9"L"b"));
model.MoveCursorTo(gfx::SelectionModel(0));
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(WideToUTF16(L"a"), model.GetSelectedText());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(WideToUTF16(L"a"), model.GetSelectedText());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(WideToUTF16(L"a\x05E9"L"b"), model.GetSelectedText());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_EQ(3U, model.GetCursorPosition());
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_EQ(WideToUTF16(L"b"), model.GetSelectedText());
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_EQ(WideToUTF16(L"b"), model.GetSelectedText());
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_EQ(WideToUTF16(L"a\x05E9"L"b"), model.GetSelectedText());
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_EQ(WideToUTF16(L"a\x05E9"), model.GetSelectedText());
- model.MoveCursorRight(gfx::LINE_BREAK, false);
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(WideToUTF16(L"\x05E9"L"b"), model.GetSelectedText());
model.ClearSelection();
@@ -393,28 +393,28 @@ TEST_F(TextfieldViewsModelTest, Selection_BidiWithNonSpacingMarks) {
TEST_F(TextfieldViewsModelTest, SelectionAndEdit) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO"));
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "EL"
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "EL"
EXPECT_TRUE(model.Backspace());
EXPECT_STR_EQ("HLO", model.GetText());
model.Append(ASCIIToUTF16("ILL"));
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "LO"
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "LO"
EXPECT_TRUE(model.Delete());
EXPECT_STR_EQ("HILL", model.GetText());
EXPECT_EQ(1U, model.GetCursorPosition());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "I"
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "I"
model.InsertChar('E');
EXPECT_STR_EQ("HELL", model.GetText());
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "H"
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "H"
model.ReplaceChar('B');
EXPECT_STR_EQ("BELL", model.GetText());
- model.MoveCursorRight(gfx::LINE_BREAK, false);
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true); // select "ELL"
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); // "ELL"
model.ReplaceChar('E');
EXPECT_STR_EQ("BEE", model.GetText());
}
@@ -444,43 +444,43 @@ TEST_F(TextfieldViewsModelTest, Word) {
TextfieldViewsModel model(NULL);
model.Append(
ASCIIToUTF16("The answer to Life, the Universe, and Everything"));
- model.MoveCursorRight(gfx::WORD_BREAK, false);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_EQ(3U, model.GetCursorPosition());
- model.MoveCursorRight(gfx::WORD_BREAK, false);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_EQ(10U, model.GetCursorPosition());
- model.MoveCursorRight(gfx::WORD_BREAK, false);
- model.MoveCursorRight(gfx::WORD_BREAK, false);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_EQ(18U, model.GetCursorPosition());
// Should passes the non word char ','
- model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_EQ(23U, model.GetCursorPosition());
EXPECT_STR_EQ(", the", model.GetSelectedText());
// Move to the end.
- model.MoveCursorRight(gfx::WORD_BREAK, true);
- model.MoveCursorRight(gfx::WORD_BREAK, true);
- model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
// Should be safe to go next word at the end.
- model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
model.InsertChar('2');
EXPECT_EQ(19U, model.GetCursorPosition());
// Now backwards.
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, false); // leave 2.
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); // leave 2.
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_EQ(14U, model.GetCursorPosition());
EXPECT_STR_EQ("Life", model.GetSelectedText());
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_STR_EQ("to Life", model.GetSelectedText());
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
- model.MoveCursorLeft(gfx::WORD_BREAK, true); // Select to the begining.
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); // Now at start.
EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
// Should be safe to go pervious word at the begining.
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
model.ReplaceChar('4');
EXPECT_EQ(string16(), model.GetSelectedText());
@@ -490,14 +490,14 @@ TEST_F(TextfieldViewsModelTest, Word) {
TEST_F(TextfieldViewsModelTest, SetText) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO"));
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
model.SetText(ASCIIToUTF16("GOODBYE"));
EXPECT_STR_EQ("GOODBYE", model.GetText());
// SetText won't reset the cursor posistion.
EXPECT_EQ(5U, model.GetCursorPosition());
model.SelectAll();
EXPECT_STR_EQ("GOODBYE", model.GetSelectedText());
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_EQ(7U, model.GetCursorPosition());
model.SetText(ASCIIToUTF16("BYE"));
@@ -522,7 +522,7 @@ TEST_F(TextfieldViewsModelTest, MAYBE_Clipboard) {
string16 clipboard_text;
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO WORLD"));
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
// Test for cut: Empty selection.
EXPECT_FALSE(model.Cut());
@@ -532,7 +532,7 @@ TEST_F(TextfieldViewsModelTest, MAYBE_Clipboard) {
EXPECT_EQ(11U, model.GetCursorPosition());
// Test for cut: Non-empty selection.
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_TRUE(model.Cut());
clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
EXPECT_STR_EQ("WORLD", clipboard_text);
@@ -557,8 +557,8 @@ TEST_F(TextfieldViewsModelTest, MAYBE_Clipboard) {
// Test for paste.
model.ClearSelection();
- model.MoveCursorRight(gfx::LINE_BREAK, false);
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_TRUE(model.Paste());
clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
@@ -577,7 +577,7 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest) {
model.Append(ASCIIToUTF16(" HELLO !! WO RLD "));
// Test when cursor is at the beginning.
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
model.SelectWord();
SelectWordTestVerifier(model, ASCIIToUTF16(" "), 2U);
@@ -608,7 +608,7 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest) {
SelectWordTestVerifier(model, ASCIIToUTF16(" "), 20U);
// Test when cursor is at the end.
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
model.SelectWord();
SelectWordTestVerifier(model, ASCIIToUTF16(" "), 24U);
}
@@ -638,9 +638,9 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest_MixScripts) {
model.SetText(WideToUTF16(L"a\x05d0 \x05d1\x05d2 \x0915\x094d\x0915 "
L"\x4E2D\x56FD\x82B1\x5929"));
for (size_t i = 0; i < word_and_cursor.size(); ++i) {
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
for (size_t j = 0; j < i; ++j)
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
model.SelectWord();
SelectWordTestVerifier(model, WideToUTF16(word_and_cursor[i].word),
word_and_cursor[i].cursor);
@@ -651,61 +651,61 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest_MixScripts) {
TEST_F(TextfieldViewsModelTest, RangeTest) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO WORLD"));
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
ui::Range range;
model.GetSelectedRange(&range);
EXPECT_TRUE(range.is_empty());
EXPECT_EQ(0U, range.start());
EXPECT_EQ(0U, range.end());
- model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_FALSE(range.is_reversed());
EXPECT_EQ(0U, range.start());
EXPECT_EQ(5U, range.end());
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_EQ(0U, range.start());
EXPECT_EQ(4U, range.end());
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
model.GetSelectedRange(&range);
EXPECT_TRUE(range.is_empty());
EXPECT_EQ(0U, range.start());
EXPECT_EQ(0U, range.end());
// now from the end.
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
model.GetSelectedRange(&range);
EXPECT_TRUE(range.is_empty());
EXPECT_EQ(11U, range.start());
EXPECT_EQ(11U, range.end());
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_TRUE(range.is_reversed());
EXPECT_EQ(11U, range.start());
EXPECT_EQ(6U, range.end());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_TRUE(range.is_reversed());
EXPECT_EQ(11U, range.start());
EXPECT_EQ(7U, range.end());
- model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
model.GetSelectedRange(&range);
EXPECT_TRUE(range.is_empty());
EXPECT_EQ(11U, range.start());
EXPECT_EQ(11U, range.end());
// Select All
- model.MoveCursorLeft(gfx::LINE_BREAK, true);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_TRUE(range.is_reversed());
@@ -760,61 +760,61 @@ TEST_F(TextfieldViewsModelTest, SelectRangeTest) {
TEST_F(TextfieldViewsModelTest, SelectionModelTest) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO WORLD"));
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
gfx::SelectionModel sel;
model.GetSelectionModel(&sel);
EXPECT_EQ(sel.selection_start(), sel.selection_end());
EXPECT_EQ(0U, sel.selection_start());
EXPECT_EQ(0U, sel.selection_end());
- model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
model.GetSelectionModel(&sel);
EXPECT_NE(sel.selection_start(), sel.selection_end());
EXPECT_LE(sel.selection_start(), sel.selection_end());
EXPECT_EQ(0U, sel.selection_start());
EXPECT_EQ(5U, sel.selection_end());
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
model.GetSelectionModel(&sel);
EXPECT_NE(sel.selection_start(), sel.selection_end());
EXPECT_EQ(0U, sel.selection_start());
EXPECT_EQ(4U, sel.selection_end());
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
model.GetSelectionModel(&sel);
EXPECT_EQ(sel.selection_start(), sel.selection_end());
EXPECT_EQ(0U, sel.selection_start());
EXPECT_EQ(0U, sel.selection_end());
// now from the end.
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
model.GetSelectionModel(&sel);
EXPECT_EQ(sel.selection_start(), sel.selection_end());
EXPECT_EQ(11U, sel.selection_start());
EXPECT_EQ(11U, sel.selection_end());
- model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
model.GetSelectionModel(&sel);
EXPECT_NE(sel.selection_start(), sel.selection_end());
EXPECT_GT(sel.selection_start(), sel.selection_end());
EXPECT_EQ(11U, sel.selection_start());
EXPECT_EQ(6U, sel.selection_end());
- model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
model.GetSelectionModel(&sel);
EXPECT_NE(sel.selection_start(), sel.selection_end());
EXPECT_GT(sel.selection_start(), sel.selection_end());
EXPECT_EQ(11U, sel.selection_start());
EXPECT_EQ(7U, sel.selection_end());
- model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
model.GetSelectionModel(&sel);
EXPECT_EQ(sel.selection_start(), sel.selection_end());
EXPECT_EQ(11U, sel.selection_start());
EXPECT_EQ(11U, sel.selection_end());
// Select All
- model.MoveCursorLeft(gfx::LINE_BREAK, true);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true);
model.GetSelectionModel(&sel);
EXPECT_NE(sel.selection_start(), sel.selection_end());
EXPECT_GT(sel.selection_start(), sel.selection_end());
@@ -907,7 +907,7 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) {
EXPECT_TRUE(model.SetText(ASCIIToUTF16("1234567890")));
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
model.SetCompositionText(composition);
EXPECT_STR_EQ("1234567890678", model.GetText());
@@ -919,7 +919,7 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) {
EXPECT_FALSE(model.HasCompositionText());
EXPECT_FALSE(model.HasSelection());
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_STR_EQ("-", model.GetSelectedText());
model.SetCompositionText(composition);
EXPECT_STR_EQ("1234567890678", model.GetText());
@@ -951,39 +951,39 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) {
model.SetText(string16());
model.SetCompositionText(composition);
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
EXPECT_STR_EQ("678", model.GetText());
EXPECT_EQ(2U, model.GetCursorPosition());
model.SetCompositionText(composition);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
EXPECT_STR_EQ("676788", model.GetText());
EXPECT_EQ(6U, model.GetCursorPosition());
model.SetCompositionText(composition);
- model.MoveCursorLeft(gfx::WORD_BREAK, false);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
EXPECT_STR_EQ("676788678", model.GetText());
model.SetText(string16());
model.SetCompositionText(composition);
- model.MoveCursorRight(gfx::WORD_BREAK, false);
+ model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
model.SetCompositionText(composition);
- model.MoveCursorLeft(gfx::LINE_BREAK, true);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
EXPECT_STR_EQ("678678", model.GetText());
model.SetCompositionText(composition);
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
EXPECT_STR_EQ("678", model.GetText());
@@ -1098,7 +1098,7 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_BasicTest) {
model.MoveCursorTo(sel);
EXPECT_TRUE(model.Delete());
EXPECT_STR_EQ("ABDE", model.GetText());
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
EXPECT_TRUE(model.Delete());
EXPECT_STR_EQ("BDE", model.GetText());
EXPECT_TRUE(model.Undo());
@@ -1194,7 +1194,7 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_BackspaceThenSetText) {
EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_STR_EQ("www.google.com", model.GetText());
model.SetText(ASCIIToUTF16("www.google.com")); // Confirm the text.
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_EQ(14U, model.GetCursorPosition());
EXPECT_TRUE(model.Backspace());
EXPECT_TRUE(model.Backspace());
@@ -1282,7 +1282,7 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest) {
EXPECT_EQ(1U, model.GetCursorPosition());
model.SelectRange(ui::Range(1, 1));
EXPECT_FALSE(model.Cut());
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_TRUE(model.Paste());
EXPECT_STR_EQ("ABCBCDEBC", model.GetText());
EXPECT_EQ(9U, model.GetCursorPosition());
@@ -1336,7 +1336,7 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest) {
model.SelectRange(ui::Range(1, 3));
model.Copy();
EXPECT_STR_EQ("1232345", model.GetText());
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
EXPECT_TRUE(model.Paste());
EXPECT_STR_EQ("123234523", model.GetText());
EXPECT_EQ(9U, model.GetCursorPosition());
@@ -1348,8 +1348,8 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest) {
TEST_F(TextfieldViewsModelTest, UndoRedo_CursorTest) {
TextfieldViewsModel model(NULL);
model.InsertChar('a');
- model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
- model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
+ model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
model.InsertChar('b');
// Moving the cursor shouldn't create a new edit.
EXPECT_STR_EQ("ab", model.GetText());
@@ -1487,7 +1487,7 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CompositionText) {
composition.selection = ui::Range(2, 3);
model.SetText(ASCIIToUTF16("ABCDE"));
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
model.InsertChar('x');
EXPECT_STR_EQ("ABCDEx", model.GetText());
EXPECT_TRUE(model.Undo()); // set composition should forget undone edit.
@@ -1510,7 +1510,7 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CompositionText) {
EXPECT_FALSE(model.Redo());
// Canceling composition
- model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false);
model.SetCompositionText(composition);
EXPECT_STR_EQ("abcABCDEabc", model.GetText());
model.CancelCompositionText();
@@ -1526,7 +1526,7 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CompositionText) {
// SetText with the same text as the result.
ResetModel(&model);
model.SetText(ASCIIToUTF16("ABCDE"));
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
model.SetCompositionText(composition);
EXPECT_STR_EQ("ABCDEabc", model.GetText());
model.SetText(ASCIIToUTF16("ABCDEabc"));
@@ -1541,7 +1541,7 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CompositionText) {
// remember composition text.
ResetModel(&model);
model.SetText(ASCIIToUTF16("ABCDE"));
- model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false);
model.SetCompositionText(composition);
EXPECT_STR_EQ("ABCDEabc", model.GetText());
model.SetText(ASCIIToUTF16("1234"));