diff options
Diffstat (limited to 'chromium/ui/gfx/render_text.cc')
-rw-r--r-- | chromium/ui/gfx/render_text.cc | 176 |
1 files changed, 138 insertions, 38 deletions
diff --git a/chromium/ui/gfx/render_text.cc b/chromium/ui/gfx/render_text.cc index 1fcf18cede4..47f42e37cb6 100644 --- a/chromium/ui/gfx/render_text.cc +++ b/chromium/ui/gfx/render_text.cc @@ -13,12 +13,12 @@ #include "third_party/icu/source/common/unicode/utf16.h" #include "third_party/skia/include/core/SkTypeface.h" #include "third_party/skia/include/effects/SkGradientShader.h" -#include "ui/base/text/text_elider.h" -#include "ui/base/text/utf16_indexing.h" #include "ui/gfx/canvas.h" #include "ui/gfx/insets.h" #include "ui/gfx/skia_util.h" #include "ui/gfx/text_constants.h" +#include "ui/gfx/text_elider.h" +#include "ui/gfx/utf16_indexing.h" namespace gfx { @@ -288,8 +288,8 @@ StyleIterator::StyleIterator(const BreakList<SkColor>& colors, StyleIterator::~StyleIterator() {} -ui::Range StyleIterator::GetRange() const { - ui::Range range(colors_.GetRange(color_)); +Range StyleIterator::GetRange() const { + Range range(colors_.GetRange(color_)); for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) range = range.Intersect(styles_[i].GetRange(style_[i])); return range; @@ -301,6 +301,14 @@ void StyleIterator::UpdatePosition(size_t position) { style_[i] = styles_[i].GetBreak(position); } +LineSegment::LineSegment() : run(0) {} + +LineSegment::~LineSegment() {} + +Line::Line() : preceding_heights(0), baseline(0) {} + +Line::~Line() {} + } // namespace internal RenderText::~RenderText() { @@ -308,6 +316,8 @@ RenderText::~RenderText() { void RenderText::SetText(const base::string16& text) { DCHECK(!composition_range_.IsValid()); + if (text_ == text) + return; text_ = text; // Adjust ranged styles and colors to accommodate a new text length. @@ -394,9 +404,18 @@ void RenderText::SetObscuredRevealIndex(int index) { ResetLayout(); } +void RenderText::SetMultiline(bool multiline) { + if (multiline != multiline_) { + multiline_ = multiline; + cached_bounds_and_offset_valid_ = false; + lines_.clear(); + } +} + void RenderText::SetDisplayRect(const Rect& r) { display_rect_ = r; cached_bounds_and_offset_valid_ = false; + lines_.clear(); } void RenderText::SetCursorPosition(size_t position) { @@ -431,8 +450,8 @@ void RenderText::MoveCursor(BreakType break_type, bool RenderText::MoveCursorTo(const SelectionModel& model) { // Enforce valid selection model components. size_t text_length = text().length(); - ui::Range range(std::min(model.selection().start(), text_length), - std::min(model.caret_pos(), text_length)); + 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 (!IsCursorablePosition(range.start()) || !IsCursorablePosition(range.end())) @@ -450,9 +469,9 @@ bool RenderText::MoveCursorTo(const Point& point, bool select) { return MoveCursorTo(position); } -bool RenderText::SelectRange(const ui::Range& range) { - ui::Range sel(std::min(range.start(), text().length()), - std::min(range.end(), text().length())); +bool RenderText::SelectRange(const Range& range) { + Range sel(std::min(range.start(), text().length()), + std::min(range.end(), text().length())); if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end())) return false; LogicalCursorDirection affinity = @@ -476,7 +495,7 @@ void RenderText::ClearSelection() { void RenderText::SelectAll(bool reversed) { const size_t length = text().length(); - const ui::Range all = reversed ? ui::Range(length, 0) : ui::Range(0, length); + const Range all = reversed ? Range(length, 0) : Range(0, length); const bool success = SelectRange(all); DCHECK(success); } @@ -517,13 +536,13 @@ void RenderText::SelectWord() { MoveCursorTo(reversed ? selection_min : selection_max, true); } -const ui::Range& RenderText::GetCompositionRange() const { +const Range& RenderText::GetCompositionRange() const { return composition_range_; } -void RenderText::SetCompositionRange(const ui::Range& composition_range) { +void RenderText::SetCompositionRange(const Range& composition_range) { CHECK(!composition_range.IsValid() || - ui::Range(0, text_.length()).Contains(composition_range)); + Range(0, text_.length()).Contains(composition_range)); composition_range_.set_end(composition_range.end()); composition_range_.set_start(composition_range.start()); ResetLayout(); @@ -539,7 +558,7 @@ void RenderText::SetColor(SkColor value) { #endif } -void RenderText::ApplyColor(SkColor value, const ui::Range& range) { +void RenderText::ApplyColor(SkColor value, const Range& range) { colors_.ApplyValue(value, range); #if defined(OS_WIN) @@ -564,9 +583,7 @@ void RenderText::SetStyle(TextStyle style, bool value) { } } -void RenderText::ApplyStyle(TextStyle style, - bool value, - const ui::Range& range) { +void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) { styles_[style].ApplyValue(value, range); // Only invalidate the layout on font changes; not for colors or decorations. @@ -686,6 +703,10 @@ void RenderText::DrawSelectedTextForDrag(Canvas* canvas) { Rect RenderText::GetCursorBounds(const SelectionModel& caret, bool insert_mode) { + // TODO(ckocagil): Support multiline. This function should return the height + // of the line the cursor is on. |GetStringSize()| now returns + // the multiline size, eliminate its use here. + EnsureLayout(); size_t caret_pos = caret.caret_pos(); @@ -704,7 +725,7 @@ Rect RenderText::GetCursorBounds(const SelectionModel& caret, } else { size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ? caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD); - ui::Range xspan(GetGlyphBounds(grapheme_start)); + Range xspan(GetGlyphBounds(grapheme_start)); if (insert_mode) { x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start(); } else { // overtype mode @@ -745,7 +766,7 @@ size_t RenderText::IndexOfAdjacentGrapheme(size_t index, } SelectionModel RenderText::GetSelectionModelForSelectionStart() { - const ui::Range& sel = selection(); + const Range& sel = selection(); if (sel.is_empty()) return selection_model_; return SelectionModel(sel.start(), @@ -768,13 +789,14 @@ RenderText::RenderText() selection_color_(kDefaultColor), selection_background_focused_color_(kDefaultSelectionBackgroundColor), focused_(false), - composition_range_(ui::Range::InvalidRange()), + composition_range_(Range::InvalidRange()), colors_(kDefaultColor), styles_(NUM_TEXT_STYLES), composition_and_selection_styles_applied_(false), obscured_(false), obscured_reveal_index_(-1), truncate_length_(0), + multiline_(false), fade_head_(false), fade_tail_(false), background_is_transparent_(false), @@ -818,6 +840,26 @@ const base::string16& RenderText::GetLayoutText() const { return layout_text_.empty() ? text_ : layout_text_; } +const BreakList<size_t>& RenderText::GetLineBreaks() { + if (line_breaks_.max() != 0) + return line_breaks_; + + const string16& layout_text = GetLayoutText(); + const size_t text_length = layout_text.length(); + line_breaks_.SetValue(0); + line_breaks_.SetMax(text_length); + base::i18n::BreakIterator iter(layout_text, + base::i18n::BreakIterator::BREAK_LINE); + const bool success = iter.Init(); + DCHECK(success); + if (success) { + do { + line_breaks_.ApplyValue(iter.pos(), Range(iter.pos(), text_length)); + } while (iter.Advance()); + } + return line_breaks_; +} + void RenderText::ApplyCompositionAndSelectionStyles() { // Save the underline and color breaks to undo the temporary styles later. DCHECK(!composition_and_selection_styles_applied_); @@ -830,7 +872,7 @@ void RenderText::ApplyCompositionAndSelectionStyles() { // Apply the selected text color to the [un-reversed] selection range. if (!selection().is_empty()) { - const ui::Range range(selection().GetMin(), selection().GetMax()); + const Range range(selection().GetMin(), selection().GetMax()); colors_.ApplyValue(selection_color_, range); } composition_and_selection_styles_applied_ = true; @@ -844,25 +886,81 @@ void RenderText::UndoCompositionAndSelectionStyles() { composition_and_selection_styles_applied_ = false; } -Vector2d RenderText::GetTextOffset() { +Vector2d RenderText::GetLineOffset(size_t line_number) { Vector2d offset = display_rect().OffsetFromOrigin(); - offset.Add(GetUpdatedDisplayOffset()); - offset.Add(GetAlignmentOffset()); + // TODO(ckocagil): Apply the display offset for multiline scrolling. + if (!multiline()) + offset.Add(GetUpdatedDisplayOffset()); + else + offset.Add(Vector2d(0, lines_[line_number].preceding_heights)); + offset.Add(GetAlignmentOffset(line_number)); return offset; } Point RenderText::ToTextPoint(const Point& point) { - return point - GetTextOffset(); + return point - GetLineOffset(0); + // TODO(ckocagil): Convert multiline view space points to text space. } Point RenderText::ToViewPoint(const Point& point) { - return point + GetTextOffset(); + if (!multiline()) + return point + GetLineOffset(0); + + // TODO(ckocagil): Traverse individual line segments for RTL support. + DCHECK(!lines_.empty()); + int x = point.x(); + size_t line = 0; + for (; line < lines_.size() && x > lines_[line].size.width(); ++line) + x -= lines_[line].size.width(); + return Point(x, point.y()) + GetLineOffset(line); } -Vector2d RenderText::GetAlignmentOffset() { +std::vector<Rect> RenderText::TextBoundsToViewBounds(const Range& x) { + std::vector<Rect> rects; + + if (!multiline()) { + rects.push_back(Rect(ToViewPoint(Point(x.GetMin(), 0)), + Size(x.length(), GetStringSize().height()))); + return rects; + } + + EnsureLayout(); + + // Each line segment keeps its position in text coordinates. Traverse all line + // segments and if the segment intersects with the given range, add the view + // rect corresponding to the intersection to |rects|. + for (size_t line = 0; line < lines_.size(); ++line) { + int line_x = 0; + const Vector2d offset = GetLineOffset(line); + for (size_t i = 0; i < lines_[line].segments.size(); ++i) { + const internal::LineSegment* segment = &lines_[line].segments[i]; + const Range intersection = segment->x_range.Intersect(x); + if (!intersection.is_empty()) { + Rect rect(line_x + intersection.start() - segment->x_range.start(), + 0, intersection.length(), lines_[line].size.height()); + rects.push_back(rect + offset); + } + line_x += segment->x_range.length(); + } + } + + return rects; +} + +Vector2d RenderText::GetAlignmentOffset(size_t line_number) { + // TODO(ckocagil): Enable |lines_| usage in other platforms. +#if defined(OS_WIN) + DCHECK_LT(line_number, lines_.size()); +#endif Vector2d offset; if (horizontal_alignment_ != ALIGN_LEFT) { - offset.set_x(display_rect().width() - GetContentWidth()); +#if defined(OS_WIN) + const int width = lines_[line_number].size.width() + + (cursor_enabled_ ? 1 : 0); +#else + const int width = GetContentWidth(); +#endif + offset.set_x(display_rect().width() - width); if (horizontal_alignment_ == ALIGN_CENTER) offset.set_x(offset.x() / 2); } @@ -875,14 +973,13 @@ Vector2d RenderText::GetAlignmentOffset() { } void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { - if (!fade_head() && !fade_tail()) + if (multiline() || (!fade_head() && !fade_tail())) return; - const int text_width = GetStringSize().width(); const int display_width = display_rect().width(); // If the text fits as-is, no need to fade. - if (text_width <= display_width) + if (GetStringSize().width() <= display_width) return; int gradient_width = CalculateFadeGradientWidth(GetPrimaryFont(), @@ -914,7 +1011,7 @@ void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { } Rect text_rect = display_rect(); - text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0); + text_rect.Inset(GetAlignmentOffset(0).x(), 0, 0, 0); // TODO(msw): Use the actual text colors corresponding to each faded part. skia::RefPtr<SkShader> shader = CreateFadeShader( @@ -929,29 +1026,30 @@ void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { } // static -bool RenderText::RangeContainsCaret(const ui::Range& range, +bool RenderText::RangeContainsCaret(const 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)); + return range.Contains(Range(caret_pos, adjacent)); } void RenderText::MoveCursorTo(size_t position, bool select) { size_t cursor = std::min(position, text().length()); if (IsCursorablePosition(cursor)) SetSelectionModel(SelectionModel( - ui::Range(select ? selection().start() : cursor, cursor), + Range(select ? selection().start() : cursor, cursor), (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD)); } void RenderText::UpdateLayoutText() { layout_text_.clear(); + line_breaks_.SetMax(0); if (obscured_) { size_t obscured_text_length = - static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length())); + static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, text_.length())); layout_text_.assign(obscured_text_length, kPasswordReplacementChar); if (obscured_reveal_index_ >= 0 && @@ -965,7 +1063,7 @@ void RenderText::UpdateLayoutText() { // Gets the index in |layout_text_| to be replaced. const size_t cp_start = - static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, start)); + static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, start)); if (layout_text_.length() > cp_start) layout_text_.replace(cp_start, 1, text_.substr(start, end - start)); } @@ -976,7 +1074,7 @@ void RenderText::UpdateLayoutText() { // Truncate the text at a valid character break and append an ellipsis. icu::StringCharacterIterator iter(text.c_str()); iter.setIndex32(truncate_length_ - 1); - layout_text_.assign(text.substr(0, iter.getIndex()) + ui::kEllipsisUTF16); + layout_text_.assign(text.substr(0, iter.getIndex()) + gfx::kEllipsisUTF16); } } @@ -984,6 +1082,8 @@ void RenderText::UpdateCachedBoundsAndOffset() { if (cached_bounds_and_offset_valid_) return; + // TODO(ckocagil): Add support for scrolling multiline text. + // First, set the valid flag true to calculate the current cursor bounds using // the stale |display_offset_|. Applying |delta_offset| at the end of this // function will set |cursor_bounds_| and |display_offset_| to correct values. |