OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |