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