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

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

Issue 7511029: Implement Pango RenderText for Linux. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 months 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
« ui/gfx/render_text_linux.h ('K') | « ui/gfx/render_text_linux.h ('k') | no next file » | 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>
8 #include <algorithm>
9
10 #include "base/logging.h"
11 #include "ui/gfx/canvas_skia.h"
12 #include "ui/gfx/gtk_util.h"
13 #include "unicode/uchar.h"
14
15 #define U16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
16 #define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
oshima 2011/08/19 18:44:07 space around operators
xji 2011/08/20 00:53:05 these are actually not needed. defined in include
17 // TODO(xji): massage ARGB to RGB.
18 #define COLOR_16_TO_32_BIT(c) ((c)/0xff * 0xffff)
oshima 2011/08/19 18:44:07 If this is to convert ARGB to RGB, please name acc
xji 2011/08/20 00:53:05 It is just convert R/G/B from 16 bit to 32 bit. bu
19
20 // TODO(xji): index saved in upper layer are utf16 index. Pango uses utf8 index.
21 // Since caret_pos is used internally, we could save utf8 index for caret_pos
22 // to avoid conversion.
23
7 namespace gfx { 24 namespace gfx {
8 25
9 RenderTextLinux::RenderTextLinux() 26 RenderTextLinux::RenderTextLinux()
10 : RenderText() { 27 : layout_(NULL),
28 layout_line_(NULL) {
11 } 29 }
12 30
13 RenderTextLinux::~RenderTextLinux() { 31 RenderTextLinux::~RenderTextLinux() {
32 ResetLayout();
14 } 33 }
15 34
16 RenderText* RenderText::CreateRenderText() { 35 RenderText* RenderText::CreateRenderText() {
17 return new RenderTextLinux; 36 return new RenderTextLinux;
18 } 37 }
19 38
39 int RenderTextLinux::GetStringWidth() {
40 PangoLayout* layout = EnsureLayout();
41 int width, height;
42 pango_layout_get_pixel_size(layout, &width, &height);
43 return width;
44 }
45
46 void RenderTextLinux::Draw(Canvas* canvas) {
47 PangoLayout* layout = EnsureLayout();
48 Rect bounds(display_rect());
49
50 // Clip the canvas to the text display area.
51 CanvasSkia* canvas_skia = static_cast<CanvasSkia*>(canvas);
oshima 2011/08/19 18:44:07 canvas->AsCanvasSkia()
xji 2011/08/20 00:53:05 Done.
52 canvas_skia->ClipRectInt(
53 bounds.x(), bounds.y(), bounds.width(), bounds.height());
54
55 // TODO(xji): is this Begin/EndPlatformPaint correct? How about performance?
oshima 2011/08/19 18:44:07 This seems to be fine (assuming this class replace
xji 2011/08/20 00:53:05 comments removed.
56 cairo_t* cr = skia::BeginPlatformPaint(canvas_skia);
57 cairo_save(cr);
58 cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height());
59 cairo_clip(cr);
60
61 int text_width, text_height;
62 pango_layout_get_pixel_size(layout, &text_width, &text_height);
63 // Vertically centered.
64 int text_y = bounds.y() + ((bounds.height() - text_height) / 2);
65 int text_x = bounds.x() + GetUpdatedDisplayOffset().x();
66 cairo_move_to(cr, text_x, text_y);
67 pango_cairo_show_layout(cr, layout);
68
69 // Destructor.
70 cairo_restore(cr);
71 skia::EndPlatformPaint(canvas_skia);
72
73 // Paint cursor.
74 bounds = GetUpdatedCursorBounds();
75 if (cursor_visible() && focused() && !bounds.IsEmpty()) {
76 if (!bounds.IsEmpty())
77 canvas->DrawRectInt(kCursorColor,
78 bounds.x(),
79 bounds.y(),
80 bounds.width(),
81 bounds.height());
82 }
83 }
84
85 SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) {
86 // TODO(xji): when points outside of text, return HOME/END position.
87 PangoLayout* layout = EnsureLayout();
88
89 if (text().length() == 0)
90 return SelectionModel(0, 0, SelectionModel::LEADING);
91
92 int adjusted_x =
93 point.x() - display_rect().x() - GetUpdatedDisplayOffset().x();
94 int caret_pos, trailing;
95 pango_layout_xy_to_index(layout,
96 adjusted_x * PANGO_SCALE,
97 point.y() * PANGO_SCALE,
98 &caret_pos,
99 &trailing);
100
101 size_t selection_end = caret_pos;
102 if (trailing > 0) {
103 const char* layout_text = pango_layout_get_text(layout);
104 selection_end = g_utf8_offset_to_pointer(layout_text + caret_pos, trailing)
105 - layout_text;
106 }
107
108 return SelectionModel(
109 Utf8IndexToUtf16Index(text(), selection_end),
110 Utf8IndexToUtf16Index(text(), caret_pos),
111 trailing > 0 ? SelectionModel::TRAILING : SelectionModel::LEADING);
112 }
113
114 Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection,
115 bool insert_mode) {
116 PangoLayout* layout = EnsureLayout();
117
118 PangoRectangle pos;
119 pango_layout_index_to_pos(
120 layout, Utf16IndexToUtf8Index(text(), selection.caret_pos()), &pos);
121
122 int x;
123 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
124 if (caret_placement == SelectionModel::TRAILING)
125 x = (pos.x + pos.width) / PANGO_SCALE;
126 else
127 x = pos.x / PANGO_SCALE;
128 x += display_rect().x() + GetUpdatedDisplayOffset().x();
oshima 2011/08/19 18:44:07 these can be int x = (caret_placement == Selectio
xji 2011/08/20 00:53:05 I'd prefer the old way. moderate use of tertiary i
129
130 const Font& font = default_style().font;
131 int h = std::min(display_rect().height(), font.GetHeight());
132 Rect bounds(x, (display_rect().height() - h) / 2, 1, h);
133
134 size_t position = selection.selection_end();
135 if (!insert_mode && text().length() != position)
136 AdjustBoundsForNonInsertMode(selection, pos, &bounds);
137
138 return bounds;
139 }
140
141 SelectionModel RenderTextLinux::GetLeftSelectionModel(
142 const SelectionModel& current, BreakType break_type) {
143 EnsureLayout();
144
145 if (break_type == LINE_BREAK)
146 return GetSelectionModelForVisualLeftmost();
oshima 2011/08/19 18:44:07 indent
xji 2011/08/20 00:53:05 Done.
147 if (break_type == CHARACTER_BREAK)
148 return GetLeftSelectionModelByGrapheme(current);
149 // TODO(xji): implementation.
150 return RenderText::GetLeftSelectionModel(current, break_type);
151 }
152
153 SelectionModel RenderTextLinux::GetRightSelectionModel(
154 const SelectionModel& current, BreakType break_type) {
155 EnsureLayout();
156
157 if (break_type == LINE_BREAK) {
158 const char* layout_text = pango_layout_get_text(layout_);
159 PangoDirection base_dir =
160 pango_find_base_dir(layout_text, strlen(layout_text));
161 if (base_dir == PANGO_DIRECTION_RTL ||
162 base_dir == PANGO_DIRECTION_WEAK_RTL)
163 return SelectionModel(0, 0, SelectionModel::LEADING);
164 return SelectionModel(
165 text().length(), text().length(), SelectionModel::LEADING);
166 }
167 if (break_type == CHARACTER_BREAK)
168 return GetRightSelectionModelByGrapheme(current);
169 // TODO(xji): implementation.
oshima 2011/08/19 18:44:07 can you update the comment to be more descriptive?
xji 2011/08/20 00:53:05 changed to "not implemented yet".
170 return RenderText::GetRightSelectionModel(current, break_type);
171 }
172
173 size_t RenderTextLinux::GetIndexOfPreviousGrapheme(size_t position) {
174 EnsureLayout();
175 return Utf16IndexOfAdjacentGrapheme(position, PREVIOUS);
176 }
177
178 SelectionModel RenderTextLinux::GetSelectionModelForVisualLeftmost() const {
179 const char* layout_text = pango_layout_get_text(layout_);
180 PangoDirection base_dir =
181 pango_find_base_dir(layout_text, strlen(layout_text));
182
183 if (base_dir == PANGO_DIRECTION_RTL || base_dir == PANGO_DIRECTION_WEAK_RTL) {
184 GSList* first_visual_run = layout_line_->runs;
185 if (first_visual_run) {
186 PangoItem* item =
187 reinterpret_cast<PangoLayoutRun*>(first_visual_run->data)->item;
188 if (item->analysis.level % 2 == 0) { // LTR.
189 size_t utf16_index = Utf8IndexToUtf16Index(text(), item->offset);
190 return SelectionModel(
191 text().length(), utf16_index, SelectionModel::LEADING);
192 } else { // RTL.
193 size_t utf16_index = Utf8IndexToUtf16Index(
194 text(), item->offset + item->length);
195 utf16_index = Utf16IndexOfAdjacentGrapheme(utf16_index, PREVIOUS);
196 return SelectionModel(
197 text().length(), utf16_index, SelectionModel::TRAILING);
198 }
199 }
200 }
201 return SelectionModel(0, 0, SelectionModel::LEADING);
202 }
203
204 GSList* RenderTextLinux::GetRunContainsCaretPos(const SelectionModel& current,
205 CursorMovementDirection dir,
206 bool* at_boundary) const {
207 GSList* run_contains_caret_pos = NULL;
208 *at_boundary = false;
209
210 GSList* run = layout_line_->runs;
211 while (run) {
212 PangoItem* pango_item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
213 size_t caret_pos = current.caret_pos();
214 size_t run_end_utf16_index =
215 Utf8IndexToUtf16Index(text(), pango_item->offset + pango_item->length);
216 size_t run_start_utf16_index =
217 Utf8IndexToUtf16Index(text(), pango_item->offset);
218
219 if (caret_pos >= run_start_utf16_index && caret_pos < run_end_utf16_index) {
220 run_contains_caret_pos = run;
221
222 size_t selection_end = current.selection_end();
223 SelectionModel::CaretPlacement caret_placement =
224 current.caret_placement();
225
226 if (caret_placement == SelectionModel::LEADING &&
227 caret_pos == run_start_utf16_index) {
228 *at_boundary = (dir == RIGHT && pango_item->analysis.level % 2) ||
229 (dir == LEFT && pango_item->analysis.level % 2 == 0);
230 } else if (caret_placement == SelectionModel::TRAILING &&
231 selection_end == run_end_utf16_index) {
232 *at_boundary = (dir == RIGHT && pango_item->analysis.level % 2 == 0) ||
233 (dir == LEFT && pango_item->analysis.level % 2);
234 }
235 break;
236 }
237 run = run->next;
238 }
239 return run_contains_caret_pos;
240 }
241
242 size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme(
243 size_t utf16_index_of_current_grapheme, RelativeLogicalPosition pos) const {
244 PangoLogAttr* log_attrs;
245 gint n_attrs;
246 pango_layout_get_log_attrs(layout_, &log_attrs, &n_attrs);
247
248 const char* layout_text = pango_layout_get_text(layout_);
249 const char* ch = layout_text +
250 Utf16IndexToUtf8Index(text(), utf16_index_of_current_grapheme);
251 int char_offset = static_cast<int>(g_utf8_pointer_to_offset(layout_text, ch));
252
253 if (pos == PREVIOUS) {
254 if (ch > layout_text) {
255 do {
256 ch = g_utf8_find_prev_char(layout_text, ch);
257 --char_offset;
258 } while (ch && *ch && !log_attrs[char_offset].is_cursor_position);
259 if (!ch)
260 ch = layout_text;
261 }
262 } else {
263 if (ch < layout_text + strlen(layout_text)) {
264 do {
265 ch = g_utf8_find_next_char(ch, NULL);
266 ++char_offset;
267 } while (ch && *ch && !log_attrs[char_offset].is_cursor_position);
268 if (ch >= layout_text + strlen(layout_text))
269 ch = layout_text + strlen(layout_text);
270 }
271 }
272
273 size_t utf8_index = static_cast<size_t>(ch - layout_text);
274 // TODO(xji): how costly those g_free is?
oshima 2011/08/19 18:44:07 negligible compared to other stuff we're doing.
xji 2011/08/20 00:53:05 comment removed.
275 g_free(log_attrs);
276 return utf8_index;
277 }
278
279 size_t RenderTextLinux::Utf16IndexOfAdjacentGrapheme(
280 size_t utf16_index_of_current_grapheme, RelativeLogicalPosition pos) const {
281 size_t utf8_index = Utf8IndexOfAdjacentGrapheme(
282 utf16_index_of_current_grapheme, pos);
283 return Utf8IndexToUtf16Index(text(), utf8_index);
284 }
285
286 SelectionModel RenderTextLinux::FirstSelectionModelInsideRun(
287 const PangoItem* run) const {
288 size_t utf16_index = Utf8IndexToUtf16Index(text(), run->offset);
oshima 2011/08/19 18:44:07 indnet
xji 2011/08/20 00:53:05 Done.
289 return SelectionModel(Utf16IndexOfAdjacentGrapheme(utf16_index, NEXT),
290 utf16_index,
291 SelectionModel::TRAILING);
292 }
293
294 SelectionModel RenderTextLinux::LastSelectionModelInsideRun(
295 const PangoItem* run) const {
296 size_t utf16_index = Utf8IndexToUtf16Index(
297 text(), run->offset + run->length);
oshima 2011/08/19 18:44:07 ditto
xji 2011/08/20 00:53:05 Done.
298 utf16_index = Utf16IndexOfAdjacentGrapheme(utf16_index, PREVIOUS);
299 return SelectionModel(utf16_index, utf16_index, SelectionModel::LEADING);
300 }
301
302 SelectionModel RenderTextLinux::LeftmostSelectionModelInsideRun(
303 const PangoItem* run) const {
304 if (run->analysis.level % 2 == 0)
305 return FirstSelectionModelInsideRun(run);
306 return LastSelectionModelInsideRun(run);
oshima 2011/08/19 18:44:07 can these be return run->analysis.level % 2 == 0 ?
xji 2011/08/20 00:53:05 Done.
307 }
308
309 SelectionModel RenderTextLinux::RightmostSelectionModelInsideRun(
310 const PangoItem* run) const {
311 if (run->analysis.level % 2)
312 return FirstSelectionModelInsideRun(run);
313 return LastSelectionModelInsideRun(run);
314 }
315
316 bool RenderTextLinux::GetVisuallyAdjacentCursorPositionForEnd(
317 const SelectionModel& current,
318 CursorMovementDirection dir,
319 SelectionModel* adjacent) const {
320 size_t text_length = text().length();
321 if (current.selection_end() == text_length &&
322 current.caret_pos() == text_length &&
323 current.caret_placement() == SelectionModel::LEADING) {
324 // Visually rightmost END position.
325 if (dir == LEFT) {
326 GSList* last_run = GetLastRun();
327 if (last_run) {
328 *adjacent = RightmostSelectionModelInsideRun(
329 reinterpret_cast<PangoLayoutRun*>(last_run->data)->item);
330 }
331 }
332 adjacent->set_selection_start(adjacent->selection_end());
333 return true;
334 }
335 return false;
336 }
337
338 // Assume caret_pos in |current| is n, 'l' represents leading in
339 // caret_placement and 't' represents trailing in caret_placement. Following
340 // is the calculation from (caret_pos, caret_placement) in |current| to
341 // (caret_pos, caret_placement, selection_end) when moving cursor left by
342 // one grapheme (for simplilcity, assume each grapheme is one character).
343 // If n is in LTR run,
344 // (n, t) ---> (n, l, n)
345 // (n, l) ---> (n-1, l, n-1) if n is inside run (not at boundary).
346 // (n, l) ---> goto across run case if n is at run boundary;
347 // If n is in RTL run,
348 // (n, l) --> (n, t, n+1);
349 // (n, t) --> (n+1, t n+2) if n is inside run;
350 // (n, t) --> goto across run case if n is at run boundar;
351 // If n is at run boudary, get its visually left run,
352 // if left run is LTR run,
353 // (n, t) --> (left run's end, l, left run's end);
354 // If rght run is RTL run,
355 // (n, t) --> (left run's begin, t, left run's begin + 1);
356 //
357 // TODO(xji): duplication with GetRightSelectionModelByGrapheme.
oshima 2011/08/19 18:44:07 do you mean you will consolidate two?
xji 2011/08/20 00:53:05 I am not sure whether to consolidate them. Althoug
358 SelectionModel RenderTextLinux::GetLeftSelectionModelByGrapheme(
359 const SelectionModel& current) const {
360 SelectionModel left(current);
361 if (GetVisuallyAdjacentCursorPositionForEnd(current, LEFT, &left))
362 return left;
363
364 bool at_run_boundary = false;
365 GSList* run = GetRunContainsCaretPos(current, LEFT, &at_run_boundary);
366 if (run == NULL) {
367 left.set_selection_start(left.selection_end());
368 return left;
369 }
370
371 PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
372 if (box->analysis.level % 2 == 0) { // LTR box.
373 if (current.caret_placement() == SelectionModel::TRAILING) {
374 left.set_caret_placement(SelectionModel::LEADING);
375 left.set_selection_end(left.caret_pos());
376 } else { // caret_placement == TRAILING.
377 if (at_run_boundary) {
378 GSList* prev_run = GetPreviousRun(run);
379 if (prev_run) {
380 return RightmostSelectionModelInsideRun(
381 reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item);
382 }
oshima 2011/08/19 18:44:07 nuke {}
xji 2011/08/20 00:53:05 Done.
383 } else { // !at_run_boundary.
384 size_t prev = Utf16IndexOfAdjacentGrapheme(left.caret_pos(), PREVIOUS);
385 left.set_caret_pos(prev);
386 left.set_selection_end(prev);
387 }
388 }
389 } else { // RTL box.
390 if (current.caret_placement() == SelectionModel::LEADING) {
391 left.set_caret_placement(SelectionModel::TRAILING);
392 left.set_selection_end(
393 Utf16IndexOfAdjacentGrapheme(current.caret_pos(), NEXT));
394 } else { // caret_placement == LEADING
395 if (at_run_boundary) {
396 GSList* prev_run = GetPreviousRun(run);
397 if (prev_run) {
398 return RightmostSelectionModelInsideRun(
399 reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item);
400 }
oshima 2011/08/19 18:44:07 ditto
xji 2011/08/20 00:53:05 Done.
401 } else { // !at_run_boundary
402 // current.seletion_end - current.caret_pos
403 // == NumOfCharsInGrapheme(caret_pos);
oshima 2011/08/19 18:44:07 is this comment or dead code?
xji 2011/08/20 00:53:05 it is comment, changed "==" to "equals to"
404 left.set_caret_pos(current.selection_end());
405 left.set_selection_end(
406 Utf16IndexOfAdjacentGrapheme(left.caret_pos(), NEXT));
407 }
408 }
409 }
410
411 left.set_selection_start(left.selection_end());
412 return left;
413 }
414
415 // Assume caret_pos in |current| is n, 'l' represents leading in
416 // caret_placement and 't' represents trailing in caret_placement. Following
417 // is the calculation from (caret_pos, caret_placement) in |current| to
418 // (caret_pos, caret_placement, selection_end) when moving cursor right by
419 // one grapheme (for simplilcity, assume each grapheme is one character).
420 // If n is in LTR run,
421 // (n, l) ---> (n, t, n+1)
422 // (n, t) ---> (n+1, t, n+2) if n is inside run (not at boundary).
423 // (n, t) ---> goto across run case if n is at run boundary;
424 // If n is in RTL run,
425 // (n, t) --> (n, l, n);
426 // (n, l) --> (n-1, l, n-1) if n is inside run;
427 // (n, l) --> goto across run case if n is at run boundar;
428 // If n is at run boudary, get its visually right run,
429 // if right run is LTR run,
430 // (n, t) --> (right run's begin, t, right run's begin + 1);
431 // If rght run is RTL run,
432 // (n, t) --> (right run's end, l, right run's end);
433 SelectionModel RenderTextLinux::GetRightSelectionModelByGrapheme(
434 const SelectionModel& current) const {
435 SelectionModel right(current);
436 if (GetVisuallyAdjacentCursorPositionForEnd(current, RIGHT, &right))
437 return right;
438
439 bool at_run_boundary = false;
440 GSList* run = GetRunContainsCaretPos(current, RIGHT, &at_run_boundary);
441 if (run == NULL) {
442 right.set_selection_start(right.selection_end());
443 return right;
444 }
445
446 PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
447 if (box->analysis.level % 2 == 0) { // LTR box.
448 if (current.caret_placement() == SelectionModel::LEADING) {
449 right.set_caret_placement(SelectionModel::TRAILING);
450 right.set_selection_end(
451 Utf16IndexOfAdjacentGrapheme(current.caret_pos(), NEXT));
452 } else { // caret_placement == TRAILING.
453 if (at_run_boundary) {
454 if (run->next) {
455 return LeftmostSelectionModelInsideRun(
456 reinterpret_cast<PangoLayoutRun*>(run->next->data)->item);
457 }
458 } else { // !at_run_boundary.
459 // current.seletion_end - current.caret_pos
460 // == NumOfCharsInGrapheme(caret_pos);
461 right.set_caret_pos(current.selection_end());
462 right.set_selection_end(
463 Utf16IndexOfAdjacentGrapheme(right.caret_pos(), NEXT));
464 }
465 }
466 } else { // RTL box.
467 if (current.caret_placement() == SelectionModel::TRAILING) {
468 right.set_caret_placement(SelectionModel::LEADING);
469 right.set_selection_end(right.caret_pos());
470 } else { // caret_placement == LEADING
471 if (at_run_boundary) {
472 if (run->next) {
473 return LeftmostSelectionModelInsideRun(
474 reinterpret_cast<PangoLayoutRun*>(run->next->data)->item);
475 }
476 } else { // !at_run_boundary
477 // current.seletion_end - current.caret_pos
478 // == NumOfCharsInGrapheme(caret_pos);
479 size_t prev = Utf16IndexOfAdjacentGrapheme(right.caret_pos(), PREVIOUS);
480 right.set_caret_pos(prev);
481 right.set_selection_end(prev);
482 }
483 }
484 }
485
486 right.set_selection_start(right.selection_end());
487 return right;
488 }
489
490 PangoLayout* RenderTextLinux::EnsureLayout() {
491 if (layout_ == NULL) {
492 CanvasSkia canvas(display_rect().width(), display_rect().height(), false);
493 // TODO(xji): how costly is the Begin/EndPlatformPaint? Can I get cairo
494 // context in another way?
495 cairo_t* cr = skia::BeginPlatformPaint(&canvas);
496
497 layout_ = pango_cairo_create_layout(cr);
498 SetupPangoLayout(layout_, text(), default_style().font,
499 display_rect().width(), GetTextDirection(),
500 CanvasSkia::DefaultCanvasTextAlignment());
501 pango_layout_set_height(layout_, display_rect().height() * PANGO_SCALE);
502 SetupPangoAttributes(layout_);
503
504 skia::EndPlatformPaint(&canvas);
505
506 layout_line_ = pango_layout_get_line_readonly(layout_, 0);
507 }
508 return layout_;
509 }
510
511 void RenderTextLinux::ResetLayout() {
512 // set_cached_bounds_and_offset_valid(false) is done in RenderText for every
513 // operation that trigger ResetLayout().
514 if (layout_) {
515 g_object_unref(layout_);
516 layout_ = NULL;
517 }
518 if (layout_line_) {
519 // TODO(xji): do I need ref/unref?
520 // pango_layout_line_unref(layout_line_);
oshima 2011/08/19 18:44:07 you may want to run test with valgrind bots. (linu
xji 2011/08/20 00:53:05 submitted.
521 layout_line_ = NULL;
522 }
523 }
524
525 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) {
526 PangoAttrList* attrs = pango_attr_list_new();
527 // Set selection background color.
528 SkColor selection_color =
529 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
530 PangoAttribute* pango_attr = pango_attr_background_new(
531 COLOR_16_TO_32_BIT(SkColorGetR(selection_color)),
532 COLOR_16_TO_32_BIT(SkColorGetG(selection_color)),
533 COLOR_16_TO_32_BIT(SkColorGetB(selection_color)));
534 AppendPangoAttribute(MinOfSelection(), MaxOfSelection(),
535 pango_attr, attrs);
536
537 StyleRanges ranges_of_style(style_ranges());
538 ApplyCompositionAndSelectionStyles(&ranges_of_style);
539
540 for (StyleRanges::const_iterator i = ranges_of_style.begin();
541 i < ranges_of_style.end(); ++i) {
542 const Font& font = !i->underline ? i->font :
543 i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED);
544 pango_attr = pango_attr_font_desc_new(
545 reinterpret_cast<PangoFontDescription*>(font.GetNativeFont()));
546 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
547
548 SkColor foreground = i->foreground;
549 pango_attr = pango_attr_foreground_new(
550 COLOR_16_TO_32_BIT(SkColorGetR(foreground)),
551 COLOR_16_TO_32_BIT(SkColorGetG(foreground)),
552 COLOR_16_TO_32_BIT(SkColorGetB(foreground)));
553 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
554
555 if (i->strike) {
556 pango_attr = pango_attr_strikethrough_new(true);
557 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
558 }
559 }
560
561 pango_layout_set_attributes(layout, attrs);
562 pango_attr_list_unref(attrs);
563 }
564
565 void RenderTextLinux::AppendPangoAttribute(size_t start,
566 size_t end,
567 PangoAttribute* pango_attr,
568 PangoAttrList* attrs) {
569 pango_attr->start_index = Utf16IndexToUtf8Index(text(), start);
570 pango_attr->end_index = Utf16IndexToUtf8Index(text(), end);
571 pango_attr_list_insert(attrs, pango_attr);
572 }
573
574 GSList* RenderTextLinux::GetPreviousRun(GSList* run) const {
575 GSList* current = layout_line_->runs;
576 GSList* prev = NULL;
577 while (current) {
578 if (current == run)
579 return prev;
580 prev = current;
581 current = current->next;
582 }
583 return NULL;
584 }
585
586 GSList* RenderTextLinux::GetLastRun() const {
587 GSList* current = layout_line_->runs;
588 while (current && current->next) {
589 current = current->next;
590 }
591 return current;
592 }
593
594 size_t RenderTextLinux::Utf16IndexToUtf8Index(const string16& text,
595 size_t index) const {
oshima 2011/08/19 18:44:07 so there is no library which does this?
xji 2011/08/20 00:53:05 there is no ICU library does that directly without
596 int utf8_index = 0;
597 for (size_t i = 0; i < index; ++i) {
598 if (text[i] < 0x80) {
599 ++utf8_index;
600 } else if (text[i] < 0x800) {
601 utf8_index += 2;
602 } else {
603 if ((U16_IS_LEAD(text[i]))) {
604 if (i + 1 < index) {
605 if (U16_IS_TRAIL(text[i + 1])) {
606 // A surrogate pair.
607 utf8_index += 4;
608 ++i;
609 continue;
610 }
611 } else if (i + 1 < text.length() && U16_IS_TRAIL(text[i + 1])) {
612 // A split surrogate pair, stop at the leading.
613 break;
614 }
615 }
616 utf8_index += 3;
617 }
618 }
619 return utf8_index;
620 }
621
622
623 size_t RenderTextLinux::Utf8IndexToUtf16Index(const string16& text,
624 size_t index) const {
625 // TODO(xji): DUP with utf16IndexToUtf8Index.
626 size_t utf8_index = 0;
627 size_t utf16_index = 0;
628 for (; utf16_index < text.length(); ++utf16_index) {
629 if (text[utf16_index] < 0x80) {
630 ++utf8_index;
631 } else if (text[utf16_index] < 0x800) {
632 utf8_index += 2;
633 } else {
634 if ((U16_IS_LEAD(text[utf16_index]))) {
635 if (utf16_index + 1 < text.length()) {
636 if (U16_IS_TRAIL(text[utf16_index + 1])) {
637 // A surrogate pair.
638 utf8_index += 4;
639 if (utf8_index >= index)
640 break;
641 ++utf16_index;
642 continue;
643 }
644 }
645 }
646 utf8_index += 3;
647 }
648 if (utf8_index > index)
649 break;
650 }
651 return utf16_index;
652 }
653
654 void RenderTextLinux::AdjustBoundsForNonInsertMode(
655 const SelectionModel& selection,
656 const PangoRectangle& pos,
657 Rect* bounds) const {
658 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
659 if (caret_placement == SelectionModel::LEADING) {
660 if (pos.width > 0) {
661 bounds->set_width(pos.width);
662 } else {
663 bounds->set_x(pos.x + pos.width);
664 bounds->set_width(-pos.width);
665 }
666 } else {
667 // TODO(xji): what is the expected behavior? For example, "abcFED",
668 // when |selection| == SelectionModel(3, 2, TRAILING), in insert-mode,
669 // cursor is at right of 'c'. In non-insert-mode, is the bounds around 'D'?
670 }
671 }
672
20 } // namespace gfx 673 } // namespace gfx
OLDNEW
« ui/gfx/render_text_linux.h ('K') | « ui/gfx/render_text_linux.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698