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

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

Issue 7458014: Implement Uniscribe RenderText for Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address nits. 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
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_win.h" 5 #include "ui/gfx/render_text_win.h"
6 6
7 #include "base/i18n/break_iterator.h"
8 #include "base/logging.h"
9 #include "base/stl_util.h"
10 #include "skia/ext/skia_utils_win.h"
11 #include "ui/gfx/canvas.h"
12 #include "ui/gfx/canvas_skia.h"
13
14 namespace {
15
16 // The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes.
17 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
18 const int kGuessItems = 100;
19 const int kMaxItems = 10000;
20
21 } // namespace
22
7 namespace gfx { 23 namespace gfx {
8 24
25 namespace internal {
26
27 TextRun::TextRun()
28 : strike(false),
29 width(0),
30 preceding_run_widths(0),
31 glyph_count(0) {
32 }
33
34 } // namespace internal
35
9 RenderTextWin::RenderTextWin() 36 RenderTextWin::RenderTextWin()
10 : RenderText() { 37 : RenderText(),
38 script_control_(),
39 script_state_(),
40 script_cache_(NULL),
41 string_width_(0) {
42 // Omitting default constructors for script_* would leave POD uninitialized.
43 HRESULT hr = 0;
44
45 // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message.
46 // TODO(msw): Use Chrome/profile locale/language settings?
47 hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_);
48 DCHECK(SUCCEEDED(hr));
49
50 hr = ScriptApplyDigitSubstitution(&digit_substitute_,
51 &script_control_,
52 &script_state_);
53 DCHECK(SUCCEEDED(hr));
54 script_control_.fMergeNeutralItems = true;
55
56 SetSelectionModel(LeftEndSelectionModel());
11 } 57 }
12 58
13 RenderTextWin::~RenderTextWin() { 59 RenderTextWin::~RenderTextWin() {
60 ScriptFreeCache(&script_cache_);
61 STLDeleteContainerPointers(runs_.begin(), runs_.end());
62 }
63
64 void RenderTextWin::SetText(const string16& text) {
65 // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
66 RenderText::SetText(text);
67 ItemizeLogicalText();
68 LayoutVisualText(CreateCompatibleDC(NULL));
69 }
70
71 void RenderTextWin::SetDisplayRect(const Rect& r) {
72 RenderText::SetDisplayRect(r);
73 ItemizeLogicalText();
74 LayoutVisualText(CreateCompatibleDC(NULL));
75 }
76
77 void RenderTextWin::ApplyStyleRange(StyleRange style_range) {
78 RenderText::ApplyStyleRange(style_range);
79 ItemizeLogicalText();
80 LayoutVisualText(CreateCompatibleDC(NULL));
81 }
82
83 void RenderTextWin::ApplyDefaultStyle() {
84 RenderText::ApplyDefaultStyle();
85 ItemizeLogicalText();
86 LayoutVisualText(CreateCompatibleDC(NULL));
87 }
88
89 int RenderTextWin::GetStringWidth() {
90 return string_width_;
91 }
92
93 void RenderTextWin::Draw(Canvas* canvas) {
94 skia::ScopedPlatformPaint scoped_platform_paint(canvas->AsCanvasSkia());
95 HDC hdc = scoped_platform_paint.GetPlatformSurface();
96 int saved_dc = SaveDC(hdc);
97 DrawSelection(canvas);
98 DrawVisualText(canvas);
99 DrawCursor(canvas);
100 RestoreDC(hdc, saved_dc);
101 }
102
103 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
104 if (text().empty())
105 return SelectionModel();
106
107 // Find the run that contains the point and adjust the argument location.
108 Point p(ToTextPoint(point));
109 size_t run_index = GetRunContainingPoint(p);
110 if (run_index == runs_.size())
111 return (p.x() < 0) ? LeftEndSelectionModel() : RightEndSelectionModel();
112 internal::TextRun* run = runs_[run_index];
113
114 int position = 0, trailing = 0;
115 HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths,
116 run->range.length(),
117 run->glyph_count,
118 run->logical_clusters.get(),
119 run->visible_attributes.get(),
120 run->advance_widths.get(),
121 &(run->script_analysis),
122 &position,
123 &trailing);
xji 2011/08/23 07:39:51 I am not able to tell whether the parameters are n
msw 2011/08/24 10:15:40 Perhaps this document clarifies your concern: http
xji 2011/08/25 05:58:38 Thanks for the link. I think they used the wrong t
124 DCHECK(SUCCEEDED(hr));
125 position += run->range.start();
126
127 // Show an alternate visual cursor for points trailing the end of a run.
128 if (position == static_cast<int>(run->range.end() - 1) && trailing > 0)
129 return SelectionModel(position + trailing, position,
130 SelectionModel::TRAILING);
131
132 position += trailing;
133 DCHECK(position >= 0 && position <= static_cast<int>(text().length()));
134 return SelectionModel(position, position, SelectionModel::LEADING);
xji 2011/08/23 07:39:51 hm.. I think we have difference here. for example:
msw 2011/08/24 10:15:40 I've adjusted the logic to (hopefully) match yours
135 }
136
137 std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
138 ui::Range range(from, to);
139 DCHECK(ui::Range(0, text().length()).Contains(range));
140 Point display_offset(GetUpdatedDisplayOffset());
141 std::vector<Rect> bounds;
142 HRESULT hr = 0;
143
144 // Add a Rect for each run/selection intersection.
145 for (size_t i = 0; i < runs_.size(); ++i) {
146 internal::TextRun* run = runs_[visual_to_logical_[i]];
147 ui::Range intersection = run->range.Intersect(range);
148 if (intersection.IsValid()) {
149 DCHECK(!intersection.is_reversed());
150 int start_offset = 0;
151 hr = ScriptCPtoX(intersection.start() - run->range.start(),
152 false,
153 run->range.length(),
154 run->glyph_count,
155 run->logical_clusters.get(),
156 run->visible_attributes.get(),
157 run->advance_widths.get(),
158 &(run->script_analysis),
159 &start_offset);
160 DCHECK(SUCCEEDED(hr));
161 int end_offset = 0;
162 hr = ScriptCPtoX(intersection.end() - run->range.start(),
163 false,
164 run->range.length(),
165 run->glyph_count,
166 run->logical_clusters.get(),
167 run->visible_attributes.get(),
168 run->advance_widths.get(),
169 &(run->script_analysis),
170 &end_offset);
xji 2011/08/23 07:39:51 is it correct that the bounds always from leading
msw 2011/08/24 10:15:40 I can't devise an example offhand, but perhaps it'
171 DCHECK(SUCCEEDED(hr));
172 if (start_offset > end_offset)
173 std::swap(start_offset, end_offset);
174 Rect rect(run->preceding_run_widths + start_offset, 0,
175 end_offset - start_offset, run->font.GetHeight());
176 // Center the rect vertically in the display area.
177 rect.Offset(0, (display_rect().height() - rect.height()) / 2);
178 rect.set_origin(ToViewPoint(rect.origin()));
179 // Union this with the last rect if they're adjacent.
180 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) {
181 rect = rect.Union(bounds.back());
182 bounds.pop_back();
183 }
184 bounds.push_back(rect);
185 }
186 }
187 return bounds;
188 }
189
190 Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection,
191 bool insert_mode) {
192 // Highlight the logical cursor (selection end) when not in insert mode.
193 size_t pos = insert_mode ? selection.caret_pos() : selection.selection_end();
194 size_t run_index = GetRunContainingPosition(pos);
195 internal::TextRun* run = run_index == runs_.size() ? NULL : runs_[run_index];
196
197 int start_x = 0, end_x = 0;
198 if (run) {
199 HRESULT hr = 0;
200 hr = ScriptCPtoX(pos - run->range.start(),
201 false,
202 run->range.length(),
203 run->glyph_count,
204 run->logical_clusters.get(),
205 run->visible_attributes.get(),
206 run->advance_widths.get(),
207 &(run->script_analysis),
208 &start_x);
209 DCHECK(SUCCEEDED(hr));
210 hr = ScriptCPtoX(pos - run->range.start(),
211 true,
212 run->range.length(),
213 run->glyph_count,
214 run->logical_clusters.get(),
215 run->visible_attributes.get(),
216 run->advance_widths.get(),
217 &(run->script_analysis),
218 &end_x);
219 DCHECK(SUCCEEDED(hr));
220 }
221 // TODO(msw): Use the last visual run's font instead of the default font?
222 int height = run ? run->font.GetHeight() : default_style().font.GetHeight();
223 Rect rect(std::min(start_x, end_x), 0, std::abs(end_x - start_x), height);
224 size_t text_end_offset = base::i18n::IsRTL() ? 0 : string_width_;
225 // Offset to the run start and center the rect vertically in the display area.
226 rect.Offset((run ? run->preceding_run_widths : text_end_offset),
227 (display_rect().height() - rect.height()) / 2);
xji 2011/08/23 07:39:51 so if run is not found, rect.x() will be set to th
msw 2011/08/24 10:15:40 Yeah, I added a comment to that effect.
228 // Adjust for leading/trailing in insert mode.
229 if (insert_mode) {
230 bool leading = selection.caret_placement() == SelectionModel::LEADING;
231 // Adjust the x value for right-side placement.
232 if (run && (run->script_analysis.fRTL == leading))
233 rect.set_x(rect.right());
234 rect.set_width(0);
235 }
236 rect.set_origin(ToViewPoint(rect.origin()));
237 return rect;
238 }
239
240 SelectionModel RenderTextWin::GetLeftSelectionModel(
241 const SelectionModel& selection,
242 BreakType break_type) {
243 if (break_type == LINE_BREAK || text().empty())
244 return LeftEndSelectionModel();
245
246 scoped_ptr<base::i18n::BreakIterator> iter;
247 if (break_type == WORD_BREAK) {
248 iter.reset(new base::i18n::BreakIterator(text(),
249 base::i18n::BreakIterator::BREAK_WORD));
250 bool success = iter->Init();
251 DCHECK(success);
252 if (!success)
253 return selection;
254 }
255
256 bool character_break = break_type == CHARACTER_BREAK;
257 bool at_word_break = false;
258 SelectionModel left(selection);
259 SCRIPT_LOGATTR attributes;
260 do {
261 left = LeftSelectionModel(left);
262 size_t run_index = GetRunContainingPosition(left.selection_end());
xji 2011/08/23 07:39:51 why use selection_end to get run containing positi
msw 2011/08/24 10:15:40 You're right and I've changed this to use caret_po
xji 2011/08/25 05:58:38 it is fine to leave it for follow-up. Looks like
263 if (run_index == runs_.size() || left.Equals(LeftEndSelectionModel()))
264 return left;
265 size_t char_index = left.selection_end() - runs_[run_index]->range.start();
266 attributes = runs_[run_index]->logical_attributes[char_index];
267 bool advancing = runs_[run_index]->script_analysis.fRTL;
xji 2011/08/23 07:39:51 advancing seems not used.
msw 2011/08/24 10:15:40 Done.
268 at_word_break = iter.get() && iter->IsWordBreakAt(left.selection_end());
269 } while (!(character_break && attributes.fCharStop) && !at_word_break);
270 return left;
271 }
272
273 SelectionModel RenderTextWin::GetRightSelectionModel(
274 const SelectionModel& selection,
275 BreakType break_type) {
276 if (break_type == LINE_BREAK || text().empty())
277 return RightEndSelectionModel();
278
279 scoped_ptr<base::i18n::BreakIterator> iter;
280 if (break_type == WORD_BREAK) {
281 iter.reset(new base::i18n::BreakIterator(text(),
282 base::i18n::BreakIterator::BREAK_WORD));
283 bool success = iter->Init();
284 DCHECK(success);
285 if (!success)
286 return selection;
287 }
288
289 bool character_break = break_type == CHARACTER_BREAK;
290 bool at_word_break = false;
291 SelectionModel right(selection);
292 SCRIPT_LOGATTR attributes;
293 do {
294 right = RightSelectionModel(right);
295 size_t run_index = GetRunContainingPosition(right.selection_end());
296 if (run_index == runs_.size() || right.Equals(RightEndSelectionModel()))
297 return right;
298 size_t char_index = right.selection_end() - runs_[run_index]->range.start();
299 attributes = runs_[run_index]->logical_attributes[char_index];
300 bool advancing = !runs_[run_index]->script_analysis.fRTL;
xji 2011/08/23 07:39:51 advancing seems not used.
msw 2011/08/24 10:15:40 Done.
301 at_word_break = iter.get() && iter->IsWordBreakAt(right.selection_end());
302 } while (!(character_break && attributes.fCharStop) && !at_word_break);
303 return right;
304 }
305
306 void RenderTextWin::ItemizeLogicalText() {
307 text_is_dirty_ = false;
308 STLDeleteContainerPointers(runs_.begin(), runs_.end());
309 runs_.clear();
310 if (text().empty())
311 return;
312
313 const wchar_t* raw_text = text().c_str();
314 const int text_length = text().length();
315
316 HRESULT hr = E_OUTOFMEMORY;
317 int script_items_count = 0;
318 scoped_array<SCRIPT_ITEM> script_items;
319 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
xji 2011/08/23 07:39:51 kGussItem is just used for guessing the max number
msw 2011/08/24 10:15:40 I don't know what you mean by "won't do any alloca
xji 2011/08/25 05:58:38 I see. I was thinking since we are dealing with on
320 // Derive the array of Uniscribe script items from the logical text.
321 // ScriptItemize always adds a terminal array item so that the length of the
322 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
323 script_items.reset(new SCRIPT_ITEM[n]);
324 hr = ScriptItemize(raw_text,
325 text_length,
326 n - 1,
327 &script_control_,
328 &script_state_,
329 script_items.get(),
330 &script_items_count);
331 }
xji 2011/08/23 07:39:51 WCHAR is 16-bit in windows, right? from http://msd
msw 2011/08/24 10:15:40 Yes.
332 DCHECK(SUCCEEDED(hr));
333
334 if (script_items_count <= 0)
335 return;
336
337 // Build the list of runs, merge font/underline styles.
338 // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
339 // TODO(msw): Apply the overriding selection and composition styles.
340 StyleRanges::const_iterator style = style_ranges().begin();
341 SCRIPT_ITEM* script_item = script_items.get();
342 for (int run_break = 0; run_break < text_length;) {
343 internal::TextRun* run = new internal::TextRun();
344 run->range.set_start(run_break);
345 run->font = !style->underline ? style->font :
346 style->font.DeriveFont(0, style->font.GetStyle() | Font::UNDERLINED);
347 run->foreground = style->foreground;
348 run->strike = style->strike;
349 run->script_analysis = script_item->a;
350
351 // Find the range end and advance the structures as needed.
352 int script_item_end = (script_item + 1)->iCharPos;
353 int style_range_end = style->range.end();
354 run_break = std::min(script_item_end, style_range_end);
355 if (script_item_end <= style_range_end)
356 script_item++;
357 if (script_item_end >= style_range_end)
358 style++;
359 run->range.set_end(run_break);
360 runs_.push_back(run);
361 }
362 }
363
364 void RenderTextWin::LayoutVisualText(HDC hdc) {
365 HRESULT hr = 0;
366 std::vector<internal::TextRun*>::const_iterator run_iter;
367 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) {
368 internal::TextRun* run = *run_iter;
369 int run_length = run->range.length();
370 string16 run_string(text().substr(run->range.start(), run_length));
371 const wchar_t* run_text = run_string.c_str();
372 // Select the font desired for glyph generation
373 SelectObject(hdc, run->font.GetNativeFont());
374
375 const int max_glyphs = static_cast<int>(1.5 * run_length + 16);
xji 2011/08/23 07:39:51 why assigned like that?
msw 2011/08/24 10:15:40 From http://msdn.microsoft.com/en-us/library/dd368
376 run->glyphs.reset(new WORD[max_glyphs]);
377 run->logical_clusters.reset(new WORD[run_length]);
378 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
379 run->glyph_count = 0;
380 hr = ScriptShape(hdc,
381 &script_cache_,
382 run_text,
383 run_length,
384 max_glyphs,
385 &(run->script_analysis),
386 run->glyphs.get(),
387 run->logical_clusters.get(),
388 run->visible_attributes.get(),
389 &(run->glyph_count));
390 DCHECK(SUCCEEDED(hr));
391
392 if (run->glyph_count > 0) {
393 run->advance_widths.reset(new int[run->glyph_count]);
394 run->offsets.reset(new GOFFSET[run->glyph_count]);
395 hr = ScriptPlace(hdc,
396 &script_cache_,
397 run->glyphs.get(),
398 run->glyph_count,
399 run->visible_attributes.get(),
400 &(run->script_analysis),
401 run->advance_widths.get(),
402 run->offsets.get(),
403 &(run->abc_widths));
404 DCHECK(SUCCEEDED(hr));
405
406 run->logical_attributes.reset(new SCRIPT_LOGATTR[run_length]);
407 hr = ScriptBreak(run_text,
408 run_length,
409 &(run->script_analysis),
410 run->logical_attributes.get());
xji 2011/08/23 07:39:51 we are targeting to single-line text, so do we nee
msw 2011/08/24 10:15:40 I was previously using this for word break, and I'
411 DCHECK(SUCCEEDED(hr));
412 }
413 }
414
415 if (runs_.size() > 0) {
416 // Build the array of bidirectional embedding levels.
417 scoped_array<BYTE> levels(new BYTE[runs_.size()]);
418 for (size_t i = 0; i < runs_.size(); ++i)
419 levels[i] = runs_[i]->script_analysis.s.uBidiLevel;
420
421 // Get the maps between visual and logical run indices.
422 visual_to_logical_.reset(new int[runs_.size()]);
423 logical_to_visual_.reset(new int[runs_.size()]);
424 hr = ScriptLayout(runs_.size(),
425 levels.get(),
426 visual_to_logical_.get(),
427 logical_to_visual_.get());
428 DCHECK(SUCCEEDED(hr));
429 }
430
431 // Precalculate run width information.
432 size_t preceding_run_widths = 0;
433 for (size_t i = 0; i < runs_.size(); ++i) {
434 internal::TextRun* run = runs_[visual_to_logical_[i]];
435 run->preceding_run_widths = preceding_run_widths;
436 const ABC& abc = run->abc_widths;
437 run->width = abc.abcA + abc.abcB + abc.abcC;
438 preceding_run_widths += run->width;
439 }
440 string_width_ = preceding_run_widths;
441 }
442
443 size_t RenderTextWin::GetRunContainingPosition(size_t position) const {
444 // Find the text run containing the argument position.
445 size_t run = 0;
446 for (; run < runs_.size(); ++run)
447 if (runs_[run]->range.start() <= position &&
448 runs_[run]->range.end() > position)
449 break;
450 return run;
451 }
452
453 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
454 // Find the text run containing the argument point (assumed already offset).
455 size_t run = 0;
456 for (; run < runs_.size(); ++run)
457 if (runs_[run]->preceding_run_widths <= point.x() &&
458 runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
459 break;
460 return run;
461 }
462
463 SelectionModel RenderTextWin::LeftSelectionModel(
464 const SelectionModel& selection) const {
465 size_t caret = selection.caret_pos();
466 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
467 size_t run_index = GetRunContainingPosition(caret);
468 DCHECK(run_index < runs_.size());
469 internal::TextRun* run = runs_[run_index];
470
471 // If the caret's associated character is in a LTR run.
472 if (!run->script_analysis.fRTL) {
473 if (caret_placement == SelectionModel::TRAILING)
474 return SelectionModel(caret, caret, SelectionModel::LEADING);
475 else if (caret > run->range.start())
476 return SelectionModel(caret - 1, caret - 1, SelectionModel::LEADING);
477 } else { // The caret's associated character is in a RTL run.
478 if (caret_placement == SelectionModel::LEADING)
479 return SelectionModel(caret + 1, caret, SelectionModel::TRAILING);
480 else if (caret < run->range.end() - 1)
481 return SelectionModel(caret + 2, caret + 1, SelectionModel::TRAILING);
482 }
483
484 // The character is at the end of its run; advance to the next visual run.
xji 2011/08/23 07:39:51 s/end/begin/? s/next/previous/
msw 2011/08/24 10:15:40 Done.
485 size_t visual_index = logical_to_visual_[run_index];
486 if (visual_index == 0)
487 return LeftEndSelectionModel();
488 internal::TextRun* next_run = runs_[visual_to_logical_[visual_index - 1]];
xji 2011/08/23 07:39:51 s/next_run/prev_run/
msw 2011/08/24 10:15:40 Done.
489 if (!next_run->script_analysis.fRTL) {
490 size_t pos = next_run->range.end() - 1;
491 return SelectionModel(pos, pos, SelectionModel::LEADING);
492 }
493 size_t pos = next_run->range.start();
494 return SelectionModel(pos + 1, pos, SelectionModel::TRAILING);
495 }
496
497 SelectionModel RenderTextWin::RightSelectionModel(
498 const SelectionModel& selection) const {
499 size_t caret = selection.caret_pos();
500 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
501 size_t run_index = GetRunContainingPosition(caret);
502 DCHECK(run_index < runs_.size());
503 internal::TextRun* run = runs_[run_index];
504
505 // If the caret's associated character is in a LTR run.
506 if (!run->script_analysis.fRTL) {
507 if (caret_placement == SelectionModel::LEADING)
508 return SelectionModel(caret + 1, caret, SelectionModel::TRAILING);
509 else if (caret < run->range.end() - 1)
510 return SelectionModel(caret + 2, caret + 1, SelectionModel::TRAILING);
511 } else { // The caret's associated character is in a RTL run.
512 if (caret_placement == SelectionModel::TRAILING)
513 return SelectionModel(caret, caret, SelectionModel::LEADING);
514 else if (caret > run->range.start())
515 return SelectionModel(caret - 1, caret - 1, SelectionModel::LEADING);
516 }
517
518 // The character is at the end of its run; advance to the next visual run.
519 size_t visual_index = logical_to_visual_[run_index];
520 if (visual_index == runs_.size() - 1)
521 return RightEndSelectionModel();
522 internal::TextRun* next_run = runs_[visual_to_logical_[visual_index + 1]];
523 if (!next_run->script_analysis.fRTL) {
524 size_t pos = next_run->range.start();
525 return SelectionModel(pos + 1, pos, SelectionModel::TRAILING);
526 }
527 size_t pos = next_run->range.end() - 1;
528 return SelectionModel(pos, pos, SelectionModel::LEADING);
529 }
530
531 SelectionModel RenderTextWin::LeftEndSelectionModel() const {
532 if (text().empty())
533 return SelectionModel(0, 0, SelectionModel::LEADING);
534 size_t cursor = base::i18n::IsRTL() ? text().length() : 0;
535 internal::TextRun* run = runs_[visual_to_logical_[0]];
536 bool rtl = run->script_analysis.fRTL;
537 size_t caret = rtl ? run->range.end() - 1 : run->range.start();
538 SelectionModel::CaretPlacement placement =
539 rtl ? SelectionModel::TRAILING : SelectionModel::LEADING;
540 return SelectionModel(cursor, caret, placement);
541 }
542
543 SelectionModel RenderTextWin::RightEndSelectionModel() const {
544 if (text().empty())
545 return SelectionModel(0, 0, SelectionModel::LEADING);
546 size_t cursor = base::i18n::IsRTL() ? 0 : text().length();
547 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]];
548 bool rtl = run->script_analysis.fRTL;
549 size_t caret = rtl ? run->range.start() : run->range.end() - 1;
550 SelectionModel::CaretPlacement placement =
551 rtl ? SelectionModel::LEADING : SelectionModel::TRAILING;
552 return SelectionModel(cursor, caret, placement);
553 }
554
555 void RenderTextWin::DrawSelection(Canvas* canvas) {
556 std::vector<Rect> sel(
557 GetSubstringBounds(GetSelectionStart(), GetCursorPosition()));
558 SkColor color = focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
559 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
560 canvas->FillRectInt(color, i->x(), i->y(), i->width(), i->height());
561 }
562
563 void RenderTextWin::DrawVisualText(Canvas* canvas) {
564 if (text().empty())
565 return;
566
567 skia::ScopedPlatformPaint scoped_platform_paint(canvas->AsCanvasSkia());
568 HDC hdc = scoped_platform_paint.GetPlatformSurface();
569
570 // Set the background mix mode to transparent.
571 int previous_background_mode = SetBkMode(hdc, TRANSPARENT);
572
573 Point offset(ToViewPoint(Point()));
574 // TODO(msw): Establish a vertical baseline for strings of mixed font heights.
575 size_t height = default_style().font.GetHeight();
576 // Center the text vertically in the display area.
577 offset.Offset(0, (display_rect().height() - height) / 2);
578
579 HRESULT hr = 0;
580 RECT rect = display_rect().ToRECT();
581 for (size_t i = 0; i < runs_.size(); ++i) {
582 // Get the run specified by the visual-to-logical map.
583 internal::TextRun* run = runs_[visual_to_logical_[i]];
584
585 // Set the font and color.
586 SelectObject(hdc, run->font.GetNativeFont());
587 SetTextColor(hdc, skia::SkColorToCOLORREF(run->foreground));
588
589 hr = ScriptTextOut(hdc,
590 &script_cache_,
591 offset.x(),
592 offset.y(),
593 0,
594 &rect,
595 &(run->script_analysis),
596 NULL,
597 0,
598 run->glyphs.get(),
599 run->glyph_count,
600 run->advance_widths.get(),
601 NULL,
602 run->offsets.get());
603 DCHECK(SUCCEEDED(hr));
604
605 // Draw the strikethrough.
606 if (run->strike) {
607 Rect bounds(offset, Size(run->width, run->font.GetHeight()));
608 SkPaint paint;
609 paint.setAntiAlias(true);
610 paint.setStyle(SkPaint::kFill_Style);
611 paint.setColor(run->foreground);
612 paint.setStrokeWidth(kStrikeWidth);
613 canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()),
614 SkIntToScalar(bounds.bottom()),
615 SkIntToScalar(bounds.right()),
616 SkIntToScalar(bounds.y()),
617 paint);
618 }
619
620 offset.Offset(run->width, 0);
621 }
622
623 // Restore the previous background mix mode.
624 SetBkMode(hdc, previous_background_mode);
625 }
626
627 void RenderTextWin::DrawCursor(Canvas* canvas) {
628 // Paint cursor. Replace cursor is drawn as rectangle for now.
629 // TODO(msw): Draw a better cursor with a better indication of association.
630 if (cursor_visible() && focused()) {
631 Rect r(GetUpdatedCursorBounds());
632 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height());
633 }
634 }
635
636 Point RenderTextWin::ToTextPoint(const Point& point) {
637 Point p(point.Subtract(display_rect().origin()));
638 p.Subtract(GetUpdatedDisplayOffset());
xji 2011/08/23 07:39:51 p = p.Subtract();
msw 2011/08/24 10:15:40 Done.
639 if (base::i18n::IsRTL())
640 p.Offset(GetStringWidth() - display_rect().width() + 1, 0);
641 return p;
642 }
643
644 Point RenderTextWin::ToViewPoint(const Point& point) {
645 Point p(point.Add(display_rect().origin()));
646 p.Add(GetUpdatedDisplayOffset());
xji 2011/08/23 07:39:51 p = p.Add(); I am using the above 2 functions as
msw 2011/08/24 10:15:40 Done, I moved them to RenderText as well, whoever
647 if (base::i18n::IsRTL())
648 p.Offset(display_rect().width() - GetStringWidth() - 1, 0);
649 return p;
14 } 650 }
15 651
16 RenderText* RenderText::CreateRenderText() { 652 RenderText* RenderText::CreateRenderText() {
17 return new RenderTextWin; 653 return new RenderTextWin;
18 } 654 }
19 655
20 } // namespace gfx 656 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698