diff options
author | xji@chromium.org <xji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-30 10:18:42 +0000 |
---|---|---|
committer | xji@chromium.org <xji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-30 10:18:42 +0000 |
commit | 6002a4f38fa52c3d5ffafff7af5c80b3620f2b69 (patch) | |
tree | 55db03775c4538e277df212413f6acce28513c8d /ui | |
parent | cb66c5afe08ffd1bc5e5a3602e14bad192d79c31 (diff) | |
download | chromium_src-6002a4f38fa52c3d5ffafff7af5c80b3620f2b69.zip chromium_src-6002a4f38fa52c3d5ffafff7af5c80b3620f2b69.tar.gz chromium_src-6002a4f38fa52c3d5ffafff7af5c80b3620f2b69.tar.bz2 |
Separate selection highlight from pango layout, highlight selection using skia.
Cache substring bounds to avoid unnecessary g_free. Consolidate drawing functions with RenderTextWin. Remove UpdateLayout() from RenderText::SetSelectionModel().
BUG=103647
TEST=TextfieldViewModelTest; Manual test selection highlight on bidi text.
(Do not really know how to test "fi" ligature part. But since the selection
highlight is not done by using pango attribute, I would assume that solves the problem).
Review URL: http://codereview.chromium.org/8536047
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112188 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/gfx/render_text.cc | 121 | ||||
-rw-r--r-- | ui/gfx/render_text.h | 39 | ||||
-rw-r--r-- | ui/gfx/render_text_linux.cc | 232 | ||||
-rw-r--r-- | ui/gfx/render_text_linux.h | 24 | ||||
-rw-r--r-- | ui/gfx/render_text_win.cc | 177 | ||||
-rw-r--r-- | ui/gfx/render_text_win.h | 13 |
6 files changed, 294 insertions, 312 deletions
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc index c3d9b6f..696a4f0 100644 --- a/ui/gfx/render_text.cc +++ b/ui/gfx/render_text.cc @@ -123,6 +123,8 @@ 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)); + + UpdateLayout(); } void RenderText::ToggleInsertMode() { @@ -344,57 +346,13 @@ int RenderText::GetStringWidth() { } void RenderText::Draw(Canvas* canvas) { - // Clip the canvas to the text display area. - canvas->ClipRect(display_rect_); - - // Draw the selection. - std::vector<Rect> selection(GetSubstringBounds(GetSelectionStart(), - GetCursorPosition())); - SkColor selection_color = - focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; - for (std::vector<Rect>::const_iterator i = selection.begin(); - i < selection.end(); ++i) { - Rect r(*i); - canvas->FillRect(selection_color, r); - } - - // Create a temporary copy of the style ranges for composition and selection. - StyleRanges style_ranges(style_ranges_); - ApplyCompositionAndSelectionStyles(&style_ranges); - - // Draw the text. - Rect bounds(display_rect_); - bounds.Offset(GetUpdatedDisplayOffset()); - for (StyleRanges::const_iterator i = style_ranges.begin(); - i < style_ranges.end(); ++i) { - const Font& font = !i->underline ? i->font : - i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED); - string16 text = text_.substr(i->range.start(), i->range.length()); - bounds.set_width(font.GetStringWidth(text)); - canvas->DrawStringInt(text, font, i->foreground, bounds); - - // Draw the strikethrough. - if (i->strike) { - SkPaint paint; - paint.setAntiAlias(true); - paint.setStyle(SkPaint::kFill_Style); - paint.setColor(i->foreground); - paint.setStrokeWidth(kStrikeWidth); - canvas->GetSkCanvas()->drawLine(SkIntToScalar(bounds.x()), - SkIntToScalar(bounds.bottom()), - SkIntToScalar(bounds.right()), - SkIntToScalar(bounds.y()), - paint); - } + EnsureLayout(); - bounds.set_x(bounds.x() + bounds.width()); + if (!text().empty()) { + DrawSelection(canvas); + DrawVisualText(canvas); } - - // Paint cursor. Replace cursor is drawn as rectangle for now. - Rect cursor(GetUpdatedCursorBounds()); - if (cursor_visible() && focused()) - canvas->DrawRectInt(kCursorColor, cursor.x(), cursor.y(), - cursor.width(), cursor.height()); + DrawCursor(canvas); } SelectionModel RenderText::FindCursorPosition(const Point& point) { @@ -426,13 +384,6 @@ SelectionModel RenderText::FindCursorPosition(const Point& point) { return SelectionModel(left_pos); } -Rect RenderText::GetCursorBounds(const SelectionModel& selection, - bool insert_mode) { - size_t from = selection.selection_end(); - size_t to = insert_mode ? from : std::min(text_.length(), from + 1); - return GetSubstringBounds(from, to)[0]; -} - const Rect& RenderText::GetUpdatedCursorBounds() { UpdateCachedBoundsAndOffset(); return cursor_bounds_; @@ -549,22 +500,21 @@ SelectionModel RenderText::RightEndSelectionModel() { return SelectionModel(cursor, caret_pos, placement); } -size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) { - return IndexOfAdjacentGrapheme(position, false); +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(text().length(), static_cast<size_t>(1))); + selection_model_.set_caret_pos(model.caret_pos()); + selection_model_.set_caret_placement(model.caret_placement()); + + cached_bounds_and_offset_valid_ = false; } -std::vector<Rect> RenderText::GetSubstringBounds(size_t from, size_t to) { - size_t start = std::min(from, to); - size_t end = std::max(from, to); - const Font& font = default_style_.font; - int start_x = font.GetStringWidth(text().substr(0, start)); - int end_x = font.GetStringWidth(text().substr(0, end)); - Rect rect(start_x, 0, end_x - start_x, font.GetHeight()); - rect.Offset(display_rect_.origin()); - rect.Offset(GetUpdatedDisplayOffset()); - // Center the rect vertically in |display_rect_|. - rect.Offset(Point(0, (display_rect_.height() - rect.height()) / 2)); - return std::vector<Rect>(1, rect); +size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) { + return IndexOfAdjacentGrapheme(position, false); } void RenderText::ApplyCompositionAndSelectionStyles( @@ -605,20 +555,6 @@ Point RenderText::ToViewPoint(const Point& point) { return p; } -void RenderText::SetSelectionModel(const SelectionModel& selection_model) { - DCHECK_LE(selection_model.selection_start(), text().length()); - selection_model_.set_selection_start(selection_model.selection_start()); - DCHECK_LE(selection_model.selection_end(), text().length()); - selection_model_.set_selection_end(selection_model.selection_end()); - DCHECK_LT(selection_model.caret_pos(), - std::max(text().length(), static_cast<size_t>(1))); - selection_model_.set_caret_pos(selection_model.caret_pos()); - selection_model_.set_caret_placement(selection_model.caret_placement()); - - cached_bounds_and_offset_valid_ = false; - UpdateLayout(); -} - void RenderText::MoveCursorTo(size_t position, bool select) { size_t cursor = std::min(position, text().length()); size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); @@ -664,4 +600,21 @@ void RenderText::UpdateCachedBoundsAndOffset() { cursor_bounds_.Offset(delta_offset, 0); } +void RenderText::DrawSelection(Canvas* canvas) { + std::vector<Rect> sel; + GetSubstringBounds(GetSelectionStart(), GetCursorPosition(), &sel); + 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) { + // 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()) { + Rect r(GetUpdatedCursorBounds()); + canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height()); + } +} + } // namespace gfx diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h index 6b39d16..da3607d 100644 --- a/ui/gfx/render_text.h +++ b/ui/gfx/render_text.h @@ -21,9 +21,6 @@ namespace gfx { -// Strike line width. -const int kStrikeWidth = 2; - // Color settings for text, backgrounds and cursor. // These are tentative, and should be derived from theme, system // settings and current settings. @@ -172,7 +169,7 @@ class UI_EXPORT RenderText { // 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 Rect GetCursorBounds(const SelectionModel& selection, - bool insert_mode); + bool insert_mode) = 0; // Compute the current cursor bounds, panning the text to show the cursor in // the display rect if necessary. These bounds are in local coordinates. @@ -209,25 +206,36 @@ class UI_EXPORT RenderText { virtual SelectionModel LeftEndSelectionModel(); virtual SelectionModel RightEndSelectionModel(); - // Get the logical index of the grapheme preceeding the argument |position|. - virtual size_t GetIndexOfPreviousGrapheme(size_t position); + // 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|. 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. + // |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. - // TODO(msw) Re-evaluate this function's necessity and signature. - virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to); + virtual void GetSubstringBounds(size_t from, + size_t to, + std::vector<Rect>* bounds) = 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. virtual bool IsCursorablePosition(size_t position) = 0; - // Updates the layout so that the next draw request can correctly + // Update the layout so that the next draw request can correctly // render the text and its attributes. virtual void UpdateLayout() = 0; + // Ensure the text is laid out. + virtual void EnsureLayout() = 0; + + // Draw the text. + virtual void DrawVisualText(Canvas* canvas) = 0; + + // Get the logical index of the grapheme preceding the argument |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) const; @@ -249,9 +257,6 @@ class UI_EXPORT RenderText { // The return value is bounded by 0 and the text length, inclusive. virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) = 0; - // Sets the selection model, the argument is assumed to be valid. - void SetSelectionModel(const SelectionModel& selection_model); - // Set the cursor to |position|, with the caret trailing the previous // grapheme, or if there is no previous grapheme, leading the cursor position. // If |select| is false, the selection start is moved to the same position. @@ -263,6 +268,10 @@ class UI_EXPORT RenderText { // cursor is within the visible display area. void UpdateCachedBoundsAndOffset(); + // Draw the selection and cursor. + void DrawSelection(Canvas* canvas); + void DrawCursor(Canvas* canvas); + // Logical UTF-16 string data to be drawn. string16 text_; diff --git a/ui/gfx/render_text_linux.cc b/ui/gfx/render_text_linux.cc index ab6e725..e2f0d67 100644 --- a/ui/gfx/render_text_linux.cc +++ b/ui/gfx/render_text_linux.cc @@ -5,7 +5,6 @@ #include "ui/gfx/render_text_linux.h" #include <pango/pangocairo.h> - #include <algorithm> #include "base/i18n/break_iterator.h" @@ -58,48 +57,14 @@ base::i18n::TextDirection RenderTextLinux::GetTextDirection() { } int RenderTextLinux::GetStringWidth() { - PangoLayout* layout = EnsureLayout(); + EnsureLayout(); int width; - pango_layout_get_pixel_size(layout, &width, NULL); + pango_layout_get_pixel_size(layout_, &width, NULL); return width; } -void RenderTextLinux::Draw(Canvas* canvas) { - PangoLayout* layout = EnsureLayout(); - Rect bounds(display_rect()); - - // Clip the canvas to the text display area. - SkCanvas* canvas_skia = canvas->GetSkCanvas(); - - skia::ScopedPlatformPaint scoped_platform_paint(canvas_skia); - cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); - cairo_save(cr); - cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height()); - cairo_clip(cr); - - int text_width, text_height; - pango_layout_get_pixel_size(layout, &text_width, &text_height); - Point offset(ToViewPoint(Point())); - // Vertically centered. - int text_y = offset.y() + ((bounds.height() - text_height) / 2); - // TODO(xji): need to use SkCanvas->drawPosText() for gpu acceleration. - cairo_move_to(cr, offset.x(), text_y); - pango_cairo_show_layout(cr, layout); - - cairo_restore(cr); - - // Paint cursor. - bounds = GetUpdatedCursorBounds(); - if (cursor_visible() && focused()) - canvas->DrawRectInt(kCursorColor, - bounds.x(), - bounds.y(), - bounds.width(), - bounds.height()); -} - SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) { - PangoLayout* layout = EnsureLayout(); + EnsureLayout(); if (text().empty()) return SelectionModel(0, 0, SelectionModel::LEADING); @@ -113,7 +78,7 @@ SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) { return RightEndSelectionModel(); int caret_pos, trailing; - pango_layout_xy_to_index(layout, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE, + pango_layout_xy_to_index(layout_, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE, &caret_pos, &trailing); size_t selection_end = caret_pos; @@ -133,12 +98,12 @@ SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) { Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection, bool insert_mode) { - PangoLayout* layout = EnsureLayout(); + EnsureLayout(); size_t caret_pos = insert_mode ? selection.caret_pos() : selection.selection_end(); PangoRectangle pos; - pango_layout_index_to_pos(layout, Utf16IndexToUtf8Index(caret_pos), &pos); + pango_layout_index_to_pos(layout_, Utf16IndexToUtf8Index(caret_pos), &pos); SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); int x = pos.x; @@ -220,6 +185,33 @@ SelectionModel RenderTextLinux::RightEndSelectionModel() { return SelectionModel(0, 0, SelectionModel::LEADING); } +void RenderTextLinux::SetSelectionModel(const SelectionModel& model) { + if (GetSelectionStart() != model.selection_start() || + GetCursorPosition() != model.selection_end()) { + selection_visual_bounds_.clear(); + } + + RenderText::SetSelectionModel(model); +} + +void RenderTextLinux::GetSubstringBounds(size_t from, + size_t to, + std::vector<Rect>* bounds) { + DCHECK(from <= text().length()); + DCHECK(to <= text().length()); + + bounds->clear(); + if (from == to) + return; + + EnsureLayout(); + + if (from == GetSelectionStart() && to == GetCursorPosition()) + GetSelectionBounds(bounds); + else + CalculateSubstringBounds(from, to, bounds); +} + bool RenderTextLinux::IsCursorablePosition(size_t position) { if (position == 0 && text().empty()) return true; @@ -233,6 +225,64 @@ void RenderTextLinux::UpdateLayout() { ResetLayout(); } +void RenderTextLinux::EnsureLayout() { + if (layout_ == NULL) { + CanvasSkia canvas(display_rect().width(), display_rect().height(), false); + skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas()); + cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); + + layout_ = pango_cairo_create_layout(cr); + SetupPangoLayout( + layout_, + text(), + default_style().font, + display_rect().width(), + base::i18n::GetFirstStrongCharacterDirection(text()), + CanvasSkia::DefaultCanvasTextAlignment()); + + // No width set so that the x-axis position is relative to the start of the + // text. ToViewPoint and ToTextPoint take care of the position conversion + // between text space and view spaces. + pango_layout_set_width(layout_, -1); + // TODO(xji): If RenderText will be used for displaying purpose, such as + // label, we will need to remove the single-line-mode setting. + pango_layout_set_single_paragraph_mode(layout_, true); + SetupPangoAttributes(layout_); + + current_line_ = pango_layout_get_line_readonly(layout_, 0); + pango_layout_line_ref(current_line_); + + pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_); + + layout_text_ = pango_layout_get_text(layout_); + layout_text_len_ = strlen(layout_text_); + } +} + +void RenderTextLinux::DrawVisualText(Canvas* canvas) { + Rect bounds(display_rect()); + + // Clip the canvas to the text display area. + SkCanvas* canvas_skia = canvas->GetSkCanvas(); + + skia::ScopedPlatformPaint scoped_platform_paint(canvas_skia); + cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); + cairo_save(cr); + cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height()); + cairo_clip(cr); + + int text_width, text_height; + pango_layout_get_pixel_size(layout_, &text_width, &text_height); + Point offset(ToViewPoint(Point())); + // Vertically centered. + int text_y = offset.y() + ((bounds.height() - text_height) / 2); + // TODO(xji): need to use SkCanvas->drawPosText() for gpu acceleration. + cairo_move_to(cr, offset.x(), text_y); + pango_cairo_show_layout(cr, layout_); + + cairo_restore(cr); +} + size_t RenderTextLinux::IndexOfAdjacentGrapheme(size_t index, bool next) { EnsureLayout(); return Utf16IndexOfAdjacentGrapheme(Utf16IndexToUtf8Index(index), next); @@ -474,41 +524,6 @@ SelectionModel RenderTextLinux::RightSelectionModelByWord( return right_end; } -PangoLayout* RenderTextLinux::EnsureLayout() { - if (layout_ == NULL) { - CanvasSkia canvas(display_rect().width(), display_rect().height(), false); - skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas()); - cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); - - layout_ = pango_cairo_create_layout(cr); - SetupPangoLayout( - layout_, - text(), - default_style().font, - display_rect().width(), - base::i18n::GetFirstStrongCharacterDirection(text()), - CanvasSkia::DefaultCanvasTextAlignment()); - - // No width set so that the x-axis position is relative to the start of the - // text. ToViewPoint and ToTextPoint take care of the position conversion - // between text space and view spaces. - pango_layout_set_width(layout_, -1); - // TODO(xji): If RenderText will be used for displaying purpose, such as - // label, we will need to remove the single-line-mode setting. - pango_layout_set_single_paragraph_mode(layout_, true); - SetupPangoAttributes(layout_); - - current_line_ = pango_layout_get_line_readonly(layout_, 0); - pango_layout_line_ref(current_line_); - - pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_); - - layout_text_ = pango_layout_get_text(layout_); - layout_text_len_ = strlen(layout_text_); - } - return layout_; -} - void RenderTextLinux::ResetLayout() { // set_cached_bounds_and_offset_valid(false) is done in RenderText for every // operation that triggers ResetLayout(). @@ -525,44 +540,25 @@ void RenderTextLinux::ResetLayout() { log_attrs_ = NULL; num_log_attrs_ = 0; } + if (!selection_visual_bounds_.empty()) + selection_visual_bounds_.clear(); layout_text_ = NULL; layout_text_len_ = 0; } void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { PangoAttrList* attrs = pango_attr_list_new(); - // Set selection background color. - // TODO(xji): There's a bug in pango that it can't use two colors in one - // glyph. Please refer to https://bugzilla.gnome.org/show_bug.cgi?id=648157 - // for detail. So for example, if a font has "ffi" ligature, but you select - // half of that glyph, you either get the entire "ffi" ligature - // selection-colored, or none of it. - // We could use clipping to render selection. - // Use pango_glyph_item_get_logical_widths to find the exact boundaries of - // selection, then cairo_clip that, paint background, set color to white and - // redraw the layout. - SkColor selection_color = - focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; - size_t start = std::min(MinOfSelection(), text().length()); - size_t end = std::min(MaxOfSelection(), text().length()); - PangoAttribute* pango_attr; - if (end > start) { - pango_attr = pango_attr_background_new( - ConvertColorFrom8BitTo16Bit(SkColorGetR(selection_color)), - ConvertColorFrom8BitTo16Bit(SkColorGetG(selection_color)), - ConvertColorFrom8BitTo16Bit(SkColorGetB(selection_color))); - AppendPangoAttribute(start, end, pango_attr, attrs); - } StyleRanges ranges_of_style(style_ranges()); ApplyCompositionAndSelectionStyles(&ranges_of_style); PlatformFont* default_platform_font = default_style().font.platform_font(); + PangoAttribute* pango_attr; for (StyleRanges::const_iterator i = ranges_of_style.begin(); i < ranges_of_style.end(); ++i) { - start = std::min(i->range.start(), text().length()); - end = std::min(i->range.end(), text().length()); + size_t start = std::min(i->range.start(), text().length()); + size_t end = std::min(i->range.end(), text().length()); if (start >= end) continue; @@ -657,4 +653,40 @@ size_t RenderTextLinux::Utf8IndexToUtf16Index(size_t index) const { return utf16_index; } +void RenderTextLinux::CalculateSubstringBounds(size_t from, + size_t to, + std::vector<Rect>* bounds) { + int* ranges; + int n_ranges; + size_t from_in_utf8 = Utf16IndexToUtf8Index(from); + size_t to_in_utf8 = Utf16IndexToUtf8Index(to); + pango_layout_line_get_x_ranges( + current_line_, + std::min(from_in_utf8, to_in_utf8), + std::max(from_in_utf8, to_in_utf8), + &ranges, + &n_ranges); + + int height; + pango_layout_get_pixel_size(layout_, NULL, &height); + + int y = (display_rect().height() - height) / 2; + + 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); + } + g_free(ranges); +} + +void RenderTextLinux::GetSelectionBounds(std::vector<Rect>* bounds) { + if (selection_visual_bounds_.empty()) + CalculateSubstringBounds(GetSelectionStart(), GetCursorPosition(), + &selection_visual_bounds_); + *bounds = selection_visual_bounds_; +} + } // namespace gfx diff --git a/ui/gfx/render_text_linux.h b/ui/gfx/render_text_linux.h index b473d9d..462494d 100644 --- a/ui/gfx/render_text_linux.h +++ b/ui/gfx/render_text_linux.h @@ -7,6 +7,7 @@ #pragma once #include <pango/pango.h> +#include <vector> #include "ui/gfx/render_text.h" @@ -21,7 +22,6 @@ class RenderTextLinux : public RenderText { // Overridden from RenderText: virtual base::i18n::TextDirection GetTextDirection() OVERRIDE; virtual int GetStringWidth() OVERRIDE; - virtual void Draw(Canvas* canvas) OVERRIDE; virtual SelectionModel FindCursorPosition(const Point& point) OVERRIDE; virtual Rect GetCursorBounds(const SelectionModel& position, bool insert_mode) OVERRIDE; @@ -34,8 +34,14 @@ class RenderTextLinux : public RenderText { 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 void SetSelectionModel(const SelectionModel& model) OVERRIDE; virtual bool IsCursorablePosition(size_t position) OVERRIDE; virtual void UpdateLayout() OVERRIDE; + virtual void EnsureLayout() OVERRIDE; + virtual void DrawVisualText(Canvas* canvas) OVERRIDE; private: virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) OVERRIDE; @@ -69,10 +75,6 @@ class RenderTextLinux : public RenderText { SelectionModel LeftSelectionModelByWord(const SelectionModel& current); SelectionModel RightSelectionModelByWord(const SelectionModel& current); - // If |layout_| is NULL, create and setup |layout_|, retain and ref - // |current_line_|. Return |layout_|. - PangoLayout* EnsureLayout(); - // Unref |layout_| and |pango_line_|. Set them to NULL. void ResetLayout(); @@ -96,6 +98,15 @@ class RenderTextLinux : public RenderText { 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); + + // Save the visual bounds of logical selection into |bounds|. + void GetSelectionBounds(std::vector<Rect>* bounds); + // Pango Layout. PangoLayout* layout_; // A single line layout resulting from laying out via |layout_|. @@ -106,6 +117,9 @@ class RenderTextLinux : public RenderText { // Number of attributes in |log_attrs_|. int num_log_attrs_; + // Vector of the visual bounds containing the logical substring of selection. + std::vector<Rect> selection_visual_bounds_; + // The text in the |layout_|. const char* layout_text_; // The text length. diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc index e5c64d0..3fcd856 100644 --- a/ui/gfx/render_text_win.cc +++ b/ui/gfx/render_text_win.cc @@ -173,13 +173,6 @@ int RenderTextWin::GetStringWidth() { return string_width_; } -void RenderTextWin::Draw(Canvas* canvas) { - EnsureLayout(); - DrawSelection(canvas); - DrawVisualText(canvas); - DrawCursor(canvas); -} - SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { if (text().empty()) return SelectionModel(); @@ -319,14 +312,19 @@ SelectionModel RenderTextWin::RightEndSelectionModel() { return SelectionModel(cursor, caret, placement); } -std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) { +void RenderTextWin::GetSubstringBounds(size_t from, + size_t to, + std::vector<Rect>* bounds) { DCHECK(!needs_layout_); ui::Range range(from, to); DCHECK(ui::Range(0, text().length()).Contains(range)); Point display_offset(GetUpdatedDisplayOffset()); - std::vector<Rect> bounds; HRESULT hr = 0; + bounds->clear(); + if (from == to) + return; + // Add a Rect for each run/selection intersection. // TODO(msw): The bounds should probably not always be leading the range ends. for (size_t i = 0; i < runs_.size(); ++i) { @@ -364,14 +362,13 @@ std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) { 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; } bool RenderTextWin::IsCursorablePosition(size_t position) { @@ -396,6 +393,72 @@ void RenderTextWin::UpdateLayout() { needs_layout_ = true; } +void RenderTextWin::EnsureLayout() { + if (!needs_layout_) + return; + // TODO(msw): Skip complex processing if ScriptIsComplex returns false. + ItemizeLogicalText(); + if (!runs_.empty()) + LayoutVisualText(); + needs_layout_ = false; +} + +void RenderTextWin::DrawVisualText(Canvas* canvas) { + SkCanvas* canvas_skia = canvas->GetSkCanvas(); + + Point offset(ToViewPoint(Point())); + // TODO(msw): Establish a vertical baseline for strings of mixed font heights. + size_t height = default_style().font.GetHeight(); + + SkScalar x = SkIntToScalar(offset.x()); + SkScalar y = SkIntToScalar(offset.y()); + // Center the text vertically in the display area. + y += (display_rect().height() - height) / 2; + // Offset by the font size to account for Skia expecting y to be the bottom. + y += default_style().font.GetFontSize(); + + SkPaint paint; + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setStyle(SkPaint::kFill_Style); + paint.setAntiAlias(true); + paint.setSubpixelText(true); + paint.setLCDRenderText(true); + + std::vector<SkPoint> pos; + for (size_t i = 0; i < runs_.size(); ++i) { + // Get the run specified by the visual-to-logical map. + internal::TextRun* run = runs_[visual_to_logical_[i]]; + + // TODO(msw): Font default/fallback and style integration. + SkTypeface::Style style = SkTypeface::kNormal; + SkTypeface* typeface = + SkTypeface::CreateFromName(run->font.GetFontName().c_str(), style); + if (typeface) { + paint.setTypeface(typeface); + // |paint| adds its own ref. Release the ref from CreateFromName. + typeface->unref(); + } + paint.setTextSize(run->font.GetFontSize()); + paint.setColor(run->foreground); + + SkScalar run_x = x; + + // Based on WebCore::skiaDrawText. + pos.resize(run->glyph_count); + for (int glyph = 0; glyph < run->glyph_count; glyph++) { + pos[glyph].set(x + run->offsets[glyph].du, + y + run->offsets[glyph].dv); + x += SkIntToScalar(run->advance_widths[glyph]); + } + + size_t byte_length = run->glyph_count * sizeof(WORD); + canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint); + + if (run->strike || run->underline) + DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y); + } +} + size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { EnsureLayout(); size_t run_index = GetRunContainingPosition(index); @@ -416,16 +479,6 @@ size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { return std::max(std::min(ch, length) + start, 0); } -void RenderTextWin::EnsureLayout() { - if (!needs_layout_) - return; - // TODO(msw): Skip complex processing if ScriptIsComplex returns false. - ItemizeLogicalText(); - if (!runs_.empty()) - LayoutVisualText(); - needs_layout_ = false; -} - void RenderTextWin::ItemizeLogicalText() { STLDeleteContainerPointers(runs_.begin(), runs_.end()); runs_.clear(); @@ -690,82 +743,6 @@ SelectionModel RenderTextWin::RightSelectionModel( FirstSelectionModelInsideRun(next); } -void RenderTextWin::DrawSelection(Canvas* canvas) { - 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 RenderTextWin::DrawVisualText(Canvas* canvas) { - if (text().empty()) - return; - - SkCanvas* canvas_skia = canvas->GetSkCanvas(); - - Point offset(ToViewPoint(Point())); - // TODO(msw): Establish a vertical baseline for strings of mixed font heights. - size_t height = default_style().font.GetHeight(); - - SkScalar x = SkIntToScalar(offset.x()); - SkScalar y = SkIntToScalar(offset.y()); - // Center the text vertically in the display area. - y += (display_rect().height() - height) / 2; - // Offset by the font size to account for Skia expecting y to be the bottom. - y += default_style().font.GetFontSize(); - - SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setStyle(SkPaint::kFill_Style); - paint.setAntiAlias(true); - paint.setSubpixelText(true); - paint.setLCDRenderText(true); - - std::vector<SkPoint> pos; - for (size_t i = 0; i < runs_.size(); ++i) { - // Get the run specified by the visual-to-logical map. - internal::TextRun* run = runs_[visual_to_logical_[i]]; - - // TODO(msw): Font default/fallback and style integration. - SkTypeface::Style style = SkTypeface::kNormal; - SkTypeface* typeface = - SkTypeface::CreateFromName(run->font.GetFontName().c_str(), style); - if (typeface) { - paint.setTypeface(typeface); - // |paint| adds its own ref. Release the ref from CreateFromName. - typeface->unref(); - } - paint.setTextSize(run->font.GetFontSize()); - paint.setColor(run->foreground); - - SkScalar run_x = x; - - // Based on WebCore::skiaDrawText. - pos.resize(run->glyph_count); - for (int glyph = 0; glyph < run->glyph_count; glyph++) { - pos[glyph].set(x + run->offsets[glyph].du, - y + run->offsets[glyph].dv); - x += SkIntToScalar(run->advance_widths[glyph]); - } - - size_t byte_length = run->glyph_count * sizeof(WORD); - canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint); - - if (run->strike || run->underline) - DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y); - } -} - -void RenderTextWin::DrawCursor(Canvas* canvas) { - // 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()) { - Rect r(GetUpdatedCursorBounds()); - canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height()); - } -} - RenderText* RenderText::CreateRenderText() { return new RenderTextWin; } diff --git a/ui/gfx/render_text_win.h b/ui/gfx/render_text_win.h index 1711d7b..5733760 100644 --- a/ui/gfx/render_text_win.h +++ b/ui/gfx/render_text_win.h @@ -60,7 +60,6 @@ class RenderTextWin : public RenderText { // Overridden from RenderText: virtual int GetStringWidth() OVERRIDE; - virtual void Draw(Canvas* canvas) OVERRIDE; virtual SelectionModel FindCursorPosition(const Point& point) OVERRIDE; virtual Rect GetCursorBounds(const SelectionModel& selection, bool insert_mode) OVERRIDE; @@ -73,14 +72,17 @@ class RenderTextWin : public RenderText { BreakType break_type) OVERRIDE; virtual SelectionModel LeftEndSelectionModel() OVERRIDE; virtual SelectionModel RightEndSelectionModel() OVERRIDE; - virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to) OVERRIDE; + virtual void GetSubstringBounds(size_t from, + size_t to, + std::vector<Rect>* bounds) OVERRIDE; virtual bool IsCursorablePosition(size_t position) OVERRIDE; virtual void UpdateLayout() OVERRIDE; + virtual void EnsureLayout() OVERRIDE; + virtual void DrawVisualText(Canvas* canvas) OVERRIDE; private: virtual size_t IndexOfAdjacentGrapheme(size_t index, bool next) OVERRIDE; - void EnsureLayout(); void ItemizeLogicalText(); void LayoutVisualText(); @@ -100,11 +102,6 @@ class RenderTextWin : public RenderText { SelectionModel LeftSelectionModel(const SelectionModel& selection); SelectionModel RightSelectionModel(const SelectionModel& selection); - // Draw the text, cursor, and selection. - void DrawSelection(Canvas* canvas); - void DrawVisualText(Canvas* canvas); - void DrawCursor(Canvas* canvas); - // National Language Support native digit and digit substitution settings. SCRIPT_DIGITSUBSTITUTE digit_substitute_; |