| Index: ui/gfx/render_text.cc
|
| diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
|
| index 35134e8ac5daae44a934da4df3a6472379c69107..91c7fddea20e6262fbe507a59ad43993f2f1d341 100644
|
| --- a/ui/gfx/render_text.cc
|
| +++ b/ui/gfx/render_text.cc
|
| @@ -338,7 +338,7 @@ void RenderText::SetText(const string16& text) {
|
|
|
| // Reset selection model. SetText should always followed by SetSelectionModel
|
| // or SetCursorPosition in upper layer.
|
| - SetSelectionModel(SelectionModel(0, 0, SelectionModel::LEADING));
|
| + SetSelectionModel(SelectionModel());
|
|
|
| UpdateLayout();
|
| }
|
| @@ -391,10 +391,6 @@ void RenderText::SetDisplayRect(const Rect& r) {
|
| UpdateLayout();
|
| }
|
|
|
| -size_t RenderText::GetCursorPosition() const {
|
| - return selection_model_.selection_end();
|
| -}
|
| -
|
| void RenderText::SetCursorPosition(size_t position) {
|
| MoveCursorTo(position, false);
|
| }
|
| @@ -402,10 +398,9 @@ void RenderText::SetCursorPosition(size_t position) {
|
| void RenderText::MoveCursor(BreakType break_type,
|
| VisualCursorDirection direction,
|
| bool select) {
|
| - SelectionModel position(selection_model());
|
| - position.set_selection_start(GetCursorPosition());
|
| + SelectionModel position(cursor_position(), selection_model_.caret_affinity());
|
| // Cancelling a selection moves to the edge of the selection.
|
| - if (break_type != LINE_BREAK && !EmptySelection() && !select) {
|
| + if (break_type != LINE_BREAK && !selection().is_empty() && !select) {
|
| SelectionModel selection_start = GetSelectionModelForSelectionStart();
|
| int start_x = GetCursorBounds(selection_start, true).x();
|
| int cursor_x = GetCursorBounds(position, true).x();
|
| @@ -421,90 +416,63 @@ void RenderText::MoveCursor(BreakType break_type,
|
| position = GetAdjacentSelectionModel(position, break_type, direction);
|
| }
|
| if (select)
|
| - position.set_selection_start(GetSelectionStart());
|
| + position.set_selection_start(selection().start());
|
| MoveCursorTo(position);
|
| }
|
|
|
| bool RenderText::MoveCursorTo(const SelectionModel& model) {
|
| - SelectionModel sel(model);
|
| - size_t text_length = text().length();
|
| // Enforce valid selection model components.
|
| - if (sel.selection_start() > text_length)
|
| - sel.set_selection_start(text_length);
|
| - if (sel.selection_end() > text_length)
|
| - sel.set_selection_end(text_length);
|
| + size_t text_length = text().length();
|
| + ui::Range range(std::min(model.selection().start(), text_length),
|
| + std::min(model.caret_pos(), text_length));
|
| // The current model only supports caret positions at valid character indices.
|
| - if (text_length == 0) {
|
| - sel.set_caret_pos(0);
|
| - sel.set_caret_placement(SelectionModel::LEADING);
|
| - } else if (sel.caret_pos() >= text_length) {
|
| - 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()) ||
|
| - !IsCursorablePosition(sel.selection_end()) ||
|
| - !IsCursorablePosition(sel.caret_pos()))
|
| + if (!IsCursorablePosition(range.start()) ||
|
| + !IsCursorablePosition(range.end()))
|
| return false;
|
| -
|
| - bool changed = !sel.Equals(selection_model_);
|
| + SelectionModel sel(range, model.caret_affinity());
|
| + bool changed = sel != selection_model_;
|
| SetSelectionModel(sel);
|
| return changed;
|
| }
|
|
|
| bool RenderText::MoveCursorTo(const Point& point, bool select) {
|
| - SelectionModel selection = FindCursorPosition(point);
|
| + SelectionModel position = FindCursorPosition(point);
|
| if (select)
|
| - selection.set_selection_start(GetSelectionStart());
|
| - return MoveCursorTo(selection);
|
| + position.set_selection_start(selection().start());
|
| + return MoveCursorTo(position);
|
| }
|
|
|
| bool RenderText::SelectRange(const ui::Range& range) {
|
| - size_t text_length = text().length();
|
| - size_t start = std::min(range.start(), text_length);
|
| - size_t end = std::min(range.end(), text_length);
|
| -
|
| - if (!IsCursorablePosition(start) || !IsCursorablePosition(end))
|
| + ui::Range sel(std::min(range.start(), text().length()),
|
| + std::min(range.end(), text().length()));
|
| + if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end()))
|
| return false;
|
| -
|
| - size_t pos = end;
|
| - SelectionModel::CaretPlacement placement = SelectionModel::LEADING;
|
| - if (start < end) {
|
| - pos = IndexOfAdjacentGrapheme(end, CURSOR_BACKWARD);
|
| - DCHECK_LT(pos, end);
|
| - placement = SelectionModel::TRAILING;
|
| - } else if (end == text_length) {
|
| - SelectionModel end_selection =
|
| - EdgeSelectionModel(GetVisualDirectionOfLogicalEnd());
|
| - pos = end_selection.caret_pos();
|
| - placement = end_selection.caret_placement();
|
| - }
|
| - SetSelectionModel(SelectionModel(start, end, pos, placement));
|
| + LogicalCursorDirection affinity =
|
| + (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD;
|
| + SetSelectionModel(SelectionModel(sel, affinity));
|
| return true;
|
| }
|
|
|
| bool RenderText::IsPointInSelection(const Point& point) {
|
| - if (EmptySelection())
|
| + if (selection().is_empty())
|
| return false;
|
| - // TODO(xji): should this check whether the point is inside the visual
|
| - // selection bounds? In case of "abcFED", if "ED" is selected, |point| points
|
| - // to the right half of 'c', is the point in selection?
|
| - size_t pos = FindCursorPosition(point).selection_end();
|
| - return (pos >= MinOfSelection() && pos < MaxOfSelection());
|
| + SelectionModel cursor = FindCursorPosition(point);
|
| + return RangeContainsCaret(
|
| + selection(), cursor.caret_pos(), cursor.caret_affinity());
|
| }
|
|
|
| void RenderText::ClearSelection() {
|
| - SelectionModel sel(selection_model());
|
| - sel.set_selection_start(GetCursorPosition());
|
| - SetSelectionModel(sel);
|
| + SetSelectionModel(SelectionModel(cursor_position(),
|
| + selection_model_.caret_affinity()));
|
| }
|
|
|
| void RenderText::SelectAll() {
|
| - SelectionModel sel = EdgeSelectionModel(CURSOR_RIGHT);
|
| - sel.set_selection_start(EdgeSelectionModel(CURSOR_LEFT).selection_start());
|
| - SetSelectionModel(sel);
|
| + SelectionModel all;
|
| + if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT)
|
| + all = SelectionModel(ui::Range(0, text().length()), CURSOR_FORWARD);
|
| + else
|
| + all = SelectionModel(ui::Range(text().length(), 0), CURSOR_BACKWARD);
|
| + SetSelectionModel(all);
|
| }
|
|
|
| void RenderText::SelectWord() {
|
| @@ -513,7 +481,7 @@ void RenderText::SelectWord() {
|
| return;
|
| }
|
|
|
| - size_t cursor_position = GetCursorPosition();
|
| + size_t cursor_pos = cursor_position();
|
|
|
| base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
|
| bool success = iter.Init();
|
| @@ -521,24 +489,22 @@ void RenderText::SelectWord() {
|
| if (!success)
|
| return;
|
|
|
| - size_t selection_start = cursor_position;
|
| + size_t selection_start = cursor_pos;
|
| for (; selection_start != 0; --selection_start) {
|
| if (iter.IsStartOfWord(selection_start) ||
|
| iter.IsEndOfWord(selection_start))
|
| break;
|
| }
|
|
|
| - if (selection_start == cursor_position)
|
| - ++cursor_position;
|
| + if (selection_start == cursor_pos)
|
| + ++cursor_pos;
|
|
|
| - for (; cursor_position < text().length(); ++cursor_position) {
|
| - if (iter.IsEndOfWord(cursor_position) ||
|
| - iter.IsStartOfWord(cursor_position))
|
| + for (; cursor_pos < text().length(); ++cursor_pos)
|
| + if (iter.IsEndOfWord(cursor_pos) || iter.IsStartOfWord(cursor_pos))
|
| break;
|
| - }
|
|
|
| MoveCursorTo(selection_start, false);
|
| - MoveCursorTo(cursor_position, true);
|
| + MoveCursorTo(cursor_pos, true);
|
| }
|
|
|
| const ui::Range& RenderText::GetCompositionRange() const {
|
| @@ -604,24 +570,51 @@ void RenderText::Draw(Canvas* canvas) {
|
| canvas->Restore();
|
| }
|
|
|
| +Rect RenderText::GetCursorBounds(const SelectionModel& caret,
|
| + bool insert_mode) {
|
| + EnsureLayout();
|
| +
|
| + size_t caret_pos = caret.caret_pos();
|
| + // In overtype mode, ignore the affinity and always indicate that we will
|
| + // overtype the next character.
|
| + LogicalCursorDirection caret_affinity =
|
| + insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
|
| + int x = 0, width = 0, height = 0;
|
| + if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
|
| + // The caret is attached to the boundary. Always return a zero-width caret,
|
| + // since there is nothing to overtype.
|
| + Size size = GetStringSize();
|
| + if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0))
|
| + x = size.width();
|
| + height = size.height();
|
| + } else {
|
| + size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ?
|
| + caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD);
|
| + ui::Range xspan;
|
| + GetGlyphBounds(grapheme_start, &xspan, &height);
|
| + if (insert_mode) {
|
| + x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start();
|
| + } else { // overtype mode
|
| + x = xspan.GetMin();
|
| + width = xspan.length();
|
| + }
|
| + }
|
| + height = std::min(height, display_rect().height());
|
| + int y = (display_rect().height() - height) / 2;
|
| + return Rect(ToViewPoint(Point(x, y)), Size(width, height));
|
| +}
|
| +
|
| const Rect& RenderText::GetUpdatedCursorBounds() {
|
| UpdateCachedBoundsAndOffset();
|
| return cursor_bounds_;
|
| }
|
|
|
| SelectionModel RenderText::GetSelectionModelForSelectionStart() {
|
| - size_t selection_start = GetSelectionStart();
|
| - size_t selection_end = GetCursorPosition();
|
| - if (selection_start < selection_end)
|
| - return SelectionModel(selection_start,
|
| - selection_start,
|
| - SelectionModel::LEADING);
|
| - else if (selection_start > selection_end)
|
| - return SelectionModel(
|
| - selection_start,
|
| - IndexOfAdjacentGrapheme(selection_start, CURSOR_BACKWARD),
|
| - SelectionModel::TRAILING);
|
| - return selection_model_;
|
| + const ui::Range& sel = selection();
|
| + if (sel.is_empty())
|
| + return selection_model_;
|
| + return SelectionModel(sel.start(),
|
| + sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
|
| }
|
|
|
| RenderText::RenderText()
|
| @@ -658,15 +651,16 @@ SelectionModel RenderText::GetAdjacentSelectionModel(
|
| return AdjacentWordSelectionModel(current, direction);
|
| }
|
|
|
| -void RenderText::SetSelectionModel(const SelectionModel& model) {
|
| - DCHECK_LE(model.selection_start(), text().length());
|
| - selection_model_.set_selection_start(model.selection_start());
|
| - DCHECK_LE(model.selection_end(), text().length());
|
| - selection_model_.set_selection_end(model.selection_end());
|
| - DCHECK_LT(model.caret_pos(), std::max<size_t>(text().length(), 1));
|
| - selection_model_.set_caret_pos(model.caret_pos());
|
| - selection_model_.set_caret_placement(model.caret_placement());
|
| +SelectionModel RenderText::EdgeSelectionModel(
|
| + VisualCursorDirection direction) {
|
| + if (direction == GetVisualDirectionOfLogicalEnd())
|
| + return SelectionModel(text().length(), CURSOR_FORWARD);
|
| + return SelectionModel(0, CURSOR_BACKWARD);
|
| +}
|
|
|
| +void RenderText::SetSelectionModel(const SelectionModel& model) {
|
| + DCHECK_LE(model.selection().GetMax(), text().length());
|
| + selection_model_ = model;
|
| cached_bounds_and_offset_valid_ = false;
|
| }
|
|
|
| @@ -686,17 +680,16 @@ void RenderText::ApplyCompositionAndSelectionStyles(
|
| if (composition_range_.IsValid() && !composition_range_.is_empty()) {
|
| StyleRange composition_style(default_style_);
|
| composition_style.underline = true;
|
| - composition_style.range.set_start(composition_range_.start());
|
| - composition_style.range.set_end(composition_range_.end());
|
| + composition_style.range = composition_range_;
|
| ApplyStyleRangeImpl(style_ranges, composition_style);
|
| }
|
| // Apply a selection style override to a copy of the style ranges.
|
| - if (!EmptySelection()) {
|
| + if (!selection().is_empty()) {
|
| StyleRange selection_style(default_style_);
|
| selection_style.foreground = NativeTheme::instance()->GetSystemColor(
|
| NativeTheme::kColorId_TextfieldSelectionColor);
|
| - selection_style.range.set_start(MinOfSelection());
|
| - selection_style.range.set_end(MaxOfSelection());
|
| + selection_style.range = ui::Range(selection().GetMin(),
|
| + selection().GetMax());
|
| ApplyStyleRangeImpl(style_ranges, selection_style);
|
| }
|
| // Apply replacement-mode style override to a copy of the style ranges.
|
| @@ -711,7 +704,7 @@ void RenderText::ApplyCompositionAndSelectionStyles(
|
| StyleRange replacement_mode_style(default_style_);
|
| replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor(
|
| NativeTheme::kColorId_TextfieldSelectionColor);
|
| - size_t cursor = GetCursorPosition();
|
| + size_t cursor = cursor_position();
|
| replacement_mode_style.range.set_start(cursor);
|
| replacement_mode_style.range.set_end(
|
| IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD));
|
| @@ -735,7 +728,7 @@ Point RenderText::ToViewPoint(const Point& point) {
|
| }
|
|
|
| int RenderText::GetContentWidth() {
|
| - return GetStringWidth() + (cursor_enabled_ ? 1 : 0);
|
| + return GetStringSize().width() + (cursor_enabled_ ? 1 : 0);
|
| }
|
|
|
| Point RenderText::GetAlignmentOffset() {
|
| @@ -765,7 +758,7 @@ void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
|
| if (!fade_head() && !fade_tail())
|
| return;
|
|
|
| - const int text_width = GetStringWidth();
|
| + const int text_width = GetStringSize().width();
|
| const int display_width = display_rect().width();
|
|
|
| // If the text fits as-is, no need to fade.
|
| @@ -808,16 +801,22 @@ void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
|
| }
|
| }
|
|
|
| +// static
|
| +bool RenderText::RangeContainsCaret(const ui::Range& range,
|
| + size_t caret_pos,
|
| + LogicalCursorDirection caret_affinity) {
|
| + // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
|
| + size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
|
| + caret_pos - 1 : caret_pos + 1;
|
| + return range.Contains(ui::Range(caret_pos, adjacent));
|
| +}
|
| +
|
| void RenderText::MoveCursorTo(size_t position, bool select) {
|
| size_t cursor = std::min(position, text().length());
|
| - 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;
|
| - if (IsCursorablePosition(cursor)) {
|
| - SelectionModel sel(selection_start, cursor, caret_pos, placement);
|
| - SetSelectionModel(sel);
|
| - }
|
| + if (IsCursorablePosition(cursor))
|
| + SetSelectionModel(SelectionModel(
|
| + ui::Range(select ? selection().start() : cursor, cursor),
|
| + (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
|
| }
|
|
|
| void RenderText::UpdateCachedBoundsAndOffset() {
|
| @@ -866,8 +865,7 @@ void RenderText::UpdateCachedBoundsAndOffset() {
|
| }
|
|
|
| void RenderText::DrawSelection(Canvas* canvas) {
|
| - std::vector<Rect> sel = GetSubstringBounds(
|
| - GetSelectionStart(), GetCursorPosition());
|
| + std::vector<Rect> sel = GetSubstringBounds(selection());
|
| NativeTheme::ColorId color_id = focused() ?
|
| NativeTheme::kColorId_TextfieldSelectionBackgroundFocused :
|
| NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused;
|
|
|