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

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: rebase (no changes) Created 8 years, 9 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 320 matching lines...) Expand 10 before | Expand all | Expand 10 after
331 // range ends beyond text_.length, it must be the last one. 331 // range ends beyond text_.length, it must be the last one.
332 style_ranges_.back().range.set_end(text_.length()); 332 style_ranges_.back().range.set_end(text_.length());
333 } 333 }
334 #ifndef NDEBUG 334 #ifndef NDEBUG
335 CheckStyleRanges(style_ranges_, text_.length()); 335 CheckStyleRanges(style_ranges_, text_.length());
336 #endif 336 #endif
337 cached_bounds_and_offset_valid_ = false; 337 cached_bounds_and_offset_valid_ = false;
338 338
339 // Reset selection model. SetText should always followed by SetSelectionModel 339 // Reset selection model. SetText should always followed by SetSelectionModel
340 // or SetCursorPosition in upper layer. 340 // or SetCursorPosition in upper layer.
341 SetSelectionModel(SelectionModel(0, 0, SelectionModel::LEADING)); 341 SetSelectionModel(SelectionModel());
342 342
343 UpdateLayout(); 343 UpdateLayout();
344 } 344 }
345 345
346 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) { 346 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) {
347 if (horizontal_alignment_ != alignment) { 347 if (horizontal_alignment_ != alignment) {
348 horizontal_alignment_ = alignment; 348 horizontal_alignment_ = alignment;
349 display_offset_ = Point(); 349 display_offset_ = Point();
350 cached_bounds_and_offset_valid_ = false; 350 cached_bounds_and_offset_valid_ = false;
351 } 351 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
384 UpdateLayout(); 384 UpdateLayout();
385 } 385 }
386 } 386 }
387 387
388 void RenderText::SetDisplayRect(const Rect& r) { 388 void RenderText::SetDisplayRect(const Rect& r) {
389 display_rect_ = r; 389 display_rect_ = r;
390 cached_bounds_and_offset_valid_ = false; 390 cached_bounds_and_offset_valid_ = false;
391 UpdateLayout(); 391 UpdateLayout();
392 } 392 }
393 393
394 size_t RenderText::GetCursorPosition() const {
395 return selection_model_.selection_end();
396 }
397
398 void RenderText::SetCursorPosition(size_t position) { 394 void RenderText::SetCursorPosition(size_t position) {
399 MoveCursorTo(position, false); 395 MoveCursorTo(position, false);
400 } 396 }
401 397
402 void RenderText::MoveCursor(BreakType break_type, 398 void RenderText::MoveCursor(BreakType break_type,
403 VisualCursorDirection direction, 399 VisualCursorDirection direction,
404 bool select) { 400 bool select) {
405 SelectionModel position(selection_model()); 401 SelectionModel position(cursor_position(), selection_model_.caret_affinity());
406 position.set_selection_start(GetCursorPosition());
407 // Cancelling a selection moves to the edge of the selection. 402 // Cancelling a selection moves to the edge of the selection.
408 if (break_type != LINE_BREAK && !EmptySelection() && !select) { 403 if (break_type != LINE_BREAK && !selection().is_empty() && !select) {
409 SelectionModel selection_start = GetSelectionModelForSelectionStart(); 404 SelectionModel selection_start = GetSelectionModelForSelectionStart();
410 int start_x = GetCursorBounds(selection_start, true).x(); 405 int start_x = GetCursorBounds(selection_start, true).x();
411 int cursor_x = GetCursorBounds(position, true).x(); 406 int cursor_x = GetCursorBounds(position, true).x();
412 // Use the selection start if it is left (when |direction| is CURSOR_LEFT) 407 // Use the selection start if it is left (when |direction| is CURSOR_LEFT)
413 // or right (when |direction| is CURSOR_RIGHT) of the selection end. 408 // or right (when |direction| is CURSOR_RIGHT) of the selection end.
414 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) 409 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x)
415 position = selection_start; 410 position = selection_start;
416 // For word breaks, use the nearest word boundary in the appropriate 411 // For word breaks, use the nearest word boundary in the appropriate
417 // |direction|. 412 // |direction|.
418 if (break_type == WORD_BREAK) 413 if (break_type == WORD_BREAK)
419 position = GetAdjacentSelectionModel(position, break_type, direction); 414 position = GetAdjacentSelectionModel(position, break_type, direction);
420 } else { 415 } else {
421 position = GetAdjacentSelectionModel(position, break_type, direction); 416 position = GetAdjacentSelectionModel(position, break_type, direction);
422 } 417 }
423 if (select) 418 if (select)
424 position.set_selection_start(GetSelectionStart()); 419 position.set_selection_start(selection().start());
425 MoveCursorTo(position); 420 MoveCursorTo(position);
426 } 421 }
427 422
428 bool RenderText::MoveCursorTo(const SelectionModel& model) { 423 bool RenderText::MoveCursorTo(const SelectionModel& model) {
429 SelectionModel sel(model); 424 // Enforce valid selection model components.
430 size_t text_length = text().length(); 425 size_t text_length = text().length();
431 // Enforce valid selection model components. 426 ui::Range range(std::min(model.selection().start(), text_length),
432 if (sel.selection_start() > text_length) 427 std::min(model.caret_pos(), text_length));
433 sel.set_selection_start(text_length);
434 if (sel.selection_end() > text_length)
435 sel.set_selection_end(text_length);
436 // The current model only supports caret positions at valid character indices. 428 // The current model only supports caret positions at valid character indices.
437 if (text_length == 0) { 429 if (!IsCursorablePosition(range.start()) ||
438 sel.set_caret_pos(0); 430 !IsCursorablePosition(range.end()))
439 sel.set_caret_placement(SelectionModel::LEADING);
440 } else if (sel.caret_pos() >= text_length) {
441 SelectionModel end_selection =
442 EdgeSelectionModel(GetVisualDirectionOfLogicalEnd());
443 sel.set_caret_pos(end_selection.caret_pos());
444 sel.set_caret_placement(end_selection.caret_placement());
445 }
446
447 if (!IsCursorablePosition(sel.selection_start()) ||
448 !IsCursorablePosition(sel.selection_end()) ||
449 !IsCursorablePosition(sel.caret_pos()))
450 return false; 431 return false;
451 432 SelectionModel sel(range, model.caret_affinity());
452 bool changed = !sel.Equals(selection_model_); 433 bool changed = sel != selection_model_;
453 SetSelectionModel(sel); 434 SetSelectionModel(sel);
454 return changed; 435 return changed;
455 } 436 }
456 437
457 bool RenderText::MoveCursorTo(const Point& point, bool select) { 438 bool RenderText::MoveCursorTo(const Point& point, bool select) {
458 SelectionModel selection = FindCursorPosition(point); 439 SelectionModel position = FindCursorPosition(point);
459 if (select) 440 if (select)
460 selection.set_selection_start(GetSelectionStart()); 441 position.set_selection_start(selection().start());
461 return MoveCursorTo(selection); 442 return MoveCursorTo(position);
462 } 443 }
463 444
464 bool RenderText::SelectRange(const ui::Range& range) { 445 bool RenderText::SelectRange(const ui::Range& range) {
465 size_t text_length = text().length(); 446 ui::Range sel(std::min(range.start(), text().length()),
466 size_t start = std::min(range.start(), text_length); 447 std::min(range.end(), text().length()));
467 size_t end = std::min(range.end(), text_length); 448 if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end()))
468
469 if (!IsCursorablePosition(start) || !IsCursorablePosition(end))
470 return false; 449 return false;
471 450 LogicalCursorDirection affinity =
472 size_t pos = end; 451 (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD;
473 SelectionModel::CaretPlacement placement = SelectionModel::LEADING; 452 SetSelectionModel(SelectionModel(sel, affinity));
474 if (start < end) {
475 pos = IndexOfAdjacentGrapheme(end, CURSOR_BACKWARD);
476 DCHECK_LT(pos, end);
477 placement = SelectionModel::TRAILING;
478 } else if (end == text_length) {
479 SelectionModel end_selection =
480 EdgeSelectionModel(GetVisualDirectionOfLogicalEnd());
481 pos = end_selection.caret_pos();
482 placement = end_selection.caret_placement();
483 }
484 SetSelectionModel(SelectionModel(start, end, pos, placement));
485 return true; 453 return true;
486 } 454 }
487 455
488 bool RenderText::IsPointInSelection(const Point& point) { 456 bool RenderText::IsPointInSelection(const Point& point) {
489 if (EmptySelection()) 457 if (selection().is_empty())
490 return false; 458 return false;
491 // TODO(xji): should this check whether the point is inside the visual 459 SelectionModel cursor = FindCursorPosition(point);
492 // selection bounds? In case of "abcFED", if "ED" is selected, |point| points 460 return RangeContainsCaret(
493 // to the right half of 'c', is the point in selection? 461 selection(), cursor.caret_pos(), cursor.caret_affinity());
494 size_t pos = FindCursorPosition(point).selection_end();
495 return (pos >= MinOfSelection() && pos < MaxOfSelection());
496 } 462 }
497 463
498 void RenderText::ClearSelection() { 464 void RenderText::ClearSelection() {
499 SelectionModel sel(selection_model()); 465 SetSelectionModel(SelectionModel(cursor_position(),
500 sel.set_selection_start(GetCursorPosition()); 466 selection_model_.caret_affinity()));
501 SetSelectionModel(sel);
502 } 467 }
503 468
504 void RenderText::SelectAll() { 469 void RenderText::SelectAll() {
505 SelectionModel sel = EdgeSelectionModel(CURSOR_RIGHT); 470 SelectionModel all;
506 sel.set_selection_start(EdgeSelectionModel(CURSOR_LEFT).selection_start()); 471 if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT)
507 SetSelectionModel(sel); 472 all = SelectionModel(ui::Range(0, text().length()), CURSOR_FORWARD);
473 else
474 all = SelectionModel(ui::Range(text().length(), 0), CURSOR_BACKWARD);
475 SetSelectionModel(all);
508 } 476 }
509 477
510 void RenderText::SelectWord() { 478 void RenderText::SelectWord() {
511 if (obscured_) { 479 if (obscured_) {
512 SelectAll(); 480 SelectAll();
513 return; 481 return;
514 } 482 }
515 483
516 size_t cursor_position = GetCursorPosition(); 484 size_t cursor_pos = cursor_position();
517 485
518 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); 486 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
519 bool success = iter.Init(); 487 bool success = iter.Init();
520 DCHECK(success); 488 DCHECK(success);
521 if (!success) 489 if (!success)
522 return; 490 return;
523 491
524 size_t selection_start = cursor_position; 492 size_t selection_start = cursor_pos;
525 for (; selection_start != 0; --selection_start) { 493 for (; selection_start != 0; --selection_start) {
526 if (iter.IsStartOfWord(selection_start) || 494 if (iter.IsStartOfWord(selection_start) ||
527 iter.IsEndOfWord(selection_start)) 495 iter.IsEndOfWord(selection_start))
528 break; 496 break;
529 } 497 }
530 498
531 if (selection_start == cursor_position) 499 if (selection_start == cursor_pos)
532 ++cursor_position; 500 ++cursor_pos;
533 501
534 for (; cursor_position < text().length(); ++cursor_position) { 502 for (; cursor_pos < text().length(); ++cursor_pos)
535 if (iter.IsEndOfWord(cursor_position) || 503 if (iter.IsEndOfWord(cursor_pos) || iter.IsStartOfWord(cursor_pos))
536 iter.IsStartOfWord(cursor_position))
537 break; 504 break;
538 }
539 505
540 MoveCursorTo(selection_start, false); 506 MoveCursorTo(selection_start, false);
541 MoveCursorTo(cursor_position, true); 507 MoveCursorTo(cursor_pos, true);
542 } 508 }
543 509
544 const ui::Range& RenderText::GetCompositionRange() const { 510 const ui::Range& RenderText::GetCompositionRange() const {
545 return composition_range_; 511 return composition_range_;
546 } 512 }
547 513
548 void RenderText::SetCompositionRange(const ui::Range& composition_range) { 514 void RenderText::SetCompositionRange(const ui::Range& composition_range) {
549 CHECK(!composition_range.IsValid() || 515 CHECK(!composition_range.IsValid() ||
550 ui::Range(0, text_.length()).Contains(composition_range)); 516 ui::Range(0, text_.length()).Contains(composition_range));
551 composition_range_.set_end(composition_range.end()); 517 composition_range_.set_end(composition_range.end());
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
597 563
598 DrawCursor(canvas); 564 DrawCursor(canvas);
599 565
600 if (!text().empty()) { 566 if (!text().empty()) {
601 TRACE_EVENT0("gfx", "RenderText::Draw draw text"); 567 TRACE_EVENT0("gfx", "RenderText::Draw draw text");
602 DrawVisualText(canvas); 568 DrawVisualText(canvas);
603 } 569 }
604 canvas->Restore(); 570 canvas->Restore();
605 } 571 }
606 572
573 Rect RenderText::GetCursorBounds(const SelectionModel& caret,
574 bool insert_mode) {
575 EnsureLayout();
576
577 size_t caret_pos = caret.caret_pos();
578 // In overtype mode, ignore the affinity and always indicate that we will
579 // overtype the next character.
580 LogicalCursorDirection caret_affinity =
581 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
582 int x = 0, width = 0, height = 0;
583 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
584 // The caret is attached to the boundary. Always return a zero-width caret,
585 // since there is nothing to overtype.
586 Size size = GetStringSize();
587 if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0))
588 x = size.width();
589 height = size.height();
590 } else {
591 size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ?
592 caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD);
593 ui::Range xspan;
594 GetGlyphBounds(grapheme_start, &xspan, &height);
595 if (insert_mode) {
596 x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start();
597 } else { // overtype mode
598 x = xspan.GetMin();
599 width = xspan.length();
600 }
601 }
602 height = std::min(height, display_rect().height());
603 int y = (display_rect().height() - height) / 2;
604 return Rect(ToViewPoint(Point(x, y)), Size(width, height));
605 }
606
607 const Rect& RenderText::GetUpdatedCursorBounds() { 607 const Rect& RenderText::GetUpdatedCursorBounds() {
608 UpdateCachedBoundsAndOffset(); 608 UpdateCachedBoundsAndOffset();
609 return cursor_bounds_; 609 return cursor_bounds_;
610 } 610 }
611 611
612 SelectionModel RenderText::GetSelectionModelForSelectionStart() { 612 SelectionModel RenderText::GetSelectionModelForSelectionStart() {
613 size_t selection_start = GetSelectionStart(); 613 const ui::Range& sel = selection();
614 size_t selection_end = GetCursorPosition(); 614 if (sel.is_empty())
615 if (selection_start < selection_end) 615 return selection_model_;
616 return SelectionModel(selection_start, 616 return SelectionModel(sel.start(),
617 selection_start, 617 sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
618 SelectionModel::LEADING);
619 else if (selection_start > selection_end)
620 return SelectionModel(
621 selection_start,
622 IndexOfAdjacentGrapheme(selection_start, CURSOR_BACKWARD),
623 SelectionModel::TRAILING);
624 return selection_model_;
625 } 618 }
626 619
627 RenderText::RenderText() 620 RenderText::RenderText()
628 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), 621 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
629 cursor_enabled_(true), 622 cursor_enabled_(true),
630 cursor_visible_(false), 623 cursor_visible_(false),
631 insert_mode_(true), 624 insert_mode_(true),
632 cursor_color_(kDefaultCursorColor), 625 cursor_color_(kDefaultCursorColor),
633 focused_(false), 626 focused_(false),
634 composition_range_(ui::Range::InvalidRange()), 627 composition_range_(ui::Range::InvalidRange()),
(...skipping 16 matching lines...) Expand all
651 EnsureLayout(); 644 EnsureLayout();
652 645
653 if (break_type == LINE_BREAK || text().empty()) 646 if (break_type == LINE_BREAK || text().empty())
654 return EdgeSelectionModel(direction); 647 return EdgeSelectionModel(direction);
655 if (break_type == CHARACTER_BREAK) 648 if (break_type == CHARACTER_BREAK)
656 return AdjacentCharSelectionModel(current, direction); 649 return AdjacentCharSelectionModel(current, direction);
657 DCHECK(break_type == WORD_BREAK); 650 DCHECK(break_type == WORD_BREAK);
658 return AdjacentWordSelectionModel(current, direction); 651 return AdjacentWordSelectionModel(current, direction);
659 } 652 }
660 653
654 SelectionModel RenderText::EdgeSelectionModel(
655 VisualCursorDirection direction) {
656 if (direction == GetVisualDirectionOfLogicalEnd())
657 return SelectionModel(text().length(), CURSOR_FORWARD);
658 return SelectionModel(0, CURSOR_BACKWARD);
659 }
660
661 void RenderText::SetSelectionModel(const SelectionModel& model) { 661 void RenderText::SetSelectionModel(const SelectionModel& model) {
662 DCHECK_LE(model.selection_start(), text().length()); 662 DCHECK_LE(model.selection().GetMax(), text().length());
663 selection_model_.set_selection_start(model.selection_start()); 663 selection_model_ = model;
664 DCHECK_LE(model.selection_end(), text().length());
665 selection_model_.set_selection_end(model.selection_end());
666 DCHECK_LT(model.caret_pos(), std::max<size_t>(text().length(), 1));
667 selection_model_.set_caret_pos(model.caret_pos());
668 selection_model_.set_caret_placement(model.caret_placement());
669
670 cached_bounds_and_offset_valid_ = false; 664 cached_bounds_and_offset_valid_ = false;
671 } 665 }
672 666
673 string16 RenderText::GetDisplayText() const { 667 string16 RenderText::GetDisplayText() const {
674 if (!obscured_) 668 if (!obscured_)
675 return text_; 669 return text_;
676 size_t obscured_text_length = 670 size_t obscured_text_length =
677 static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length())); 671 static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length()));
678 return string16(obscured_text_length, kPasswordReplacementChar); 672 return string16(obscured_text_length, kPasswordReplacementChar);
679 } 673 }
680 674
681 void RenderText::ApplyCompositionAndSelectionStyles( 675 void RenderText::ApplyCompositionAndSelectionStyles(
682 StyleRanges* style_ranges) { 676 StyleRanges* style_ranges) {
683 // TODO(msw): This pattern ought to be reconsidered; what about composition 677 // TODO(msw): This pattern ought to be reconsidered; what about composition
684 // and selection overlaps, retain existing local style features? 678 // and selection overlaps, retain existing local style features?
685 // Apply a composition style override to a copy of the style ranges. 679 // Apply a composition style override to a copy of the style ranges.
686 if (composition_range_.IsValid() && !composition_range_.is_empty()) { 680 if (composition_range_.IsValid() && !composition_range_.is_empty()) {
687 StyleRange composition_style(default_style_); 681 StyleRange composition_style(default_style_);
688 composition_style.underline = true; 682 composition_style.underline = true;
689 composition_style.range.set_start(composition_range_.start()); 683 composition_style.range = composition_range_;
690 composition_style.range.set_end(composition_range_.end());
691 ApplyStyleRangeImpl(style_ranges, composition_style); 684 ApplyStyleRangeImpl(style_ranges, composition_style);
692 } 685 }
693 // Apply a selection style override to a copy of the style ranges. 686 // Apply a selection style override to a copy of the style ranges.
694 if (!EmptySelection()) { 687 if (!selection().is_empty()) {
695 StyleRange selection_style(default_style_); 688 StyleRange selection_style(default_style_);
696 selection_style.foreground = NativeTheme::instance()->GetSystemColor( 689 selection_style.foreground = NativeTheme::instance()->GetSystemColor(
697 NativeTheme::kColorId_TextfieldSelectionColor); 690 NativeTheme::kColorId_TextfieldSelectionColor);
698 selection_style.range.set_start(MinOfSelection()); 691 selection_style.range = ui::Range(selection().GetMin(),
699 selection_style.range.set_end(MaxOfSelection()); 692 selection().GetMax());
700 ApplyStyleRangeImpl(style_ranges, selection_style); 693 ApplyStyleRangeImpl(style_ranges, selection_style);
701 } 694 }
702 // Apply replacement-mode style override to a copy of the style ranges. 695 // Apply replacement-mode style override to a copy of the style ranges.
703 // 696 //
704 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to 697 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to
705 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline 698 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline
706 // character to be drawn correctly, we will need to re-layout the text. It's 699 // character to be drawn correctly, we will need to re-layout the text. It's
707 // not practical to do layout on every cursor blink. We need to fix Windows 700 // not practical to do layout on every cursor blink. We need to fix Windows
708 // port to apply styles during drawing phase like Linux port does. 701 // port to apply styles during drawing phase like Linux port does.
709 // http://crbug.com/110109 702 // http://crbug.com/110109
710 if (!insert_mode_ && cursor_visible() && focused()) { 703 if (!insert_mode_ && cursor_visible() && focused()) {
711 StyleRange replacement_mode_style(default_style_); 704 StyleRange replacement_mode_style(default_style_);
712 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor( 705 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor(
713 NativeTheme::kColorId_TextfieldSelectionColor); 706 NativeTheme::kColorId_TextfieldSelectionColor);
714 size_t cursor = GetCursorPosition(); 707 size_t cursor = cursor_position();
715 replacement_mode_style.range.set_start(cursor); 708 replacement_mode_style.range.set_start(cursor);
716 replacement_mode_style.range.set_end( 709 replacement_mode_style.range.set_end(
717 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD)); 710 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD));
718 ApplyStyleRangeImpl(style_ranges, replacement_mode_style); 711 ApplyStyleRangeImpl(style_ranges, replacement_mode_style);
719 } 712 }
720 } 713 }
721 714
722 Point RenderText::GetTextOrigin() { 715 Point RenderText::GetTextOrigin() {
723 Point origin = display_rect().origin(); 716 Point origin = display_rect().origin();
724 origin = origin.Add(GetUpdatedDisplayOffset()); 717 origin = origin.Add(GetUpdatedDisplayOffset());
725 origin = origin.Add(GetAlignmentOffset()); 718 origin = origin.Add(GetAlignmentOffset());
726 return origin; 719 return origin;
727 } 720 }
728 721
729 Point RenderText::ToTextPoint(const Point& point) { 722 Point RenderText::ToTextPoint(const Point& point) {
730 return point.Subtract(GetTextOrigin()); 723 return point.Subtract(GetTextOrigin());
731 } 724 }
732 725
733 Point RenderText::ToViewPoint(const Point& point) { 726 Point RenderText::ToViewPoint(const Point& point) {
734 return point.Add(GetTextOrigin()); 727 return point.Add(GetTextOrigin());
735 } 728 }
736 729
737 int RenderText::GetContentWidth() { 730 int RenderText::GetContentWidth() {
738 return GetStringWidth() + (cursor_enabled_ ? 1 : 0); 731 return GetStringSize().width() + (cursor_enabled_ ? 1 : 0);
739 } 732 }
740 733
741 Point RenderText::GetAlignmentOffset() { 734 Point RenderText::GetAlignmentOffset() {
742 if (horizontal_alignment() != ALIGN_LEFT) { 735 if (horizontal_alignment() != ALIGN_LEFT) {
743 int x_offset = display_rect().width() - GetContentWidth(); 736 int x_offset = display_rect().width() - GetContentWidth();
744 if (horizontal_alignment() == ALIGN_CENTER) 737 if (horizontal_alignment() == ALIGN_CENTER)
745 x_offset /= 2; 738 x_offset /= 2;
746 return Point(x_offset, 0); 739 return Point(x_offset, 0);
747 } 740 }
748 return Point(); 741 return Point();
749 } 742 }
750 743
751 Point RenderText::GetOriginForSkiaDrawing() { 744 Point RenderText::GetOriginForSkiaDrawing() {
752 Point origin(GetTextOrigin()); 745 Point origin(GetTextOrigin());
753 // TODO(msw): Establish a vertical baseline for strings of mixed font heights. 746 // TODO(msw): Establish a vertical baseline for strings of mixed font heights.
754 const Font& font = GetFont(); 747 const Font& font = GetFont();
755 int height = font.GetHeight(); 748 int height = font.GetHeight();
756 DCHECK_LE(height, display_rect().height()); 749 DCHECK_LE(height, display_rect().height());
757 // Center the text vertically in the display area. 750 // Center the text vertically in the display area.
758 origin.Offset(0, (display_rect().height() - height) / 2); 751 origin.Offset(0, (display_rect().height() - height) / 2);
759 // Offset by the font size to account for Skia expecting y to be the bottom. 752 // Offset by the font size to account for Skia expecting y to be the bottom.
760 origin.Offset(0, font.GetFontSize()); 753 origin.Offset(0, font.GetFontSize());
761 return origin; 754 return origin;
762 } 755 }
763 756
764 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { 757 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
765 if (!fade_head() && !fade_tail()) 758 if (!fade_head() && !fade_tail())
766 return; 759 return;
767 760
768 const int text_width = GetStringWidth(); 761 const int text_width = GetStringSize().width();
769 const int display_width = display_rect().width(); 762 const int display_width = display_rect().width();
770 763
771 // If the text fits as-is, no need to fade. 764 // If the text fits as-is, no need to fade.
772 if (text_width <= display_width) 765 if (text_width <= display_width)
773 return; 766 return;
774 767
775 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); 768 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width);
776 if (gradient_width == 0) 769 if (gradient_width == 0)
777 return; 770 return;
778 771
(...skipping 22 matching lines...) Expand all
801 794
802 const SkColor color = default_style().foreground; 795 const SkColor color = default_style().foreground;
803 SkAutoTUnref<SkShader> shader( 796 SkAutoTUnref<SkShader> shader(
804 CreateFadeShader(text_rect, left_part, right_part, color)); 797 CreateFadeShader(text_rect, left_part, right_part, color));
805 if (shader.get()) { 798 if (shader.get()) {
806 // |renderer| adds its own ref. So don't |release()| it from the ref ptr. 799 // |renderer| adds its own ref. So don't |release()| it from the ref ptr.
807 renderer->SetShader(shader.get()); 800 renderer->SetShader(shader.get());
808 } 801 }
809 } 802 }
810 803
804 // static
805 bool RenderText::RangeContainsCaret(const ui::Range& range,
806 size_t caret_pos,
807 LogicalCursorDirection caret_affinity) {
808 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
809 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
810 caret_pos - 1 : caret_pos + 1;
811 return range.Contains(ui::Range(caret_pos, adjacent));
812 }
813
811 void RenderText::MoveCursorTo(size_t position, bool select) { 814 void RenderText::MoveCursorTo(size_t position, bool select) {
812 size_t cursor = std::min(position, text().length()); 815 size_t cursor = std::min(position, text().length());
813 size_t caret_pos = IndexOfAdjacentGrapheme(cursor, CURSOR_BACKWARD); 816 if (IsCursorablePosition(cursor))
814 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? 817 SetSelectionModel(SelectionModel(
815 SelectionModel::LEADING : SelectionModel::TRAILING; 818 ui::Range(select ? selection().start() : cursor, cursor),
816 size_t selection_start = select ? GetSelectionStart() : cursor; 819 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
817 if (IsCursorablePosition(cursor)) {
818 SelectionModel sel(selection_start, cursor, caret_pos, placement);
819 SetSelectionModel(sel);
820 }
821 } 820 }
822 821
823 void RenderText::UpdateCachedBoundsAndOffset() { 822 void RenderText::UpdateCachedBoundsAndOffset() {
824 if (cached_bounds_and_offset_valid_) 823 if (cached_bounds_and_offset_valid_)
825 return; 824 return;
826 825
827 // First, set the valid flag true to calculate the current cursor bounds using 826 // First, set the valid flag true to calculate the current cursor bounds using
828 // the stale |display_offset_|. Applying |delta_offset| at the end of this 827 // the stale |display_offset_|. Applying |delta_offset| at the end of this
829 // function will set |cursor_bounds_| and |display_offset_| to correct values. 828 // function will set |cursor_bounds_| and |display_offset_| to correct values.
830 cached_bounds_and_offset_valid_ = true; 829 cached_bounds_and_offset_valid_ = true;
(...skipping 28 matching lines...) Expand all
859 const int offset = negate_rtl * display_offset_.x(); 858 const int offset = negate_rtl * display_offset_.x();
860 if (display_width > (content_width + offset)) 859 if (display_width > (content_width + offset))
861 delta_offset = negate_rtl * (display_width - (content_width + offset)); 860 delta_offset = negate_rtl * (display_width - (content_width + offset));
862 } 861 }
863 862
864 display_offset_.Offset(delta_offset, 0); 863 display_offset_.Offset(delta_offset, 0);
865 cursor_bounds_.Offset(delta_offset, 0); 864 cursor_bounds_.Offset(delta_offset, 0);
866 } 865 }
867 866
868 void RenderText::DrawSelection(Canvas* canvas) { 867 void RenderText::DrawSelection(Canvas* canvas) {
869 std::vector<Rect> sel = GetSubstringBounds( 868 std::vector<Rect> sel = GetSubstringBounds(selection());
870 GetSelectionStart(), GetCursorPosition());
871 NativeTheme::ColorId color_id = focused() ? 869 NativeTheme::ColorId color_id = focused() ?
872 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused : 870 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused :
873 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused; 871 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused;
874 SkColor color = NativeTheme::instance()->GetSystemColor(color_id); 872 SkColor color = NativeTheme::instance()->GetSystemColor(color_id);
875 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) 873 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
876 canvas->FillRect(*i, color); 874 canvas->FillRect(*i, color);
877 } 875 }
878 876
879 void RenderText::DrawCursor(Canvas* canvas) { 877 void RenderText::DrawCursor(Canvas* canvas) {
880 // Paint cursor. Replace cursor is drawn as rectangle for now. 878 // Paint cursor. Replace cursor is drawn as rectangle for now.
881 // TODO(msw): Draw a better cursor with a better indication of association. 879 // TODO(msw): Draw a better cursor with a better indication of association.
882 if (cursor_enabled() && cursor_visible() && focused()) { 880 if (cursor_enabled() && cursor_visible() && focused()) {
883 const Rect& bounds = GetUpdatedCursorBounds(); 881 const Rect& bounds = GetUpdatedCursorBounds();
884 if (bounds.width() != 0) 882 if (bounds.width() != 0)
885 canvas->FillRect(bounds, cursor_color_); 883 canvas->FillRect(bounds, cursor_color_);
886 else 884 else
887 canvas->DrawRect(bounds, cursor_color_); 885 canvas->DrawRect(bounds, cursor_color_);
888 } 886 }
889 } 887 }
890 888
891 } // namespace gfx 889 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text.h ('k') | ui/gfx/render_text_linux.h » ('j') | ui/gfx/render_text_win.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698