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

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: fix cursor bounds for RTL UI 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)
msw 2011/08/19 23:16:59 Make these functions instead of pre-processor dire
xji 2011/08/22 23:57:28 removed. already defined in include in uchar.h
16 #define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
17 // TODO(xji): massage ARGB to RGB.
18 #define COLOR_16_TO_32_BIT(c) ((c)/0xff * 0xffff)
19
20 // TODO(xji): index saved in upper layer are utf16 index. Pango uses utf8 index.
msw 2011/08/19 23:16:59 That's unfortunate, I believe Uniscribe is UTF16,
xji 2011/08/22 23:57:28 I looked at those Uniscibe functions you used. In
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);
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?
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().empty())
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)
msw 2011/08/19 23:16:59 Yuck! GTK? We're trying to remove all GTK; is ther
xji 2011/08/22 23:57:28 those from glib or glib-object are fine from oshim
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(
msw 2011/08/19 23:16:59 why not just call pango_layout_index_to_pos on sel
xji 2011/08/22 23:57:28 Done.
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);
126 else
127 x = pos.x;
128
129 if (pango_layout_get_alignment(layout_) == PANGO_ALIGN_RIGHT)
msw 2011/08/19 23:16:59 Can you comment on why this is offsetting by one?
xji 2011/08/22 23:57:28 Done.
130 x -= 1;
131 x = x / PANGO_SCALE;
132 x += display_rect().x() + GetUpdatedDisplayOffset().x();
133
134 const Font& font = default_style().font;
135 int h = std::min(display_rect().height(), font.GetHeight());
msw 2011/08/19 23:16:59 Is the PangoRectangle's height inaccurate? Otherwi
xji 2011/08/22 23:57:28 Ah, yes, you are right. I can use pos.height. I di
msw 2011/08/23 08:01:01 Good. FYI someone could set a different font/size
136 Rect bounds(x, (display_rect().height() - h) / 2, 1, h);
msw 2011/08/19 23:16:59 It looks like a width of 0 is okay for rendering a
xji 2011/08/22 23:57:28 Done.
137
138 size_t position = selection.selection_end();
139 if (!insert_mode && text().length() != position)
140 AdjustBoundsForNonInsertMode(selection, pos, &bounds);
141
142 return bounds;
143 }
144
145 SelectionModel RenderTextLinux::GetLeftSelectionModel(
146 const SelectionModel& current, BreakType break_type) {
msw 2011/08/19 23:16:59 Put each argument on its own line if the whole sig
xji 2011/08/22 23:57:28 Done.
147 EnsureLayout();
148
149 if (break_type == LINE_BREAK)
150 return GetSelectionModelForVisualLeftmost();
151 if (break_type == CHARACTER_BREAK)
152 return GetLeftSelectionModelByGrapheme(current);
153 // TODO(xji): implementation.
154 return RenderText::GetLeftSelectionModel(current, break_type);
msw 2011/08/19 23:16:59 Sigh, word break is breaking our backs.
155 }
156
157 SelectionModel RenderTextLinux::GetRightSelectionModel(
158 const SelectionModel& current, BreakType break_type) {
msw 2011/08/19 23:16:59 Ditto for arguments on separate lines.
xji 2011/08/22 23:57:28 Done.
159 EnsureLayout();
160
161 if (break_type == LINE_BREAK) {
162 const char* layout_text = pango_layout_get_text(layout_);
msw 2011/08/19 23:16:59 FYI, You might find it easier to implement somethi
xji 2011/08/22 23:57:28 Done.
163 PangoDirection base_dir =
164 pango_find_base_dir(layout_text, strlen(layout_text));
msw 2011/08/19 23:16:59 Maybe add a TODO like my // TODO(msw): Implement R
xji 2011/08/22 23:57:28 I've changed it to use GetTextDirection(). it retu
165 if (base_dir == PANGO_DIRECTION_RTL ||
166 base_dir == PANGO_DIRECTION_WEAK_RTL)
167 return SelectionModel(0, 0, SelectionModel::LEADING);
168 return SelectionModel(
169 text().length(), text().length(), SelectionModel::LEADING);
msw 2011/08/19 23:16:59 Perhaps we should discuss the merits of using text
xji 2011/08/22 23:57:28 I've changed to your implementation.
170 }
171 if (break_type == CHARACTER_BREAK)
172 return GetRightSelectionModelByGrapheme(current);
173 // TODO(xji): implementation.
174 return RenderText::GetRightSelectionModel(current, break_type);
175 }
176
177 size_t RenderTextLinux::GetIndexOfPreviousGrapheme(size_t position) {
178 EnsureLayout();
179 return Utf16IndexOfAdjacentGrapheme(position, PREVIOUS);
180 }
181
182 SelectionModel RenderTextLinux::GetSelectionModelForVisualLeftmost() const {
183 const char* layout_text = pango_layout_get_text(layout_);
msw 2011/08/19 23:16:59 Do you need to EnsureLayout();?
xji 2011/08/22 23:57:28 this one is a private function, and it is always c
msw 2011/08/23 08:01:01 I think you're okay.
184 PangoDirection base_dir =
185 pango_find_base_dir(layout_text, strlen(layout_text));
186
187 if (base_dir == PANGO_DIRECTION_RTL || base_dir == PANGO_DIRECTION_WEAK_RTL) {
188 GSList* first_visual_run = layout_line_->runs;
189 if (first_visual_run) {
190 PangoItem* item =
191 reinterpret_cast<PangoLayoutRun*>(first_visual_run->data)->item;
msw 2011/08/19 23:16:59 As far as I can tell, PangoLayoutRun != PangoItem:
xji 2011/08/22 23:57:28 |item| is assigned to PangoLayoutRun->item.
msw 2011/08/23 08:01:01 Doh, thanks for clarifying!
192 if (item->analysis.level % 2 == 0) { // LTR.
193 size_t utf16_index = Utf8IndexToUtf16Index(text(), item->offset);
194 return SelectionModel(
195 text().length(), utf16_index, SelectionModel::LEADING);
196 } else { // RTL.
197 size_t utf16_index = Utf8IndexToUtf16Index(
198 text(), item->offset + item->length);
msw 2011/08/19 23:16:59 Should this be item->length - 1? Or I guess gettin
xji 2011/08/22 23:57:28 Yes.
msw 2011/08/23 08:01:01 Function definition/declaration is all on one line
199 utf16_index = Utf16IndexOfAdjacentGrapheme(utf16_index, PREVIOUS);
200 return SelectionModel(
201 text().length(), utf16_index, SelectionModel::TRAILING);
msw 2011/08/19 23:16:59 Ditto for moving "text().length()," to the line ab
xji 2011/08/22 23:57:28 Done.
202 }
203 }
204 }
205 return SelectionModel(0, 0, SelectionModel::LEADING);
206 }
207
208 GSList* RenderTextLinux::GetRunContainsCaretPos(const SelectionModel& current,
209 CursorMovementDirection dir,
210 bool* at_boundary) const {
211 GSList* run_contains_caret_pos = NULL;
212 *at_boundary = false;
213
214 GSList* run = layout_line_->runs;
215 while (run) {
216 PangoItem* pango_item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
xji 2011/08/22 23:57:28 ditto
217 size_t caret_pos = current.caret_pos();
218 size_t run_end_utf16_index =
219 Utf8IndexToUtf16Index(text(), pango_item->offset + pango_item->length);
220 size_t run_start_utf16_index =
221 Utf8IndexToUtf16Index(text(), pango_item->offset);
222
223 if (caret_pos >= run_start_utf16_index && caret_pos < run_end_utf16_index) {
224 run_contains_caret_pos = run;
225
226 size_t selection_end = current.selection_end();
227 SelectionModel::CaretPlacement caret_placement =
228 current.caret_placement();
229
230 if (caret_placement == SelectionModel::LEADING &&
231 caret_pos == run_start_utf16_index) {
232 *at_boundary = (dir == RIGHT && pango_item->analysis.level % 2) ||
233 (dir == LEFT && pango_item->analysis.level % 2 == 0);
234 } else if (caret_placement == SelectionModel::TRAILING &&
235 selection_end == run_end_utf16_index) {
msw 2011/08/19 23:16:59 Why are we checking selection_end instead of caret
xji 2011/08/22 23:57:28 I am checking selecion_end == run_end, you are che
236 *at_boundary = (dir == RIGHT && pango_item->analysis.level % 2 == 0) ||
237 (dir == LEFT && pango_item->analysis.level % 2);
238 }
239 break;
240 }
241 run = run->next;
242 }
243 return run_contains_caret_pos;
244 }
245
246 size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme(
247 size_t utf16_index_of_current_grapheme, RelativeLogicalPosition pos) const {
msw 2011/08/19 23:16:59 Ditto for argument lines.
xji 2011/08/22 23:57:28 Done.
248 PangoLogAttr* log_attrs;
249 gint n_attrs;
250 pango_layout_get_log_attrs(layout_, &log_attrs, &n_attrs);
251
252 const char* layout_text = pango_layout_get_text(layout_);
253 const char* ch = layout_text +
254 Utf16IndexToUtf8Index(text(), utf16_index_of_current_grapheme);
255 int char_offset = static_cast<int>(g_utf8_pointer_to_offset(layout_text, ch));
msw 2011/08/19 23:16:59 Yuck, more GTK!
256
257 if (pos == PREVIOUS) {
258 if (ch > layout_text) {
259 do {
260 ch = g_utf8_find_prev_char(layout_text, ch);
msw 2011/08/19 23:16:59 Yuck, more GTK!
261 --char_offset;
262 } while (ch && *ch && !log_attrs[char_offset].is_cursor_position);
263 if (!ch)
264 ch = layout_text;
265 }
266 } else {
267 if (ch < layout_text + strlen(layout_text)) {
268 do {
269 ch = g_utf8_find_next_char(ch, NULL);
msw 2011/08/19 23:16:59 Yuck, more GTK!
270 ++char_offset;
271 } while (ch && *ch && !log_attrs[char_offset].is_cursor_position);
272 if (ch >= layout_text + strlen(layout_text))
msw 2011/08/19 23:16:59 Could |ch| be null here (noting your check above).
xji 2011/08/22 23:57:28 pango text should be null terminated. I do not thi
273 ch = layout_text + strlen(layout_text);
274 }
275 }
276
277 size_t utf8_index = static_cast<size_t>(ch - layout_text);
278 // TODO(xji): how costly those g_free is?
279 g_free(log_attrs);
msw 2011/08/19 23:16:59 Yuck, more GTK!
280 return utf8_index;
281 }
282
283 size_t RenderTextLinux::Utf16IndexOfAdjacentGrapheme(
284 size_t utf16_index_of_current_grapheme, RelativeLogicalPosition pos) const {
msw 2011/08/19 23:16:59 Ditto for argument lines.
xji 2011/08/22 23:57:28 Done.
285 size_t utf8_index = Utf8IndexOfAdjacentGrapheme(
286 utf16_index_of_current_grapheme, pos);
287 return Utf8IndexToUtf16Index(text(), utf8_index);
288 }
289
290 SelectionModel RenderTextLinux::FirstSelectionModelInsideRun(
291 const PangoItem* run) const {
292 size_t utf16_index = Utf8IndexToUtf16Index(text(), run->offset);
msw 2011/08/19 23:16:59 Out-dent the function contents two spaces.
xji 2011/08/22 23:57:28 Done.
293 return SelectionModel(Utf16IndexOfAdjacentGrapheme(utf16_index, NEXT),
msw 2011/08/19 23:16:59 Can you split this up onto two lines like you did
xji 2011/08/22 23:57:28 Done.
294 utf16_index,
295 SelectionModel::TRAILING);
296 }
297
298 SelectionModel RenderTextLinux::LastSelectionModelInsideRun(
299 const PangoItem* run) const {
300 size_t utf16_index = Utf8IndexToUtf16Index(
msw 2011/08/19 23:16:59 Out-dent the function contents two spaces.
xji 2011/08/22 23:57:28 Done.
301 text(), run->offset + run->length);
302 utf16_index = Utf16IndexOfAdjacentGrapheme(utf16_index, PREVIOUS);
303 return SelectionModel(utf16_index, utf16_index, SelectionModel::LEADING);
304 }
305
306 SelectionModel RenderTextLinux::LeftmostSelectionModelInsideRun(
307 const PangoItem* run) const {
308 if (run->analysis.level % 2 == 0)
309 return FirstSelectionModelInsideRun(run);
310 return LastSelectionModelInsideRun(run);
311 }
312
313 SelectionModel RenderTextLinux::RightmostSelectionModelInsideRun(
314 const PangoItem* run) const {
315 if (run->analysis.level % 2)
316 return FirstSelectionModelInsideRun(run);
317 return LastSelectionModelInsideRun(run);
318 }
319
320 bool RenderTextLinux::GetVisuallyAdjacentCursorPositionForEnd(
321 const SelectionModel& current,
msw 2011/08/19 23:16:59 Out-dent these argument lines two spaces.
xji 2011/08/22 23:57:28 Done.
322 CursorMovementDirection dir,
323 SelectionModel* adjacent) const {
324 size_t text_length = text().length();
325 if (current.selection_end() == text_length &&
326 current.caret_pos() == text_length &&
327 current.caret_placement() == SelectionModel::LEADING) {
328 // Visually rightmost END position.
329 if (dir == LEFT) {
330 GSList* last_run = GetLastRun();
331 if (last_run) {
332 *adjacent = RightmostSelectionModelInsideRun(
msw 2011/08/19 23:16:59 Does this work in a RTL context with plain RTL tex
xji 2011/08/22 23:57:28 it works. but this function is no longer needed.
333 reinterpret_cast<PangoLayoutRun*>(last_run->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
334 }
335 }
336 adjacent->set_selection_start(adjacent->selection_end());
337 return true;
338 }
339 return false;
340 }
341
342 // Assume caret_pos in |current| is n, 'l' represents leading in
343 // caret_placement and 't' represents trailing in caret_placement. Following
344 // is the calculation from (caret_pos, caret_placement) in |current| to
345 // (caret_pos, caret_placement, selection_end) when moving cursor left by
msw 2011/08/19 23:16:59 While I can't thank you enough to writing the psue
xji 2011/08/22 23:57:28 Done.
346 // one grapheme (for simplilcity, assume each grapheme is one character).
347 // If n is in LTR run,
348 // (n, t) ---> (n, l, n)
349 // (n, l) ---> (n-1, l, n-1) if n is inside run (not at boundary).
350 // (n, l) ---> goto across run case if n is at run boundary;
351 // If n is in RTL run,
352 // (n, l) --> (n, t, n+1);
353 // (n, t) --> (n+1, t n+2) if n is inside run;
354 // (n, t) --> goto across run case if n is at run boundar;
msw 2011/08/19 23:16:59 boundar*y*
xji 2011/08/22 23:57:28 Done.
355 // If n is at run boudary, get its visually left run,
msw 2011/08/19 23:16:59 bou*n*dary
xji 2011/08/22 23:57:28 Done.
356 // if left run is LTR run,
357 // (n, t) --> (left run's end, l, left run's end);
358 // If rght run is RTL run,
359 // (n, t) --> (left run's begin, t, left run's begin + 1);
360 //
361 // TODO(xji): duplication with GetRightSelectionModelByGrapheme.
362 SelectionModel RenderTextLinux::GetLeftSelectionModelByGrapheme(
363 const SelectionModel& current) const {
364 SelectionModel left(current);
365 if (GetVisuallyAdjacentCursorPositionForEnd(current, LEFT, &left))
366 return left;
367
368 bool at_run_boundary = false;
369 GSList* run = GetRunContainsCaretPos(current, LEFT, &at_run_boundary);
370 if (run == NULL) {
371 left.set_selection_start(left.selection_end());
372 return left;
373 }
374
375 PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
376 if (box->analysis.level % 2 == 0) { // LTR box.
377 if (current.caret_placement() == SelectionModel::TRAILING) {
378 left.set_caret_placement(SelectionModel::LEADING);
379 left.set_selection_end(left.caret_pos());
380 } else { // caret_placement == TRAILING.
381 if (at_run_boundary) {
382 GSList* prev_run = GetPreviousRun(run);
383 if (prev_run) {
384 return RightmostSelectionModelInsideRun(
385 reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
386 }
387 } else { // !at_run_boundary.
388 size_t prev = Utf16IndexOfAdjacentGrapheme(left.caret_pos(), PREVIOUS);
389 left.set_caret_pos(prev);
390 left.set_selection_end(prev);
391 }
392 }
393 } else { // RTL box.
394 if (current.caret_placement() == SelectionModel::LEADING) {
395 left.set_caret_placement(SelectionModel::TRAILING);
396 left.set_selection_end(
397 Utf16IndexOfAdjacentGrapheme(current.caret_pos(), NEXT));
398 } else { // caret_placement == LEADING
399 if (at_run_boundary) {
400 GSList* prev_run = GetPreviousRun(run);
401 if (prev_run) {
402 return RightmostSelectionModelInsideRun(
403 reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
404 }
405 } else { // !at_run_boundary
406 // current.seletion_end - current.caret_pos
407 // == NumOfCharsInGrapheme(caret_pos);
408 left.set_caret_pos(current.selection_end());
409 left.set_selection_end(
410 Utf16IndexOfAdjacentGrapheme(left.caret_pos(), NEXT));
411 }
412 }
413 }
414
415 left.set_selection_start(left.selection_end());
416 return left;
417 }
418
419 // Assume caret_pos in |current| is n, 'l' represents leading in
msw 2011/08/19 23:16:59 Ditto on triplet order and line endings.
xji 2011/08/22 23:57:28 Done.
420 // caret_placement and 't' represents trailing in caret_placement. Following
421 // is the calculation from (caret_pos, caret_placement) in |current| to
422 // (caret_pos, caret_placement, selection_end) when moving cursor right by
423 // one grapheme (for simplilcity, assume each grapheme is one character).
424 // If n is in LTR run,
425 // (n, l) ---> (n, t, n+1)
426 // (n, t) ---> (n+1, t, n+2) if n is inside run (not at boundary).
427 // (n, t) ---> goto across run case if n is at run boundary;
428 // If n is in RTL run,
429 // (n, t) --> (n, l, n);
430 // (n, l) --> (n-1, l, n-1) if n is inside run;
431 // (n, l) --> goto across run case if n is at run boundar;
msw 2011/08/19 23:16:59 boundar*y*
xji 2011/08/22 23:57:28 Done.
432 // If n is at run boudary, get its visually right run,
msw 2011/08/19 23:16:59 bou*n*dary
xji 2011/08/22 23:57:28 Done.
433 // if right run is LTR run,
434 // (n, t) --> (right run's begin, t, right run's begin + 1);
435 // If rght run is RTL run,
436 // (n, t) --> (right run's end, l, right run's end);
437 SelectionModel RenderTextLinux::GetRightSelectionModelByGrapheme(
438 const SelectionModel& current) const {
439 SelectionModel right(current);
440 if (GetVisuallyAdjacentCursorPositionForEnd(current, RIGHT, &right))
441 return right;
442
443 bool at_run_boundary = false;
444 GSList* run = GetRunContainsCaretPos(current, RIGHT, &at_run_boundary);
445 if (run == NULL) {
446 right.set_selection_start(right.selection_end());
447 return right;
448 }
449
450 PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
451 if (box->analysis.level % 2 == 0) { // LTR box.
452 if (current.caret_placement() == SelectionModel::LEADING) {
453 right.set_caret_placement(SelectionModel::TRAILING);
454 right.set_selection_end(
455 Utf16IndexOfAdjacentGrapheme(current.caret_pos(), NEXT));
456 } else { // caret_placement == TRAILING.
457 if (at_run_boundary) {
458 if (run->next) {
459 return LeftmostSelectionModelInsideRun(
460 reinterpret_cast<PangoLayoutRun*>(run->next->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
461 }
462 } else { // !at_run_boundary.
463 // current.seletion_end - current.caret_pos
464 // == NumOfCharsInGrapheme(caret_pos);
465 right.set_caret_pos(current.selection_end());
466 right.set_selection_end(
467 Utf16IndexOfAdjacentGrapheme(right.caret_pos(), NEXT));
468 }
469 }
470 } else { // RTL box.
471 if (current.caret_placement() == SelectionModel::TRAILING) {
472 right.set_caret_placement(SelectionModel::LEADING);
473 right.set_selection_end(right.caret_pos());
474 } else { // caret_placement == LEADING
475 if (at_run_boundary) {
476 if (run->next) {
477 return LeftmostSelectionModelInsideRun(
478 reinterpret_cast<PangoLayoutRun*>(run->next->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
479 }
480 } else { // !at_run_boundary
481 // current.seletion_end - current.caret_pos
482 // == NumOfCharsInGrapheme(caret_pos);
483 size_t prev = Utf16IndexOfAdjacentGrapheme(right.caret_pos(), PREVIOUS);
484 right.set_caret_pos(prev);
485 right.set_selection_end(prev);
486 }
487 }
488 }
489
490 right.set_selection_start(right.selection_end());
491 return right;
492 }
493
494 PangoLayout* RenderTextLinux::EnsureLayout() {
495 if (layout_ == NULL) {
496 CanvasSkia canvas(display_rect().width(), display_rect().height(), false);
497 // TODO(xji): how costly is the Begin/EndPlatformPaint? Can I get cairo
498 // context in another way?
499 cairo_t* cr = skia::BeginPlatformPaint(&canvas);
500
501 layout_ = pango_cairo_create_layout(cr);
502 SetupPangoLayout(layout_, text(), default_style().font,
503 display_rect().width(), GetTextDirection(),
504 CanvasSkia::DefaultCanvasTextAlignment());
505 pango_layout_set_height(layout_, display_rect().height() * PANGO_SCALE);
506 SetupPangoAttributes(layout_);
507
508 skia::EndPlatformPaint(&canvas);
509
510 layout_line_ = pango_layout_get_line_readonly(layout_, 0);
511 }
512 return layout_;
513 }
514
515 void RenderTextLinux::ResetLayout() {
516 // set_cached_bounds_and_offset_valid(false) is done in RenderText for every
517 // operation that trigger ResetLayout().
msw 2011/08/19 23:16:59 Yeah, we might want to make a more general shared
518 if (layout_) {
519 g_object_unref(layout_);
msw 2011/08/19 23:16:59 Yuck, more GTK!
520 layout_ = NULL;
521 }
522 if (layout_line_) {
523 // TODO(xji): do I need ref/unref?
524 // pango_layout_line_unref(layout_line_);
msw 2011/08/19 23:16:59 Sounds like you should ref and unref it to retain
xji 2011/08/22 23:57:28 Done.
525 layout_line_ = NULL;
526 }
527 }
528
529 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) {
530 PangoAttrList* attrs = pango_attr_list_new();
531 // Set selection background color.
532 SkColor selection_color =
533 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
534 PangoAttribute* pango_attr = pango_attr_background_new(
535 COLOR_16_TO_32_BIT(SkColorGetR(selection_color)),
msw 2011/08/19 23:16:59 Can you use SkColorGetR/G/B or similar instead her
xji 2011/08/22 23:57:28 I am using SKColorGetR/G/B, but I need to convert
536 COLOR_16_TO_32_BIT(SkColorGetG(selection_color)),
537 COLOR_16_TO_32_BIT(SkColorGetB(selection_color)));
538 AppendPangoAttribute(MinOfSelection(), MaxOfSelection(),
539 pango_attr, attrs);
540
541 StyleRanges ranges_of_style(style_ranges());
542 ApplyCompositionAndSelectionStyles(&ranges_of_style);
543
544 for (StyleRanges::const_iterator i = ranges_of_style.begin();
545 i < ranges_of_style.end(); ++i) {
546 const Font& font = !i->underline ? i->font :
547 i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED);
548 pango_attr = pango_attr_font_desc_new(
549 reinterpret_cast<PangoFontDescription*>(font.GetNativeFont()));
550 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
551
552 SkColor foreground = i->foreground;
553 pango_attr = pango_attr_foreground_new(
554 COLOR_16_TO_32_BIT(SkColorGetR(foreground)),
555 COLOR_16_TO_32_BIT(SkColorGetG(foreground)),
556 COLOR_16_TO_32_BIT(SkColorGetB(foreground)));
557 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
558
559 if (i->strike) {
560 pango_attr = pango_attr_strikethrough_new(true);
561 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
562 }
563 }
564
565 pango_layout_set_attributes(layout, attrs);
566 pango_attr_list_unref(attrs);
567 }
568
569 void RenderTextLinux::AppendPangoAttribute(size_t start,
570 size_t end,
571 PangoAttribute* pango_attr,
572 PangoAttrList* attrs) {
573 pango_attr->start_index = Utf16IndexToUtf8Index(text(), start);
574 pango_attr->end_index = Utf16IndexToUtf8Index(text(), end);
575 pango_attr_list_insert(attrs, pango_attr);
576 }
577
578 GSList* RenderTextLinux::GetPreviousRun(GSList* run) const {
msw 2011/08/19 23:16:59 lol at singly linked lists :)
579 GSList* current = layout_line_->runs;
580 GSList* prev = NULL;
581 while (current) {
582 if (current == run)
583 return prev;
584 prev = current;
585 current = current->next;
586 }
587 return NULL;
588 }
589
590 GSList* RenderTextLinux::GetLastRun() const {
591 GSList* current = layout_line_->runs;
msw 2011/08/19 23:16:59 i'm done loling, now i'm sighing :(
592 while (current && current->next) {
593 current = current->next;
594 }
595 return current;
596 }
597
598 size_t RenderTextLinux::Utf16IndexToUtf8Index(const string16& text,
599 size_t index) const {
600 int utf8_index = 0;
601 for (size_t i = 0; i < index; ++i) {
602 if (text[i] < 0x80) {
603 ++utf8_index;
604 } else if (text[i] < 0x800) {
605 utf8_index += 2;
606 } else {
607 if ((U16_IS_LEAD(text[i]))) {
608 if (i + 1 < index) {
609 if (U16_IS_TRAIL(text[i + 1])) {
610 // A surrogate pair.
611 utf8_index += 4;
612 ++i;
613 continue;
614 }
615 } else if (i + 1 < text.length() && U16_IS_TRAIL(text[i + 1])) {
616 // A split surrogate pair, stop at the leading.
617 break;
618 }
619 }
620 utf8_index += 3;
621 }
622 }
623 return utf8_index;
624 }
625
626
627 size_t RenderTextLinux::Utf8IndexToUtf16Index(const string16& text,
628 size_t index) const {
629 // TODO(xji): DUP with utf16IndexToUtf8Index.
630 size_t utf8_index = 0;
631 size_t utf16_index = 0;
632 for (; utf16_index < text.length(); ++utf16_index) {
633 if (text[utf16_index] < 0x80) {
634 ++utf8_index;
635 } else if (text[utf16_index] < 0x800) {
636 utf8_index += 2;
637 } else {
638 if ((U16_IS_LEAD(text[utf16_index]))) {
639 if (utf16_index + 1 < text.length()) {
640 if (U16_IS_TRAIL(text[utf16_index + 1])) {
641 // A surrogate pair.
642 utf8_index += 4;
643 if (utf8_index >= index)
644 break;
645 ++utf16_index;
646 continue;
647 }
648 }
649 }
650 utf8_index += 3;
651 }
652 if (utf8_index > index)
653 break;
654 }
655 return utf16_index;
656 }
657
658 void RenderTextLinux::AdjustBoundsForNonInsertMode(
msw 2011/08/19 23:16:59 I think this function should be in-lined in GetCur
xji 2011/08/22 23:57:28 Done.
659 const SelectionModel& selection,
660 const PangoRectangle& pos,
661 Rect* bounds) const {
662 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
663 if (caret_placement == SelectionModel::LEADING) {
664 if (pos.width > 0) {
665 bounds->set_width(pos.width);
666 } else {
667 bounds->set_x(pos.x + pos.width);
668 bounds->set_width(-pos.width);
669 }
670 } else {
671 // TODO(xji): what is the expected behavior? For example, "abcFED",
672 // when |selection| == SelectionModel(3, 2, TRAILING), in insert-mode,
673 // cursor is at right of 'c'. In non-insert-mode, is the bounds around 'D'?
msw 2011/08/19 23:16:59 I'd say yes, but I'm not sure if you're asking me
xji 2011/08/22 23:57:28 I implement as this. we can always change it if it
674 }
675 }
676
20 } // namespace gfx 677 } // 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