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

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

Issue 7511029: Implement Pango RenderText for Linux. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: add dcheck and todo Created 9 years, 3 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.h" 5 #include "ui/gfx/render_text.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/i18n/break_iterator.h" 9 #include "base/i18n/break_iterator.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 } else if (i->range.end() > text_.length()) { 155 } else if (i->range.end() > text_.length()) {
156 i->range.set_end(text_.length()); 156 i->range.set_end(text_.length());
157 } 157 }
158 } 158 }
159 style_ranges_.back().range.set_end(text_.length()); 159 style_ranges_.back().range.set_end(text_.length());
160 } 160 }
161 #ifndef NDEBUG 161 #ifndef NDEBUG
162 CheckStyleRanges(style_ranges_, text_.length()); 162 CheckStyleRanges(style_ranges_, text_.length());
163 #endif 163 #endif
164 cached_bounds_and_offset_valid_ = false; 164 cached_bounds_and_offset_valid_ = false;
165
166 // Reset selection model. SetText should always followed by SetSelectionModel
167 // or SetCursorPosition in upper layer.
168 SetSelectionModel(SelectionModel(0, 0, SelectionModel::LEADING));
165 } 169 }
166 170
167 void RenderText::SetSelectionModel(const SelectionModel& sel) { 171 void RenderText::SetSelectionModel(const SelectionModel& sel) {
168 size_t start = sel.selection_start(); 172 size_t start = sel.selection_start();
169 size_t end = sel.selection_end(); 173 size_t end = sel.selection_end();
170 selection_model_.set_selection_start(std::min(start, text().length())); 174 selection_model_.set_selection_start(std::min(start, text().length()));
171 selection_model_.set_selection_end(std::min(end, text().length())); 175 selection_model_.set_selection_end(std::min(end, text().length()));
172 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length())); 176 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length()));
173 selection_model_.set_caret_placement(sel.caret_placement()); 177 selection_model_.set_caret_placement(sel.caret_placement());
174 178
175 cached_bounds_and_offset_valid_ = false; 179 cached_bounds_and_offset_valid_ = false;
176 } 180 }
177 181
178 void RenderText::SetDisplayRect(const Rect& r) { 182 void RenderText::SetDisplayRect(const Rect& r) {
179 display_rect_ = r; 183 display_rect_ = r;
180 cached_bounds_and_offset_valid_ = false; 184 cached_bounds_and_offset_valid_ = false;
181 } 185 }
182 186
183 size_t RenderText::GetCursorPosition() const { 187 size_t RenderText::GetCursorPosition() const {
184 return selection_model_.selection_end(); 188 return selection_model_.selection_end();
185 } 189 }
186 190
187 void RenderText::SetCursorPosition(const size_t position) { 191 void RenderText::SetCursorPosition(size_t position) {
188 SelectionModel sel(selection_model()); 192 MoveCursorTo(position, false);
189 sel.set_selection_start(position);
190 sel.set_selection_end(position);
191 sel.set_caret_pos(GetIndexOfPreviousGrapheme(position));
192 sel.set_caret_placement(SelectionModel::TRAILING);
193 SetSelectionModel(sel);
194 } 193 }
195 194
196 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { 195 void RenderText::MoveCursorLeft(BreakType break_type, bool select) {
197 SelectionModel position(selection_model()); 196 SelectionModel position(selection_model());
198 position.set_selection_start(GetCursorPosition()); 197 position.set_selection_start(GetCursorPosition());
199 // Cancelling a selection moves to the edge of the selection. 198 // Cancelling a selection moves to the edge of the selection.
200 if (break_type != LINE_BREAK && !EmptySelection() && !select) { 199 if (break_type != LINE_BREAK && !EmptySelection() && !select) {
201 // Use the selection start if it is left of the selection end. 200 // Use the selection start if it is left of the selection end.
202 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), 201 SelectionModel selection_start = GetSelectionModelForSelectionStart();
203 SelectionModel::LEADING);
204 if (GetCursorBounds(selection_start, false).x() < 202 if (GetCursorBounds(selection_start, false).x() <
205 GetCursorBounds(position, false).x()) 203 GetCursorBounds(position, false).x())
206 position = selection_start; 204 position = selection_start;
207 // For word breaks, use the nearest word boundary left of the selection. 205 // For word breaks, use the nearest word boundary left of the selection.
208 if (break_type == WORD_BREAK) 206 if (break_type == WORD_BREAK)
209 position = GetLeftSelectionModel(position, break_type); 207 position = GetLeftSelectionModel(position, break_type);
210 } else { 208 } else {
211 position = GetLeftSelectionModel(position, break_type); 209 position = GetLeftSelectionModel(position, break_type);
212 } 210 }
213 if (select) 211 if (select)
214 position.set_selection_start(GetSelectionStart()); 212 position.set_selection_start(GetSelectionStart());
215 MoveCursorTo(position); 213 MoveCursorTo(position);
216 } 214 }
217 215
218 void RenderText::MoveCursorRight(BreakType break_type, bool select) { 216 void RenderText::MoveCursorRight(BreakType break_type, bool select) {
219 SelectionModel position(selection_model()); 217 SelectionModel position(selection_model());
220 position.set_selection_start(GetCursorPosition()); 218 position.set_selection_start(GetCursorPosition());
221 // Cancelling a selection moves to the edge of the selection. 219 // Cancelling a selection moves to the edge of the selection.
222 if (break_type != LINE_BREAK && !EmptySelection() && !select) { 220 if (break_type != LINE_BREAK && !EmptySelection() && !select) {
223 // Use the selection start if it is right of the selection end. 221 // Use the selection start if it is right of the selection end.
224 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), 222 SelectionModel selection_start = GetSelectionModelForSelectionStart();
225 SelectionModel::LEADING);
226 if (GetCursorBounds(selection_start, false).x() > 223 if (GetCursorBounds(selection_start, false).x() >
227 GetCursorBounds(position, false).x()) 224 GetCursorBounds(position, false).x())
228 position = selection_start; 225 position = selection_start;
229 // For word breaks, use the nearest word boundary right of the selection. 226 // For word breaks, use the nearest word boundary right of the selection.
230 if (break_type == WORD_BREAK) 227 if (break_type == WORD_BREAK)
231 position = GetRightSelectionModel(position, break_type); 228 position = GetRightSelectionModel(position, break_type);
232 } else { 229 } else {
233 position = GetRightSelectionModel(position, break_type); 230 position = GetRightSelectionModel(position, break_type);
234 } 231 }
235 if (select) 232 if (select)
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 } 298 }
302 299
303 // Now we move selection_end_ to end of selection. Selection boundary 300 // Now we move selection_end_ to end of selection. Selection boundary
304 // is defined as the position where we have alpha-num character on one side 301 // is defined as the position where we have alpha-num character on one side
305 // and non-alpha-num char on the other side. 302 // and non-alpha-num char on the other side.
306 for (; cursor_position < text().length(); cursor_position++) { 303 for (; cursor_position < text().length(); cursor_position++) {
307 if (IsPositionAtWordSelectionBoundary(cursor_position)) 304 if (IsPositionAtWordSelectionBoundary(cursor_position))
308 break; 305 break;
309 } 306 }
310 307
311 SelectionModel sel(selection_model()); 308 MoveCursorTo(selection_start, false);
312 sel.set_selection_start(selection_start); 309 MoveCursorTo(cursor_position, true);
313 sel.set_selection_end(cursor_position);
314 sel.set_caret_pos(GetIndexOfPreviousGrapheme(cursor_position));
315 sel.set_caret_placement(SelectionModel::TRAILING);
316 SetSelectionModel(sel);
317 } 310 }
318 311
319 const ui::Range& RenderText::GetCompositionRange() const { 312 const ui::Range& RenderText::GetCompositionRange() const {
320 return composition_range_; 313 return composition_range_;
321 } 314 }
322 315
323 void RenderText::SetCompositionRange(const ui::Range& composition_range) { 316 void RenderText::SetCompositionRange(const ui::Range& composition_range) {
324 CHECK(!composition_range.IsValid() || 317 CHECK(!composition_range.IsValid() ||
325 ui::Range(0, text_.length()).Contains(composition_range)); 318 ui::Range(0, text_.length()).Contains(composition_range));
326 composition_range_.set_end(composition_range.end()); 319 composition_range_.set_end(composition_range.end());
(...skipping 15 matching lines...) Expand all
342 } 335 }
343 336
344 void RenderText::ApplyDefaultStyle() { 337 void RenderText::ApplyDefaultStyle() {
345 style_ranges_.clear(); 338 style_ranges_.clear();
346 StyleRange style = StyleRange(default_style_); 339 StyleRange style = StyleRange(default_style_);
347 style.range.set_end(text_.length()); 340 style.range.set_end(text_.length());
348 style_ranges_.push_back(style); 341 style_ranges_.push_back(style);
349 cached_bounds_and_offset_valid_ = false; 342 cached_bounds_and_offset_valid_ = false;
350 } 343 }
351 344
352 base::i18n::TextDirection RenderText::GetTextDirection() const { 345 base::i18n::TextDirection RenderText::GetTextDirection() {
353 // TODO(msw): Bidi implementation, intended to replace the functionality added 346 if (base::i18n::IsRTL())
354 // in crrev.com/91881 (discussed in codereview.chromium.org/7324011). 347 return base::i18n::RIGHT_TO_LEFT;
355 return base::i18n::LEFT_TO_RIGHT; 348 return base::i18n::LEFT_TO_RIGHT;
356 } 349 }
357 350
358 int RenderText::GetStringWidth() { 351 int RenderText::GetStringWidth() {
359 return default_style_.font.GetStringWidth(text()); 352 return default_style_.font.GetStringWidth(text());
360 } 353 }
361 354
362 void RenderText::Draw(Canvas* canvas) { 355 void RenderText::Draw(Canvas* canvas) {
363 // Clip the canvas to the text display area. 356 // Clip the canvas to the text display area.
364 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(), 357 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(),
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
419 int left = 0; 412 int left = 0;
420 int left_pos = 0; 413 int left_pos = 0;
421 int right = font.GetStringWidth(text()); 414 int right = font.GetStringWidth(text());
422 int right_pos = text().length(); 415 int right_pos = text().length();
423 416
424 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x()); 417 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x());
425 if (x <= left) return SelectionModel(left_pos); 418 if (x <= left) return SelectionModel(left_pos);
426 if (x >= right) return SelectionModel(right_pos); 419 if (x >= right) return SelectionModel(right_pos);
427 // binary searching the cursor position. 420 // binary searching the cursor position.
428 // TODO(oshima): use the center of character instead of edge. 421 // TODO(oshima): use the center of character instead of edge.
429 // Binary search may not work for language like arabic. 422 // Binary search may not work for language like Arabic.
430 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { 423 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) {
431 int pivot_pos = left_pos + (right_pos - left_pos) / 2; 424 int pivot_pos = left_pos + (right_pos - left_pos) / 2;
432 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); 425 int pivot = font.GetStringWidth(text().substr(0, pivot_pos));
433 if (pivot < x) { 426 if (pivot < x) {
434 left = pivot; 427 left = pivot;
435 left_pos = pivot_pos; 428 left_pos = pivot_pos;
436 } else if (pivot == x) { 429 } else if (pivot == x) {
437 return SelectionModel(pivot_pos); 430 return SelectionModel(pivot_pos);
438 } else { 431 } else {
439 right = pivot; 432 right = pivot;
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
490 483
491 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, 484 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current,
492 BreakType break_type) { 485 BreakType break_type) {
493 if (break_type == LINE_BREAK) 486 if (break_type == LINE_BREAK)
494 return SelectionModel(0, 0, SelectionModel::LEADING); 487 return SelectionModel(0, 0, SelectionModel::LEADING);
495 size_t pos = std::max(static_cast<long>(current.selection_end() - 1), 488 size_t pos = std::max(static_cast<long>(current.selection_end() - 1),
496 static_cast<long>(0)); 489 static_cast<long>(0));
497 if (break_type == CHARACTER_BREAK) 490 if (break_type == CHARACTER_BREAK)
498 return SelectionModel(pos, pos, SelectionModel::LEADING); 491 return SelectionModel(pos, pos, SelectionModel::LEADING);
499 492
500 // Notes: We always iterate words from the begining. 493 // Notes: We always iterate words from the beginning.
501 // This is probably fast enough for our usage, but we may 494 // This is probably fast enough for our usage, but we may
502 // want to modify WordIterator so that it can start from the 495 // want to modify WordIterator so that it can start from the
503 // middle of string and advance backwards. 496 // middle of string and advance backwards.
504 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); 497 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
505 bool success = iter.Init(); 498 bool success = iter.Init();
506 DCHECK(success); 499 DCHECK(success);
507 if (!success) 500 if (!success)
508 return current; 501 return current;
509 while (iter.Advance()) { 502 while (iter.Advance()) {
510 if (iter.IsWord()) { 503 if (iter.IsWord()) {
(...skipping 11 matching lines...) Expand all
522 pos = iter.pos() - iter.GetString().length(); 515 pos = iter.pos() - iter.GetString().length();
523 } 516 }
524 } 517 }
525 } 518 }
526 519
527 return SelectionModel(pos, pos, SelectionModel::LEADING); 520 return SelectionModel(pos, pos, SelectionModel::LEADING);
528 } 521 }
529 522
530 SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current, 523 SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current,
531 BreakType break_type) { 524 BreakType break_type) {
525 if (text_.empty())
526 return SelectionModel(0, 0, SelectionModel::LEADING);
532 if (break_type == LINE_BREAK) 527 if (break_type == LINE_BREAK)
533 return SelectionModel(text().length(), 528 return SelectionModel(text().length(),
534 GetIndexOfPreviousGrapheme(text().length()), SelectionModel::TRAILING); 529 GetIndexOfPreviousGrapheme(text().length()), SelectionModel::TRAILING);
535 size_t pos = std::min(current.selection_end() + 1, text().length()); 530 size_t pos = std::min(current.selection_end() + 1, text().length());
536 if (break_type == CHARACTER_BREAK) 531 if (break_type == CHARACTER_BREAK)
537 return SelectionModel(pos, pos, SelectionModel::LEADING); 532 return SelectionModel(pos, pos, SelectionModel::LEADING);
538 533
539 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); 534 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
540 bool success = iter.Init(); 535 bool success = iter.Init();
541 DCHECK(success); 536 DCHECK(success);
(...skipping 27 matching lines...) Expand all
569 // Apply a selection style override to a copy of the style ranges. 564 // Apply a selection style override to a copy of the style ranges.
570 if (!EmptySelection()) { 565 if (!EmptySelection()) {
571 StyleRange selection_style(default_style_); 566 StyleRange selection_style(default_style_);
572 selection_style.foreground = kSelectedTextColor; 567 selection_style.foreground = kSelectedTextColor;
573 selection_style.range.set_start(MinOfSelection()); 568 selection_style.range.set_start(MinOfSelection());
574 selection_style.range.set_end(MaxOfSelection()); 569 selection_style.range.set_end(MaxOfSelection());
575 ApplyStyleRangeImpl(style_ranges, selection_style); 570 ApplyStyleRangeImpl(style_ranges, selection_style);
576 } 571 }
577 } 572 }
578 573
574 Point RenderText::ToTextPoint(const Point& point) {
575 Point p(point.Subtract(display_rect_.origin()));
576 p = p.Subtract(GetUpdatedDisplayOffset());
577 if (base::i18n::IsRTL())
578 p.Offset(GetStringWidth() - display_rect_.width() + 1, 0);
579 return p;
580 }
581
582 Point RenderText::ToViewPoint(const Point& point) {
583 Point p(point.Add(display_rect_.origin()));
584 p = p.Add(GetUpdatedDisplayOffset());
585 if (base::i18n::IsRTL())
586 p.Offset(display_rect_.width() - GetStringWidth() - 1, 0);
587 return p;
588 }
589
590 void RenderText::MoveCursorTo(size_t position, bool select) {
591 size_t caret_pos = GetIndexOfPreviousGrapheme(position);
592 SelectionModel::CaretPlacement placement = (caret_pos == position) ?
593 SelectionModel::LEADING : SelectionModel::TRAILING;
594 size_t selection_start = select ? GetSelectionStart() : position;
595 SelectionModel sel(selection_start, position, caret_pos, placement);
596 SetSelectionModel(sel);
597 }
598
579 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { 599 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) {
580 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || 600 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) ||
581 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); 601 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos]));
582 } 602 }
583 603
584 void RenderText::UpdateCachedBoundsAndOffset() { 604 void RenderText::UpdateCachedBoundsAndOffset() {
585 if (cached_bounds_and_offset_valid_) 605 if (cached_bounds_and_offset_valid_)
586 return; 606 return;
587 // First, set the valid flag true to calculate the current cursor bounds using 607 // First, set the valid flag true to calculate the current cursor bounds using
588 // the stale |display_offset_|. Applying |delta_offset| at the end of this 608 // the stale |display_offset_|. Applying |delta_offset| at the end of this
589 // function will set |cursor_bounds_| and |display_offset_| to correct values. 609 // function will set |cursor_bounds_| and |display_offset_| to correct values.
590 cached_bounds_and_offset_valid_ = true; 610 cached_bounds_and_offset_valid_ = true;
591 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); 611 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_);
592 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1)); 612 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1));
593 // Update |display_offset_| to ensure the current cursor is visible. 613 // Update |display_offset_| to ensure the current cursor is visible.
594 int display_width = display_rect_.width(); 614 int display_width = display_rect_.width();
595 int string_width = GetStringWidth(); 615 int string_width = GetStringWidth();
596 int delta_offset = 0; 616 int delta_offset = 0;
597 if (string_width < display_width) { 617 if (string_width < display_width) {
598 // Show all text whenever the text fits to the size. 618 // Show all text whenever the text fits to the size.
599 delta_offset = -display_offset_.x(); 619 delta_offset = -display_offset_.x();
600 } else if (cursor_bounds_.right() > display_rect_.right()) { 620 } else if (cursor_bounds_.right() > display_rect_.right()) {
621 // TODO(xji): when the character overflow is a RTL character, currently, if
622 // we pan cursor at the rightmost position, the entered RTL character is not
623 // displayed. Should pan cursor to show the last logical characters.
624 //
601 // Pan to show the cursor when it overflows to the right, 625 // Pan to show the cursor when it overflows to the right,
602 delta_offset = display_rect_.right() - cursor_bounds_.right(); 626 delta_offset = display_rect_.right() - cursor_bounds_.right();
603 } else if (cursor_bounds_.x() < display_rect_.x()) { 627 } else if (cursor_bounds_.x() < display_rect_.x()) {
628 // TODO(xji): have similar problem as above when overflow character is a
629 // LTR character.
630 //
604 // Pan to show the cursor when it overflows to the left. 631 // Pan to show the cursor when it overflows to the left.
605 delta_offset = display_rect_.x() - cursor_bounds_.x(); 632 delta_offset = display_rect_.x() - cursor_bounds_.x();
606 } 633 }
607 display_offset_.Offset(delta_offset, 0); 634 display_offset_.Offset(delta_offset, 0);
608 cursor_bounds_.Offset(delta_offset, 0); 635 cursor_bounds_.Offset(delta_offset, 0);
609 } 636 }
610 637
638 SelectionModel RenderText::GetSelectionModelForSelectionStart() {
639 size_t selection_start = GetSelectionStart();
640 size_t selection_end = GetCursorPosition();
641 if (selection_start < selection_end)
642 return SelectionModel(selection_start,
643 selection_start,
644 SelectionModel::LEADING);
645 else if (selection_start > selection_end)
646 return SelectionModel(selection_start,
647 GetIndexOfPreviousGrapheme(selection_start),
648 SelectionModel::TRAILING);
649 return selection_model_;
650 }
651
611 } // namespace gfx 652 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698