Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(600)

Side by Side Diff: ui/gfx/render_text_linux.cc

Issue 8536047: Separate selection highlight from pango layout (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: address comments Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ui/gfx/render_text_linux.h ('k') | ui/gfx/render_text_win.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/render_text_linux.h" 5 #include "ui/gfx/render_text_linux.h"
6 6
7 #include <pango/pangocairo.h> 7 #include <pango/pangocairo.h>
8
9 #include <algorithm> 8 #include <algorithm>
10 9
11 #include "base/i18n/break_iterator.h" 10 #include "base/i18n/break_iterator.h"
12 #include "base/logging.h" 11 #include "base/logging.h"
13 #include "ui/gfx/canvas_skia.h" 12 #include "ui/gfx/canvas_skia.h"
14 #include "ui/gfx/pango_util.h" 13 #include "ui/gfx/pango_util.h"
15 #include "unicode/uchar.h" 14 #include "unicode/uchar.h"
16 #include "unicode/ustring.h" 15 #include "unicode/ustring.h"
17 16
18 namespace { 17 namespace {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 base::i18n::TextDirection RenderTextLinux::GetTextDirection() { 50 base::i18n::TextDirection RenderTextLinux::GetTextDirection() {
52 EnsureLayout(); 51 EnsureLayout();
53 52
54 PangoDirection base_dir = pango_find_base_dir(layout_text_, -1); 53 PangoDirection base_dir = pango_find_base_dir(layout_text_, -1);
55 if (base_dir == PANGO_DIRECTION_RTL || base_dir == PANGO_DIRECTION_WEAK_RTL) 54 if (base_dir == PANGO_DIRECTION_RTL || base_dir == PANGO_DIRECTION_WEAK_RTL)
56 return base::i18n::RIGHT_TO_LEFT; 55 return base::i18n::RIGHT_TO_LEFT;
57 return base::i18n::LEFT_TO_RIGHT; 56 return base::i18n::LEFT_TO_RIGHT;
58 } 57 }
59 58
60 int RenderTextLinux::GetStringWidth() { 59 int RenderTextLinux::GetStringWidth() {
61 PangoLayout* layout = EnsureLayout(); 60 EnsureLayout();
62 int width; 61 int width;
63 pango_layout_get_pixel_size(layout, &width, NULL); 62 pango_layout_get_pixel_size(layout_, &width, NULL);
64 return width; 63 return width;
65 } 64 }
66 65
67 void RenderTextLinux::Draw(Canvas* canvas) {
68 PangoLayout* layout = EnsureLayout();
69 Rect bounds(display_rect());
70
71 // Clip the canvas to the text display area.
72 SkCanvas* canvas_skia = canvas->GetSkCanvas();
73
74 skia::ScopedPlatformPaint scoped_platform_paint(canvas_skia);
75 cairo_t* cr = scoped_platform_paint.GetPlatformSurface();
76 cairo_save(cr);
77 cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height());
78 cairo_clip(cr);
79
80 int text_width, text_height;
81 pango_layout_get_pixel_size(layout, &text_width, &text_height);
82 Point offset(ToViewPoint(Point()));
83 // Vertically centered.
84 int text_y = offset.y() + ((bounds.height() - text_height) / 2);
85 // TODO(xji): need to use SkCanvas->drawPosText() for gpu acceleration.
86 cairo_move_to(cr, offset.x(), text_y);
87 pango_cairo_show_layout(cr, layout);
88
89 cairo_restore(cr);
90
91 // Paint cursor.
92 bounds = GetUpdatedCursorBounds();
93 if (cursor_visible() && focused())
94 canvas->DrawRectInt(kCursorColor,
95 bounds.x(),
96 bounds.y(),
97 bounds.width(),
98 bounds.height());
99 }
100
101 SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) { 66 SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) {
102 PangoLayout* layout = EnsureLayout(); 67 EnsureLayout();
103 68
104 if (text().empty()) 69 if (text().empty())
105 return SelectionModel(0, 0, SelectionModel::LEADING); 70 return SelectionModel(0, 0, SelectionModel::LEADING);
106 71
107 Point p(ToTextPoint(point)); 72 Point p(ToTextPoint(point));
108 73
109 // When the point is outside of text, return HOME/END position. 74 // When the point is outside of text, return HOME/END position.
110 if (p.x() < 0) 75 if (p.x() < 0)
111 return LeftEndSelectionModel(); 76 return LeftEndSelectionModel();
112 else if (p.x() > GetStringWidth()) 77 else if (p.x() > GetStringWidth())
113 return RightEndSelectionModel(); 78 return RightEndSelectionModel();
114 79
115 int caret_pos, trailing; 80 int caret_pos, trailing;
116 pango_layout_xy_to_index(layout, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE, 81 pango_layout_xy_to_index(layout_, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE,
117 &caret_pos, &trailing); 82 &caret_pos, &trailing);
118 83
119 size_t selection_end = caret_pos; 84 size_t selection_end = caret_pos;
120 if (trailing > 0) { 85 if (trailing > 0) {
121 const char* ch = g_utf8_offset_to_pointer(layout_text_ + caret_pos, 86 const char* ch = g_utf8_offset_to_pointer(layout_text_ + caret_pos,
122 trailing); 87 trailing);
123 DCHECK_GE(ch, layout_text_); 88 DCHECK_GE(ch, layout_text_);
124 DCHECK_LE(ch, layout_text_ + layout_text_len_); 89 DCHECK_LE(ch, layout_text_ + layout_text_len_);
125 selection_end = ch - layout_text_; 90 selection_end = ch - layout_text_;
126 } 91 }
127 92
128 return SelectionModel( 93 return SelectionModel(
129 Utf8IndexToUtf16Index(selection_end), 94 Utf8IndexToUtf16Index(selection_end),
130 Utf8IndexToUtf16Index(caret_pos), 95 Utf8IndexToUtf16Index(caret_pos),
131 trailing > 0 ? SelectionModel::TRAILING : SelectionModel::LEADING); 96 trailing > 0 ? SelectionModel::TRAILING : SelectionModel::LEADING);
132 } 97 }
133 98
134 Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection, 99 Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection,
135 bool insert_mode) { 100 bool insert_mode) {
136 PangoLayout* layout = EnsureLayout(); 101 EnsureLayout();
137 102
138 size_t caret_pos = insert_mode ? selection.caret_pos() : 103 size_t caret_pos = insert_mode ? selection.caret_pos() :
139 selection.selection_end(); 104 selection.selection_end();
140 PangoRectangle pos; 105 PangoRectangle pos;
141 pango_layout_index_to_pos(layout, Utf16IndexToUtf8Index(caret_pos), &pos); 106 pango_layout_index_to_pos(layout_, Utf16IndexToUtf8Index(caret_pos), &pos);
142 107
143 SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); 108 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
144 int x = pos.x; 109 int x = pos.x;
145 if ((insert_mode && caret_placement == SelectionModel::TRAILING) || 110 if ((insert_mode && caret_placement == SelectionModel::TRAILING) ||
146 (!insert_mode && pos.width < 0)) 111 (!insert_mode && pos.width < 0))
147 x += pos.width; 112 x += pos.width;
148 x = PANGO_PIXELS(x); 113 x = PANGO_PIXELS(x);
149 114
150 int h = std::min(display_rect().height(), PANGO_PIXELS(pos.height)); 115 int h = std::min(display_rect().height(), PANGO_PIXELS(pos.height));
151 Rect bounds(x, (display_rect().height() - h) / 2, 0, h); 116 Rect bounds(x, (display_rect().height() - h) / 2, 0, h);
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 return SelectionModel(text().length(), caret, SelectionModel::TRAILING); 178 return SelectionModel(text().length(), caret, SelectionModel::TRAILING);
214 } else { // RTL. 179 } else { // RTL.
215 size_t caret = Utf8IndexToUtf16Index(item->offset); 180 size_t caret = Utf8IndexToUtf16Index(item->offset);
216 return SelectionModel(text().length(), caret, SelectionModel::LEADING); 181 return SelectionModel(text().length(), caret, SelectionModel::LEADING);
217 } 182 }
218 } 183 }
219 } 184 }
220 return SelectionModel(0, 0, SelectionModel::LEADING); 185 return SelectionModel(0, 0, SelectionModel::LEADING);
221 } 186 }
222 187
188 void RenderTextLinux::SetSelectionModel(const SelectionModel& model) {
189 if (GetSelectionStart() != model.selection_start() ||
190 GetCursorPosition() != model.selection_end()) {
191 selection_visual_bounds_.clear();
192 }
193
194 RenderText::SetSelectionModel(model);
195 }
196
197 void RenderTextLinux::GetSubstringBounds(size_t from,
198 size_t to,
199 std::vector<Rect>* bounds) {
200 DCHECK(from <= text().length());
201 DCHECK(to <= text().length());
202
203 bounds->clear();
204 if (from == to)
205 return;
206
207 EnsureLayout();
208
209 if (from == GetSelectionStart() && to == GetCursorPosition())
210 GetSelectionBounds(bounds);
211 else
212 CalculateSubstringBounds(from, to, bounds);
213 }
214
223 bool RenderTextLinux::IsCursorablePosition(size_t position) { 215 bool RenderTextLinux::IsCursorablePosition(size_t position) {
224 if (position == 0 && text().empty()) 216 if (position == 0 && text().empty())
225 return true; 217 return true;
226 218
227 EnsureLayout(); 219 EnsureLayout();
228 return (position < static_cast<size_t>(num_log_attrs_) && 220 return (position < static_cast<size_t>(num_log_attrs_) &&
229 log_attrs_[position].is_cursor_position); 221 log_attrs_[position].is_cursor_position);
230 } 222 }
231 223
232 void RenderTextLinux::UpdateLayout() { 224 void RenderTextLinux::UpdateLayout() {
233 ResetLayout(); 225 ResetLayout();
234 } 226 }
235 227
228 void RenderTextLinux::EnsureLayout() {
229 if (layout_ == NULL) {
230 CanvasSkia canvas(display_rect().width(), display_rect().height(), false);
231 skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
232 cairo_t* cr = scoped_platform_paint.GetPlatformSurface();
233
234 layout_ = pango_cairo_create_layout(cr);
235 SetupPangoLayout(
236 layout_,
237 text(),
238 default_style().font,
239 display_rect().width(),
240 base::i18n::GetFirstStrongCharacterDirection(text()),
241 CanvasSkia::DefaultCanvasTextAlignment());
242
243 // No width set so that the x-axis position is relative to the start of the
244 // text. ToViewPoint and ToTextPoint take care of the position conversion
245 // between text space and view spaces.
246 pango_layout_set_width(layout_, -1);
247 // TODO(xji): If RenderText will be used for displaying purpose, such as
248 // label, we will need to remove the single-line-mode setting.
249 pango_layout_set_single_paragraph_mode(layout_, true);
250 SetupPangoAttributes(layout_);
251
252 current_line_ = pango_layout_get_line_readonly(layout_, 0);
253 pango_layout_line_ref(current_line_);
254
255 pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_);
256
257 layout_text_ = pango_layout_get_text(layout_);
258 layout_text_len_ = strlen(layout_text_);
259 }
260 }
261
262 void RenderTextLinux::DrawVisualText(Canvas* canvas) {
263 Rect bounds(display_rect());
264
265 // Clip the canvas to the text display area.
266 SkCanvas* canvas_skia = canvas->GetSkCanvas();
267
268 skia::ScopedPlatformPaint scoped_platform_paint(canvas_skia);
269 cairo_t* cr = scoped_platform_paint.GetPlatformSurface();
270 cairo_save(cr);
271 cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height());
272 cairo_clip(cr);
273
274 int text_width, text_height;
275 pango_layout_get_pixel_size(layout_, &text_width, &text_height);
276 Point offset(ToViewPoint(Point()));
277 // Vertically centered.
278 int text_y = offset.y() + ((bounds.height() - text_height) / 2);
279 // TODO(xji): need to use SkCanvas->drawPosText() for gpu acceleration.
280 cairo_move_to(cr, offset.x(), text_y);
281 pango_cairo_show_layout(cr, layout_);
282
283 cairo_restore(cr);
284 }
285
236 size_t RenderTextLinux::IndexOfAdjacentGrapheme(size_t index, bool next) { 286 size_t RenderTextLinux::IndexOfAdjacentGrapheme(size_t index, bool next) {
237 EnsureLayout(); 287 EnsureLayout();
238 return Utf16IndexOfAdjacentGrapheme(Utf16IndexToUtf8Index(index), next); 288 return Utf16IndexOfAdjacentGrapheme(Utf16IndexToUtf8Index(index), next);
239 } 289 }
240 290
241 GSList* RenderTextLinux::GetRunContainingPosition(size_t position) const { 291 GSList* RenderTextLinux::GetRunContainingPosition(size_t position) const {
242 GSList* run = current_line_->runs; 292 GSList* run = current_line_->runs;
243 while (run) { 293 while (run) {
244 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; 294 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
245 size_t run_start = Utf8IndexToUtf16Index(item->offset); 295 size_t run_start = Utf8IndexToUtf16Index(item->offset);
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
467 return right; 517 return right;
468 } else { // RTL run. 518 } else { // RTL run.
469 if (iter.IsStartOfWord(cursor)) 519 if (iter.IsStartOfWord(cursor))
470 return right; 520 return right;
471 } 521 }
472 } 522 }
473 523
474 return right_end; 524 return right_end;
475 } 525 }
476 526
477 PangoLayout* RenderTextLinux::EnsureLayout() {
478 if (layout_ == NULL) {
479 CanvasSkia canvas(display_rect().width(), display_rect().height(), false);
480 skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
481 cairo_t* cr = scoped_platform_paint.GetPlatformSurface();
482
483 layout_ = pango_cairo_create_layout(cr);
484 SetupPangoLayout(
485 layout_,
486 text(),
487 default_style().font,
488 display_rect().width(),
489 base::i18n::GetFirstStrongCharacterDirection(text()),
490 CanvasSkia::DefaultCanvasTextAlignment());
491
492 // No width set so that the x-axis position is relative to the start of the
493 // text. ToViewPoint and ToTextPoint take care of the position conversion
494 // between text space and view spaces.
495 pango_layout_set_width(layout_, -1);
496 // TODO(xji): If RenderText will be used for displaying purpose, such as
497 // label, we will need to remove the single-line-mode setting.
498 pango_layout_set_single_paragraph_mode(layout_, true);
499 SetupPangoAttributes(layout_);
500
501 current_line_ = pango_layout_get_line_readonly(layout_, 0);
502 pango_layout_line_ref(current_line_);
503
504 pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_);
505
506 layout_text_ = pango_layout_get_text(layout_);
507 layout_text_len_ = strlen(layout_text_);
508 }
509 return layout_;
510 }
511
512 void RenderTextLinux::ResetLayout() { 527 void RenderTextLinux::ResetLayout() {
513 // set_cached_bounds_and_offset_valid(false) is done in RenderText for every 528 // set_cached_bounds_and_offset_valid(false) is done in RenderText for every
514 // operation that triggers ResetLayout(). 529 // operation that triggers ResetLayout().
515 if (layout_) { 530 if (layout_) {
516 g_object_unref(layout_); 531 g_object_unref(layout_);
517 layout_ = NULL; 532 layout_ = NULL;
518 } 533 }
519 if (current_line_) { 534 if (current_line_) {
520 pango_layout_line_unref(current_line_); 535 pango_layout_line_unref(current_line_);
521 current_line_ = NULL; 536 current_line_ = NULL;
522 } 537 }
523 if (log_attrs_) { 538 if (log_attrs_) {
524 g_free(log_attrs_); 539 g_free(log_attrs_);
525 log_attrs_ = NULL; 540 log_attrs_ = NULL;
526 num_log_attrs_ = 0; 541 num_log_attrs_ = 0;
527 } 542 }
543 if (!selection_visual_bounds_.empty())
544 selection_visual_bounds_.clear();
528 layout_text_ = NULL; 545 layout_text_ = NULL;
529 layout_text_len_ = 0; 546 layout_text_len_ = 0;
530 } 547 }
531 548
532 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { 549 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) {
533 PangoAttrList* attrs = pango_attr_list_new(); 550 PangoAttrList* attrs = pango_attr_list_new();
534 // Set selection background color.
535 // TODO(xji): There's a bug in pango that it can't use two colors in one
536 // glyph. Please refer to https://bugzilla.gnome.org/show_bug.cgi?id=648157
537 // for detail. So for example, if a font has "ffi" ligature, but you select
538 // half of that glyph, you either get the entire "ffi" ligature
539 // selection-colored, or none of it.
540 // We could use clipping to render selection.
541 // Use pango_glyph_item_get_logical_widths to find the exact boundaries of
542 // selection, then cairo_clip that, paint background, set color to white and
543 // redraw the layout.
544 SkColor selection_color =
545 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
546 size_t start = std::min(MinOfSelection(), text().length());
547 size_t end = std::min(MaxOfSelection(), text().length());
548 PangoAttribute* pango_attr;
549 if (end > start) {
550 pango_attr = pango_attr_background_new(
551 ConvertColorFrom8BitTo16Bit(SkColorGetR(selection_color)),
552 ConvertColorFrom8BitTo16Bit(SkColorGetG(selection_color)),
553 ConvertColorFrom8BitTo16Bit(SkColorGetB(selection_color)));
554 AppendPangoAttribute(start, end, pango_attr, attrs);
555 }
556 551
557 StyleRanges ranges_of_style(style_ranges()); 552 StyleRanges ranges_of_style(style_ranges());
558 ApplyCompositionAndSelectionStyles(&ranges_of_style); 553 ApplyCompositionAndSelectionStyles(&ranges_of_style);
559 554
560 PlatformFont* default_platform_font = default_style().font.platform_font(); 555 PlatformFont* default_platform_font = default_style().font.platform_font();
561 556
557 PangoAttribute* pango_attr;
562 for (StyleRanges::const_iterator i = ranges_of_style.begin(); 558 for (StyleRanges::const_iterator i = ranges_of_style.begin();
563 i < ranges_of_style.end(); ++i) { 559 i < ranges_of_style.end(); ++i) {
564 start = std::min(i->range.start(), text().length()); 560 size_t start = std::min(i->range.start(), text().length());
565 end = std::min(i->range.end(), text().length()); 561 size_t end = std::min(i->range.end(), text().length());
566 if (start >= end) 562 if (start >= end)
567 continue; 563 continue;
568 564
569 const Font& font = i->font; 565 const Font& font = i->font;
570 // In Pango, different fonts means different runs, and it breaks Arabic 566 // In Pango, different fonts means different runs, and it breaks Arabic
571 // shaping acorss run boundaries. So, set font only when it is different 567 // shaping acorss run boundaries. So, set font only when it is different
572 // from the default faont. 568 // from the default faont.
573 // TODO(xji): we'll eventually need to split up StyleRange into components 569 // TODO(xji): we'll eventually need to split up StyleRange into components
574 // (ColorRange, FontRange, etc.) so that we can combine adjacent ranges 570 // (ColorRange, FontRange, etc.) so that we can combine adjacent ranges
575 // with the same Fonts (to avoid unnecessarily splitting up runs) 571 // with the same Fonts (to avoid unnecessarily splitting up runs)
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
650 646
651 size_t RenderTextLinux::Utf8IndexToUtf16Index(size_t index) const { 647 size_t RenderTextLinux::Utf8IndexToUtf16Index(size_t index) const {
652 int32_t utf16_index = 0; 648 int32_t utf16_index = 0;
653 UErrorCode ec = U_ZERO_ERROR; 649 UErrorCode ec = U_ZERO_ERROR;
654 u_strFromUTF8(NULL, 0, &utf16_index, layout_text_, index, &ec); 650 u_strFromUTF8(NULL, 0, &utf16_index, layout_text_, index, &ec);
655 DCHECK(ec == U_BUFFER_OVERFLOW_ERROR || 651 DCHECK(ec == U_BUFFER_OVERFLOW_ERROR ||
656 ec == U_STRING_NOT_TERMINATED_WARNING); 652 ec == U_STRING_NOT_TERMINATED_WARNING);
657 return utf16_index; 653 return utf16_index;
658 } 654 }
659 655
656 void RenderTextLinux::CalculateSubstringBounds(size_t from,
657 size_t to,
658 std::vector<Rect>* bounds) {
659 int* ranges;
660 int n_ranges;
661 size_t from_in_utf8 = Utf16IndexToUtf8Index(from);
662 size_t to_in_utf8 = Utf16IndexToUtf8Index(to);
663 pango_layout_line_get_x_ranges(
664 current_line_,
665 std::min(from_in_utf8, to_in_utf8),
666 std::max(from_in_utf8, to_in_utf8),
667 &ranges,
668 &n_ranges);
669
670 int height;
671 pango_layout_get_pixel_size(layout_, NULL, &height);
672
673 int y = (display_rect().height() - height) / 2;
674
675 for (int i = 0; i < n_ranges; ++i) {
676 int x = PANGO_PIXELS(ranges[2 * i]);
677 int width = PANGO_PIXELS(ranges[2 * i + 1]) - x;
678 Rect rect(x, y, width, height);
679 rect.set_origin(ToViewPoint(rect.origin()));
680 bounds->push_back(rect);
681 }
682 g_free(ranges);
683 }
684
685 void RenderTextLinux::GetSelectionBounds(std::vector<Rect>* bounds) {
686 if (selection_visual_bounds_.empty())
687 CalculateSubstringBounds(GetSelectionStart(), GetCursorPosition(),
688 &selection_visual_bounds_);
689 *bounds = selection_visual_bounds_;
690 }
691
660 } // namespace gfx 692 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_linux.h ('k') | ui/gfx/render_text_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698