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

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

Powered by Google App Engine
This is Rietveld 408576698