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

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

Issue 9390022: Simplify handling of BiDi cursor movement (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: make set_selection_start private, fix accidental inclusion of local hacks Created 8 years, 10 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) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/debug/trace_event.h" 9 #include "base/debug/trace_event.h"
10 #include "base/i18n/break_iterator.h" 10 #include "base/i18n/break_iterator.h"
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 // range ends beyond text_.length, it must be the last one. 325 // range ends beyond text_.length, it must be the last one.
326 style_ranges_.back().range.set_end(text_.length()); 326 style_ranges_.back().range.set_end(text_.length());
327 } 327 }
328 #ifndef NDEBUG 328 #ifndef NDEBUG
329 CheckStyleRanges(style_ranges_, text_.length()); 329 CheckStyleRanges(style_ranges_, text_.length());
330 #endif 330 #endif
331 cached_bounds_and_offset_valid_ = false; 331 cached_bounds_and_offset_valid_ = false;
332 332
333 // Reset selection model. SetText should always followed by SetSelectionModel 333 // Reset selection model. SetText should always followed by SetSelectionModel
334 // or SetCursorPosition in upper layer. 334 // or SetCursorPosition in upper layer.
335 SetSelectionModel(SelectionModel(0, 0, SelectionModel::LEADING)); 335 SetSelectionModel(SelectionModel());
336 336
337 UpdateLayout(); 337 UpdateLayout();
338 } 338 }
339 339
340 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) { 340 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) {
341 if (horizontal_alignment_ != alignment) { 341 if (horizontal_alignment_ != alignment) {
342 horizontal_alignment_ = alignment; 342 horizontal_alignment_ = alignment;
343 display_offset_ = Point(); 343 display_offset_ = Point();
344 cached_bounds_and_offset_valid_ = false; 344 cached_bounds_and_offset_valid_ = false;
345 } 345 }
(...skipping 24 matching lines...) Expand all
370 insert_mode_ = !insert_mode_; 370 insert_mode_ = !insert_mode_;
371 cached_bounds_and_offset_valid_ = false; 371 cached_bounds_and_offset_valid_ = false;
372 } 372 }
373 373
374 void RenderText::SetDisplayRect(const Rect& r) { 374 void RenderText::SetDisplayRect(const Rect& r) {
375 display_rect_ = r; 375 display_rect_ = r;
376 cached_bounds_and_offset_valid_ = false; 376 cached_bounds_and_offset_valid_ = false;
377 UpdateLayout(); 377 UpdateLayout();
378 } 378 }
379 379
380 size_t RenderText::GetCursorPosition() const {
381 return selection_model_.selection_end();
382 }
383
384 void RenderText::SetCursorPosition(size_t position) { 380 void RenderText::SetCursorPosition(size_t position) {
385 MoveCursorTo(position, false); 381 MoveCursorTo(position, false);
386 } 382 }
387 383
388 void RenderText::MoveCursor(BreakType break_type, 384 void RenderText::MoveCursor(BreakType break_type,
389 VisualCursorDirection direction, 385 VisualCursorDirection direction,
390 bool select) { 386 bool select) {
391 SelectionModel position(selection_model()); 387 SelectionModel position(cursor_position(), selection_model_.caret_affinity());
392 position.set_selection_start(GetCursorPosition());
393 // Cancelling a selection moves to the edge of the selection. 388 // Cancelling a selection moves to the edge of the selection.
394 if (break_type != LINE_BREAK && !EmptySelection() && !select) { 389 if (break_type != LINE_BREAK && !selection().is_empty() && !select) {
395 SelectionModel selection_start = GetSelectionModelForSelectionStart(); 390 SelectionModel selection_start = GetSelectionModelForSelectionStart();
396 int start_x = GetCursorBounds(selection_start, true).x(); 391 int start_x = GetCursorBounds(selection_start, true).x();
397 int cursor_x = GetCursorBounds(position, true).x(); 392 int cursor_x = GetCursorBounds(position, true).x();
398 // Use the selection start if it is left (when |direction| is CURSOR_LEFT) 393 // Use the selection start if it is left (when |direction| is CURSOR_LEFT)
399 // or right (when |direction| is CURSOR_RIGHT) of the selection end. 394 // or right (when |direction| is CURSOR_RIGHT) of the selection end.
400 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) 395 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x)
401 position = selection_start; 396 position = selection_start;
402 // For word breaks, use the nearest word boundary in the appropriate 397 // For word breaks, use the nearest word boundary in the appropriate
403 // |direction|. 398 // |direction|.
404 if (break_type == WORD_BREAK) 399 if (break_type == WORD_BREAK)
405 position = GetAdjacentSelectionModel(position, break_type, direction); 400 position = GetAdjacentSelectionModel(position, break_type, direction);
406 } else { 401 } else {
407 position = GetAdjacentSelectionModel(position, break_type, direction); 402 position = GetAdjacentSelectionModel(position, break_type, direction);
408 } 403 }
409 if (select) 404 if (select)
410 position.set_selection_start(GetSelectionStart()); 405 position.set_selection_start(selection().start());
411 MoveCursorTo(position); 406 MoveCursorTo(position);
412 } 407 }
413 408
414 bool RenderText::MoveCursorTo(const SelectionModel& model) { 409 bool RenderText::MoveCursorTo(const SelectionModel& model) {
415 SelectionModel sel(model); 410 // Enforce valid selection model components.
416 size_t text_length = text().length(); 411 size_t text_length = text().length();
417 // Enforce valid selection model components. 412 LogicalCursorDirection affinity = model.caret_affinity();
418 if (sel.selection_start() > text_length) 413 if (model.caret_pos() > text_length) {
419 sel.set_selection_start(text_length); 414 // Make the cursor appear at the edge of the text, not next to the final
420 if (sel.selection_end() > text_length) 415 // grapheme.
421 sel.set_selection_end(text_length); 416 affinity = CURSOR_FORWARD;
422 // The current model only supports caret positions at valid character indices.
423 if (text_length == 0) {
424 sel.set_caret_pos(0);
425 sel.set_caret_placement(SelectionModel::LEADING);
426 } else if (sel.caret_pos() >= text_length) {
427 SelectionModel end_selection =
428 EdgeSelectionModel(GetVisualDirectionOfLogicalEnd());
429 sel.set_caret_pos(end_selection.caret_pos());
430 sel.set_caret_placement(end_selection.caret_placement());
431 } 417 }
432 418
433 if (!IsCursorablePosition(sel.selection_start()) || 419 ui::Range range(std::min(model.selection().start(), text_length),
434 !IsCursorablePosition(sel.selection_end()) || 420 std::min(model.caret_pos(), text_length));
435 !IsCursorablePosition(sel.caret_pos())) 421 // The current model only supports caret positions at valid character indices.
422 if (!IsCursorablePosition(range.start()) ||
423 !IsCursorablePosition(range.end()))
436 return false; 424 return false;
437 425 SelectionModel sel(range, affinity);
438 bool changed = !sel.Equals(selection_model_); 426 bool changed = sel != selection_model_;
439 SetSelectionModel(sel); 427 SetSelectionModel(sel);
440 return changed; 428 return changed;
441 } 429 }
442 430
443 bool RenderText::MoveCursorTo(const Point& point, bool select) { 431 bool RenderText::MoveCursorTo(const Point& point, bool select) {
444 SelectionModel selection = FindCursorPosition(point); 432 SelectionModel position = FindCursorPosition(point);
445 if (select) 433 if (select)
446 selection.set_selection_start(GetSelectionStart()); 434 position.set_selection_start(selection().start());
447 return MoveCursorTo(selection); 435 return MoveCursorTo(position);
448 } 436 }
449 437
450 bool RenderText::SelectRange(const ui::Range& range) { 438 bool RenderText::SelectRange(const ui::Range& range) {
451 size_t text_length = text().length(); 439 ui::Range sel(std::min(range.start(), text().length()),
452 size_t start = std::min(range.start(), text_length); 440 std::min(range.end(), text().length()));
453 size_t end = std::min(range.end(), text_length); 441 if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end()))
454
455 if (!IsCursorablePosition(start) || !IsCursorablePosition(end))
456 return false; 442 return false;
457 443 LogicalCursorDirection affinity =
458 size_t pos = end; 444 (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD;
459 SelectionModel::CaretPlacement placement = SelectionModel::LEADING; 445 SetSelectionModel(SelectionModel(sel, affinity));
460 if (start < end) {
461 pos = IndexOfAdjacentGrapheme(end, CURSOR_BACKWARD);
462 DCHECK_LT(pos, end);
463 placement = SelectionModel::TRAILING;
464 } else if (end == text_length) {
465 SelectionModel end_selection =
466 EdgeSelectionModel(GetVisualDirectionOfLogicalEnd());
467 pos = end_selection.caret_pos();
468 placement = end_selection.caret_placement();
469 }
470 SetSelectionModel(SelectionModel(start, end, pos, placement));
471 return true; 446 return true;
472 } 447 }
473 448
474 bool RenderText::IsPointInSelection(const Point& point) { 449 bool RenderText::IsPointInSelection(const Point& point) {
475 if (EmptySelection()) 450 if (selection().is_empty())
476 return false; 451 return false;
477 // TODO(xji): should this check whether the point is inside the visual 452 SelectionModel cursor = FindCursorPosition(point);
478 // selection bounds? In case of "abcFED", if "ED" is selected, |point| points 453 return RangeContainsCaret(
479 // to the right half of 'c', is the point in selection? 454 selection(), cursor.caret_pos(), cursor.caret_affinity());
480 size_t pos = FindCursorPosition(point).selection_end();
481 return (pos >= MinOfSelection() && pos < MaxOfSelection());
482 } 455 }
483 456
484 void RenderText::ClearSelection() { 457 void RenderText::ClearSelection() {
485 SelectionModel sel(selection_model()); 458 SetSelectionModel(SelectionModel(selection_model_.caret_pos(),
msw 2012/02/28 22:51:32 Use cursor_position() instead of selection_model_.
benrg 2012/03/07 01:04:44 Done.
486 sel.set_selection_start(GetCursorPosition()); 459 selection_model_.caret_affinity()));
487 SetSelectionModel(sel);
488 } 460 }
489 461
490 void RenderText::SelectAll() { 462 void RenderText::SelectAll() {
491 SelectionModel sel = EdgeSelectionModel(CURSOR_RIGHT); 463 SelectionModel all;
492 sel.set_selection_start(EdgeSelectionModel(CURSOR_LEFT).selection_start()); 464 if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT)
493 SetSelectionModel(sel); 465 all = SelectionModel(ui::Range(0, text().length()), CURSOR_FORWARD);
466 else
467 all = SelectionModel(ui::Range(text().length(), 0), CURSOR_BACKWARD);
468 SetSelectionModel(all);
494 } 469 }
495 470
496 void RenderText::SelectWord() { 471 void RenderText::SelectWord() {
497 size_t cursor_position = GetCursorPosition(); 472 size_t cursor_position = selection_model_.caret_pos();
msw 2012/02/28 22:51:32 Use cursor_position() instead of selection_model_.
benrg 2012/03/07 01:04:44 Done.
498 473
499 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); 474 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
500 bool success = iter.Init(); 475 bool success = iter.Init();
501 DCHECK(success); 476 DCHECK(success);
502 if (!success) 477 if (!success)
503 return; 478 return;
504 479
505 size_t selection_start = cursor_position; 480 size_t selection_start = cursor_position;
506 for (; selection_start != 0; --selection_start) { 481 for (; selection_start != 0; --selection_start) {
507 if (iter.IsStartOfWord(selection_start) || 482 if (iter.IsStartOfWord(selection_start) ||
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 553
579 DrawCursor(canvas); 554 DrawCursor(canvas);
580 555
581 if (!text().empty()) { 556 if (!text().empty()) {
582 TRACE_EVENT0("gfx", "RenderText::Draw draw text"); 557 TRACE_EVENT0("gfx", "RenderText::Draw draw text");
583 DrawVisualText(canvas); 558 DrawVisualText(canvas);
584 } 559 }
585 canvas->Restore(); 560 canvas->Restore();
586 } 561 }
587 562
563 Rect RenderText::GetCursorBounds(const SelectionModel& caret,
564 bool insert_mode) {
565 EnsureLayout();
566
567 size_t caret_pos = caret.caret_pos();
568 // In overstrike mode, ignore the affinity and always indicate that we will
xji 2012/02/24 20:16:52 nit: I looked up in wiki, http://en.wikipedia.org/
benrg 2012/03/07 01:04:44 Done.
569 // overstrike the next character.
570 LogicalCursorDirection caret_affinity =
571 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
572 int x, width, height;
msw 2012/02/28 22:51:32 Init these locals to 0.
benrg 2012/03/07 01:04:44 For the record, I don't like doing this because it
573 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
574 // The caret is attached to the boundary. Always return a zero-width caret,
575 // since there is nothing to overstrike.
576 Size size = GetStringSize();
577 if ((GetTextDirection() == base::i18n::LEFT_TO_RIGHT) == (caret_pos == 0))
578 x = 0;
msw 2012/02/28 22:51:32 With x init to 0, rewrite this to just handle the
benrg 2012/03/07 01:04:44 Done.
579 else
580 x = size.width();
581 width = 0;
msw 2012/02/28 22:51:32 Unnecessary with width init to 0.
benrg 2012/03/07 01:04:44 Done.
582 height = size.height();
583 } else {
584 size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ?
585 caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD);
586 ui::Range xspan;
587 GetGlyphBounds(grapheme_start, &xspan, &height);
588 if (insert_mode) {
589 x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start();
590 width = 0;
msw 2012/02/28 22:51:32 Unnecessary with width init to 0.
benrg 2012/03/07 01:04:44 Done.
591 } else { // overstrike mode
592 x = xspan.GetMin();
593 width = xspan.length();
594 }
595 }
596 height = std::min(height, display_rect().height());
597 int y = (display_rect().height() - height) / 2;
598 return Rect(ToViewPoint(Point(x, y)), Size(width, height));
599 }
600
588 const Rect& RenderText::GetUpdatedCursorBounds() { 601 const Rect& RenderText::GetUpdatedCursorBounds() {
589 UpdateCachedBoundsAndOffset(); 602 UpdateCachedBoundsAndOffset();
590 return cursor_bounds_; 603 return cursor_bounds_;
591 } 604 }
592 605
593 SelectionModel RenderText::GetSelectionModelForSelectionStart() { 606 SelectionModel RenderText::GetSelectionModelForSelectionStart() {
594 size_t selection_start = GetSelectionStart(); 607 const ui::Range& selection = selection_model_.selection();
msw 2012/02/28 22:51:32 Use selection() instead of selection_model_.select
benrg 2012/03/07 01:04:44 Done.
595 size_t selection_end = GetCursorPosition(); 608 if (selection.is_empty())
596 if (selection_start < selection_end) 609 return selection_model_;
597 return SelectionModel(selection_start, 610 return SelectionModel(
598 selection_start, 611 selection.start(),
599 SelectionModel::LEADING); 612 selection.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
600 else if (selection_start > selection_end)
601 return SelectionModel(
602 selection_start,
603 IndexOfAdjacentGrapheme(selection_start, CURSOR_BACKWARD),
604 SelectionModel::TRAILING);
605 return selection_model_;
606 } 613 }
607 614
608 RenderText::RenderText() 615 RenderText::RenderText()
609 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), 616 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
610 cursor_enabled_(true), 617 cursor_enabled_(true),
611 cursor_visible_(false), 618 cursor_visible_(false),
612 insert_mode_(true), 619 insert_mode_(true),
613 focused_(false), 620 focused_(false),
614 composition_range_(ui::Range::InvalidRange()), 621 composition_range_(ui::Range::InvalidRange()),
615 fade_head_(false), 622 fade_head_(false),
(...skipping 13 matching lines...) Expand all
629 EnsureLayout(); 636 EnsureLayout();
630 637
631 if (break_type == LINE_BREAK || text().empty()) 638 if (break_type == LINE_BREAK || text().empty())
632 return EdgeSelectionModel(direction); 639 return EdgeSelectionModel(direction);
633 if (break_type == CHARACTER_BREAK) 640 if (break_type == CHARACTER_BREAK)
634 return AdjacentCharSelectionModel(current, direction); 641 return AdjacentCharSelectionModel(current, direction);
635 DCHECK(break_type == WORD_BREAK); 642 DCHECK(break_type == WORD_BREAK);
636 return AdjacentWordSelectionModel(current, direction); 643 return AdjacentWordSelectionModel(current, direction);
637 } 644 }
638 645
646 SelectionModel RenderText::EdgeSelectionModel(
647 VisualCursorDirection direction) {
648 if (direction == GetVisualDirectionOfLogicalEnd())
649 return SelectionModel(text().length(), CURSOR_FORWARD);
650 return SelectionModel(0, CURSOR_BACKWARD);
651 }
652
639 void RenderText::SetSelectionModel(const SelectionModel& model) { 653 void RenderText::SetSelectionModel(const SelectionModel& model) {
640 DCHECK_LE(model.selection_start(), text().length()); 654 DCHECK_LE(model.selection().GetMax(), text().length());
641 selection_model_.set_selection_start(model.selection_start()); 655 selection_model_ = model;
642 DCHECK_LE(model.selection_end(), text().length());
643 selection_model_.set_selection_end(model.selection_end());
644 DCHECK_LT(model.caret_pos(), std::max<size_t>(text().length(), 1));
645 selection_model_.set_caret_pos(model.caret_pos());
646 selection_model_.set_caret_placement(model.caret_placement());
647
648 cached_bounds_and_offset_valid_ = false; 656 cached_bounds_and_offset_valid_ = false;
649 } 657 }
650 658
651 void RenderText::ApplyCompositionAndSelectionStyles( 659 void RenderText::ApplyCompositionAndSelectionStyles(
652 StyleRanges* style_ranges) { 660 StyleRanges* style_ranges) {
653 // TODO(msw): This pattern ought to be reconsidered; what about composition 661 // TODO(msw): This pattern ought to be reconsidered; what about composition
654 // and selection overlaps, retain existing local style features? 662 // and selection overlaps, retain existing local style features?
655 // Apply a composition style override to a copy of the style ranges. 663 // Apply a composition style override to a copy of the style ranges.
656 if (composition_range_.IsValid() && !composition_range_.is_empty()) { 664 if (composition_range_.IsValid() && !composition_range_.is_empty()) {
657 StyleRange composition_style(default_style_); 665 StyleRange composition_style(default_style_);
658 composition_style.underline = true; 666 composition_style.underline = true;
659 composition_style.range.set_start(composition_range_.start()); 667 composition_style.range = composition_range_;
660 composition_style.range.set_end(composition_range_.end());
661 ApplyStyleRangeImpl(style_ranges, composition_style); 668 ApplyStyleRangeImpl(style_ranges, composition_style);
662 } 669 }
663 // Apply a selection style override to a copy of the style ranges. 670 // Apply a selection style override to a copy of the style ranges.
664 if (!EmptySelection()) { 671 if (!selection().is_empty()) {
665 StyleRange selection_style(default_style_); 672 StyleRange selection_style(default_style_);
666 selection_style.foreground = NativeTheme::instance()->GetSystemColor( 673 selection_style.foreground = NativeTheme::instance()->GetSystemColor(
667 NativeTheme::kColorId_TextfieldSelectionColor); 674 NativeTheme::kColorId_TextfieldSelectionColor);
668 selection_style.range.set_start(MinOfSelection()); 675 selection_style.range = ui::Range(selection().GetMin(),
669 selection_style.range.set_end(MaxOfSelection()); 676 selection().GetMax());
670 ApplyStyleRangeImpl(style_ranges, selection_style); 677 ApplyStyleRangeImpl(style_ranges, selection_style);
671 } 678 }
672 // Apply replacement-mode style override to a copy of the style ranges. 679 // Apply replacement-mode style override to a copy of the style ranges.
673 // 680 //
674 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to 681 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to
675 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline 682 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline
676 // character to be drawn correctly, we will need to re-layout the text. It's 683 // character to be drawn correctly, we will need to re-layout the text. It's
677 // not practical to do layout on every cursor blink. We need to fix Windows 684 // not practical to do layout on every cursor blink. We need to fix Windows
678 // port to apply styles during drawing phase like Linux port does. 685 // port to apply styles during drawing phase like Linux port does.
679 // http://crbug.com/110109 686 // http://crbug.com/110109
680 if (!insert_mode_ && cursor_visible() && focused()) { 687 if (!insert_mode_ && cursor_visible() && focused()) {
681 StyleRange replacement_mode_style(default_style_); 688 StyleRange replacement_mode_style(default_style_);
682 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor( 689 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor(
683 NativeTheme::kColorId_TextfieldSelectionColor); 690 NativeTheme::kColorId_TextfieldSelectionColor);
684 size_t cursor = GetCursorPosition(); 691 size_t cursor = cursor_position();
685 replacement_mode_style.range.set_start(cursor); 692 replacement_mode_style.range.set_start(cursor);
686 replacement_mode_style.range.set_end( 693 replacement_mode_style.range.set_end(
687 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD)); 694 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD));
688 ApplyStyleRangeImpl(style_ranges, replacement_mode_style); 695 ApplyStyleRangeImpl(style_ranges, replacement_mode_style);
689 } 696 }
690 } 697 }
691 698
692 Point RenderText::GetTextOrigin() { 699 Point RenderText::GetTextOrigin() {
693 Point origin = display_rect().origin(); 700 Point origin = display_rect().origin();
694 origin = origin.Add(GetUpdatedDisplayOffset()); 701 origin = origin.Add(GetUpdatedDisplayOffset());
695 origin = origin.Add(GetAlignmentOffset()); 702 origin = origin.Add(GetAlignmentOffset());
696 return origin; 703 return origin;
697 } 704 }
698 705
699 Point RenderText::ToTextPoint(const Point& point) { 706 Point RenderText::ToTextPoint(const Point& point) {
700 return point.Subtract(GetTextOrigin()); 707 return point.Subtract(GetTextOrigin());
701 } 708 }
702 709
703 Point RenderText::ToViewPoint(const Point& point) { 710 Point RenderText::ToViewPoint(const Point& point) {
704 return point.Add(GetTextOrigin()); 711 return point.Add(GetTextOrigin());
705 } 712 }
706 713
707 int RenderText::GetContentWidth() { 714 int RenderText::GetContentWidth() {
708 return GetStringWidth() + (cursor_enabled_ ? 1 : 0); 715 return GetStringSize().width() + (cursor_enabled_ ? 1 : 0);
709 } 716 }
710 717
711 Point RenderText::GetAlignmentOffset() { 718 Point RenderText::GetAlignmentOffset() {
712 if (horizontal_alignment() != ALIGN_LEFT) { 719 if (horizontal_alignment() != ALIGN_LEFT) {
713 int x_offset = display_rect().width() - GetContentWidth(); 720 int x_offset = display_rect().width() - GetContentWidth();
714 if (horizontal_alignment() == ALIGN_CENTER) 721 if (horizontal_alignment() == ALIGN_CENTER)
715 x_offset /= 2; 722 x_offset /= 2;
716 return Point(x_offset, 0); 723 return Point(x_offset, 0);
717 } 724 }
718 return Point(); 725 return Point();
719 } 726 }
720 727
721 Point RenderText::GetOriginForSkiaDrawing() { 728 Point RenderText::GetOriginForSkiaDrawing() {
722 Point origin(GetTextOrigin()); 729 Point origin(GetTextOrigin());
723 // TODO(msw): Establish a vertical baseline for strings of mixed font heights. 730 // TODO(msw): Establish a vertical baseline for strings of mixed font heights.
724 const Font& font = GetFont(); 731 const Font& font = GetFont();
725 int height = font.GetHeight(); 732 int height = font.GetHeight();
726 DCHECK_LE(height, display_rect().height()); 733 DCHECK_LE(height, display_rect().height());
727 // Center the text vertically in the display area. 734 // Center the text vertically in the display area.
728 origin.Offset(0, (display_rect().height() - height) / 2); 735 origin.Offset(0, (display_rect().height() - height) / 2);
729 // Offset by the font size to account for Skia expecting y to be the bottom. 736 // Offset by the font size to account for Skia expecting y to be the bottom.
730 origin.Offset(0, font.GetFontSize()); 737 origin.Offset(0, font.GetFontSize());
731 return origin; 738 return origin;
732 } 739 }
733 740
734 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { 741 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
735 if (!fade_head() && !fade_tail()) 742 if (!fade_head() && !fade_tail())
736 return; 743 return;
737 744
738 const int text_width = GetStringWidth(); 745 const int text_width = GetStringSize().width();
739 const int display_width = display_rect().width(); 746 const int display_width = display_rect().width();
740 747
741 // If the text fits as-is, no need to fade. 748 // If the text fits as-is, no need to fade.
742 if (text_width <= display_width) 749 if (text_width <= display_width)
743 return; 750 return;
744 751
745 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); 752 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width);
746 if (gradient_width == 0) 753 if (gradient_width == 0)
747 return; 754 return;
748 755
(...skipping 22 matching lines...) Expand all
771 778
772 const SkColor color = default_style().foreground; 779 const SkColor color = default_style().foreground;
773 SkAutoTUnref<SkShader> shader( 780 SkAutoTUnref<SkShader> shader(
774 CreateFadeShader(text_rect, left_part, right_part, color)); 781 CreateFadeShader(text_rect, left_part, right_part, color));
775 if (shader.get()) { 782 if (shader.get()) {
776 // |renderer| adds its own ref. So don't |release()| it from the ref ptr. 783 // |renderer| adds its own ref. So don't |release()| it from the ref ptr.
777 renderer->SetShader(shader.get()); 784 renderer->SetShader(shader.get());
778 } 785 }
779 } 786 }
780 787
788 // static
789 bool RenderText::RangeContainsCaret(const ui::Range& range,
790 size_t caret_pos,
791 LogicalCursorDirection caret_affinity) {
792 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
793 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
794 caret_pos - 1 : caret_pos + 1;
795 return range.Contains(ui::Range(caret_pos, adjacent));
796 }
797
781 void RenderText::MoveCursorTo(size_t position, bool select) { 798 void RenderText::MoveCursorTo(size_t position, bool select) {
782 size_t cursor = std::min(position, text().length()); 799 size_t cursor = std::min(position, text().length());
783 size_t caret_pos = IndexOfAdjacentGrapheme(cursor, CURSOR_BACKWARD);
784 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ?
785 SelectionModel::LEADING : SelectionModel::TRAILING;
786 size_t selection_start = select ? GetSelectionStart() : cursor;
787 if (IsCursorablePosition(cursor)) { 800 if (IsCursorablePosition(cursor)) {
788 SelectionModel sel(selection_start, cursor, caret_pos, placement); 801 ui::Range pos(cursor);
msw 2012/02/28 22:51:32 nit: optionally: ui::Range pos(select ? selection(
benrg 2012/03/07 01:04:44 Done.
789 SetSelectionModel(sel); 802 if (select)
803 pos.set_start(selection().start());
804 LogicalCursorDirection affinity = cursor ? CURSOR_BACKWARD : CURSOR_FORWARD;
msw 2012/02/28 22:51:32 nit: I'd prefer explicit "(cursor != 0) ?", but al
benrg 2012/03/07 01:04:44 Do you like my rewritten version?
805 SetSelectionModel(SelectionModel(pos, affinity));
790 } 806 }
791 } 807 }
792 808
793 void RenderText::UpdateCachedBoundsAndOffset() { 809 void RenderText::UpdateCachedBoundsAndOffset() {
794 if (cached_bounds_and_offset_valid_) 810 if (cached_bounds_and_offset_valid_)
795 return; 811 return;
796 812
797 // First, set the valid flag true to calculate the current cursor bounds using 813 // First, set the valid flag true to calculate the current cursor bounds using
798 // the stale |display_offset_|. Applying |delta_offset| at the end of this 814 // the stale |display_offset_|. Applying |delta_offset| at the end of this
799 // function will set |cursor_bounds_| and |display_offset_| to correct values. 815 // function will set |cursor_bounds_| and |display_offset_| to correct values.
(...skipping 29 matching lines...) Expand all
829 const int offset = negate_rtl * display_offset_.x(); 845 const int offset = negate_rtl * display_offset_.x();
830 if (display_width > (content_width + offset)) 846 if (display_width > (content_width + offset))
831 delta_offset = negate_rtl * (display_width - (content_width + offset)); 847 delta_offset = negate_rtl * (display_width - (content_width + offset));
832 } 848 }
833 849
834 display_offset_.Offset(delta_offset, 0); 850 display_offset_.Offset(delta_offset, 0);
835 cursor_bounds_.Offset(delta_offset, 0); 851 cursor_bounds_.Offset(delta_offset, 0);
836 } 852 }
837 853
838 void RenderText::DrawSelection(Canvas* canvas) { 854 void RenderText::DrawSelection(Canvas* canvas) {
839 std::vector<Rect> sel = GetSubstringBounds( 855 std::vector<Rect> sel = GetSubstringBounds(selection());
840 GetSelectionStart(), GetCursorPosition());
841 NativeTheme::ColorId color_id = focused() ? 856 NativeTheme::ColorId color_id = focused() ?
842 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused : 857 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused :
843 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused; 858 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused;
844 SkColor color = NativeTheme::instance()->GetSystemColor(color_id); 859 SkColor color = NativeTheme::instance()->GetSystemColor(color_id);
845 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) 860 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
846 canvas->FillRect(*i, color); 861 canvas->FillRect(*i, color);
847 } 862 }
848 863
849 void RenderText::DrawCursor(Canvas* canvas) { 864 void RenderText::DrawCursor(Canvas* canvas) {
850 // Paint cursor. Replace cursor is drawn as rectangle for now. 865 // Paint cursor. Replace cursor is drawn as rectangle for now.
851 // TODO(msw): Draw a better cursor with a better indication of association. 866 // TODO(msw): Draw a better cursor with a better indication of association.
852 if (cursor_enabled() && cursor_visible() && focused()) { 867 if (cursor_enabled() && cursor_visible() && focused()) {
853 const Rect& bounds = GetUpdatedCursorBounds(); 868 const Rect& bounds = GetUpdatedCursorBounds();
854 if (bounds.width() != 0) 869 if (bounds.width() != 0)
855 canvas->FillRect(bounds, kCursorColor); 870 canvas->FillRect(bounds, kCursorColor);
856 else 871 else
857 canvas->DrawRect(bounds, kCursorColor); 872 canvas->DrawRect(bounds, kCursorColor);
858 } 873 }
859 } 874 }
860 875
861 } // namespace gfx 876 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698