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 #include <climits> | 8 #include <climits> |
9 | 9 |
10 #include "base/i18n/break_iterator.h" | 10 #include "base/i18n/break_iterator.h" |
(...skipping 483 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
494 } | 494 } |
495 } | 495 } |
496 | 496 |
497 void RenderText::SetCursorPosition(size_t position) { | 497 void RenderText::SetCursorPosition(size_t position) { |
498 MoveCursorTo(position, false); | 498 MoveCursorTo(position, false); |
499 } | 499 } |
500 | 500 |
501 void RenderText::MoveCursor(BreakType break_type, | 501 void RenderText::MoveCursor(BreakType break_type, |
502 VisualCursorDirection direction, | 502 VisualCursorDirection direction, |
503 bool select) { | 503 bool select) { |
504 SelectionModel position(cursor_position(), selection_model_.caret_affinity()); | 504 SelectionModel cursor(cursor_position(), selection_model_.caret_affinity()); |
505 // Cancelling a selection moves to the edge of the selection. | 505 // Cancelling a selection moves to the edge of the selection. |
506 if (break_type != LINE_BREAK && !selection().is_empty() && !select) { | 506 if (break_type != LINE_BREAK && !selection().is_empty() && !select) { |
507 SelectionModel selection_start = GetSelectionModelForSelectionStart(); | 507 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
508 int start_x = GetCursorBounds(selection_start, true).x(); | 508 int start_x = GetCursorBounds(selection_start, true).x(); |
509 int cursor_x = GetCursorBounds(position, true).x(); | 509 int cursor_x = GetCursorBounds(cursor, true).x(); |
510 // Use the selection start if it is left (when |direction| is CURSOR_LEFT) | 510 // Use the selection start if it is left (when |direction| is CURSOR_LEFT) |
511 // or right (when |direction| is CURSOR_RIGHT) of the selection end. | 511 // or right (when |direction| is CURSOR_RIGHT) of the selection end. |
512 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) | 512 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) |
513 position = selection_start; | 513 cursor = selection_start; |
514 // For word breaks, use the nearest word boundary in the appropriate | 514 // Use the nearest word boundary in the proper |direction| for word breaks. |
515 // |direction|. | |
516 if (break_type == WORD_BREAK) | 515 if (break_type == WORD_BREAK) |
517 position = GetAdjacentSelectionModel(position, break_type, direction); | 516 cursor = GetAdjacentSelectionModel(cursor, break_type, direction); |
517 // Use an adjacent selection model if the cursor is not at a valid position. | |
518 if (!IsValidCursorIndex(cursor.caret_pos())) | |
519 cursor = GetAdjacentSelectionModel(cursor, CHARACTER_BREAK, direction); | |
518 } else { | 520 } else { |
519 position = GetAdjacentSelectionModel(position, break_type, direction); | 521 cursor = GetAdjacentSelectionModel(cursor, break_type, direction); |
520 } | 522 } |
521 if (select) | 523 if (select) |
522 position.set_selection_start(selection().start()); | 524 cursor.set_selection_start(selection().start()); |
523 MoveCursorTo(position); | 525 MoveCursorTo(cursor); |
524 } | 526 } |
525 | 527 |
526 bool RenderText::MoveCursorTo(const SelectionModel& model) { | 528 bool RenderText::MoveCursorTo(const SelectionModel& model) { |
527 // Enforce valid selection model components. | 529 // Enforce valid selection model components. |
528 size_t text_length = text().length(); | 530 size_t text_length = text().length(); |
529 Range range(std::min(model.selection().start(), text_length), | 531 Range range(std::min(model.selection().start(), text_length), |
530 std::min(model.caret_pos(), text_length)); | 532 std::min(model.caret_pos(), text_length)); |
531 // The current model only supports caret positions at valid character indices. | 533 // The current model only supports caret positions at valid cursor indices. |
532 if (!IsCursorablePosition(range.start()) || | 534 if (!IsValidCursorIndex(range.start()) || !IsValidCursorIndex(range.end())) |
533 !IsCursorablePosition(range.end())) | |
534 return false; | 535 return false; |
535 SelectionModel sel(range, model.caret_affinity()); | 536 SelectionModel sel(range, model.caret_affinity()); |
536 bool changed = sel != selection_model_; | 537 bool changed = sel != selection_model_; |
537 SetSelectionModel(sel); | 538 SetSelectionModel(sel); |
538 return changed; | 539 return changed; |
539 } | 540 } |
540 | 541 |
541 bool RenderText::MoveCursorTo(const Point& point, bool select) { | 542 bool RenderText::MoveCursorTo(const Point& point, bool select) { |
542 SelectionModel position = FindCursorPosition(point); | 543 SelectionModel position = FindCursorPosition(point); |
543 if (select) | 544 if (select) |
544 position.set_selection_start(selection().start()); | 545 position.set_selection_start(selection().start()); |
545 return MoveCursorTo(position); | 546 return MoveCursorTo(position); |
546 } | 547 } |
547 | 548 |
548 bool RenderText::SelectRange(const Range& range) { | 549 bool RenderText::SelectRange(const Range& range) { |
549 Range sel(std::min(range.start(), text().length()), | 550 Range sel(std::min(range.start(), text().length()), |
550 std::min(range.end(), text().length())); | 551 std::min(range.end(), text().length())); |
551 if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end())) | 552 // Allow selection bounds at valid indicies amid multi-character graphemes. |
553 if (!IsValidLogicalIndex(sel.start()) || !IsValidLogicalIndex(sel.end())) | |
552 return false; | 554 return false; |
553 LogicalCursorDirection affinity = | 555 LogicalCursorDirection affinity = |
554 (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD; | 556 (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD; |
555 SetSelectionModel(SelectionModel(sel, affinity)); | 557 SetSelectionModel(SelectionModel(sel, affinity)); |
556 return true; | 558 return true; |
557 } | 559 } |
558 | 560 |
559 bool RenderText::IsPointInSelection(const Point& point) { | 561 bool RenderText::IsPointInSelection(const Point& point) { |
560 if (selection().is_empty()) | 562 if (selection().is_empty()) |
561 return false; | 563 return false; |
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
760 if (clip_to_display_rect()) | 762 if (clip_to_display_rect()) |
761 canvas->Restore(); | 763 canvas->Restore(); |
762 } | 764 } |
763 | 765 |
764 void RenderText::DrawCursor(Canvas* canvas, const SelectionModel& position) { | 766 void RenderText::DrawCursor(Canvas* canvas, const SelectionModel& position) { |
765 // Paint cursor. Replace cursor is drawn as rectangle for now. | 767 // Paint cursor. Replace cursor is drawn as rectangle for now. |
766 // TODO(msw): Draw a better cursor with a better indication of association. | 768 // TODO(msw): Draw a better cursor with a better indication of association. |
767 canvas->FillRect(GetCursorBounds(position, true), cursor_color_); | 769 canvas->FillRect(GetCursorBounds(position, true), cursor_color_); |
768 } | 770 } |
769 | 771 |
772 bool RenderText::IsValidLogicalIndex(size_t index) { | |
773 // Check that the index is at a valid code point (not mid-surrgate-pair) and | |
774 // that it's not truncated from the layout text (its glyph may be shown). | |
775 return ((index == 0 || index == text().length()) || | |
776 (IsValidCodePointIndex(text(), index) && | |
777 index < GetLayoutText().length())); | |
Alexei Svitkine (slow)
2014/04/30 19:53:24
Hmm, I don't understand this logic anymore.
I kno
msw
2014/05/01 20:40:04
I've modified the check to be simpler, clearer, an
| |
778 } | |
779 | |
770 Rect RenderText::GetCursorBounds(const SelectionModel& caret, | 780 Rect RenderText::GetCursorBounds(const SelectionModel& caret, |
771 bool insert_mode) { | 781 bool insert_mode) { |
772 // TODO(ckocagil): Support multiline. This function should return the height | 782 // TODO(ckocagil): Support multiline. This function should return the height |
773 // of the line the cursor is on. |GetStringSize()| now returns | 783 // of the line the cursor is on. |GetStringSize()| now returns |
774 // the multiline size, eliminate its use here. | 784 // the multiline size, eliminate its use here. |
775 | 785 |
776 EnsureLayout(); | 786 EnsureLayout(); |
777 | |
778 size_t caret_pos = caret.caret_pos(); | 787 size_t caret_pos = caret.caret_pos(); |
779 DCHECK(IsCursorablePosition(caret_pos)); | 788 DCHECK(IsValidLogicalIndex(caret_pos)); |
780 // In overtype mode, ignore the affinity and always indicate that we will | 789 // In overtype mode, ignore the affinity and always indicate that we will |
781 // overtype the next character. | 790 // overtype the next character. |
782 LogicalCursorDirection caret_affinity = | 791 LogicalCursorDirection caret_affinity = |
783 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD; | 792 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD; |
784 int x = 0, width = 1; | 793 int x = 0, width = 1; |
785 Size size = GetStringSize(); | 794 Size size = GetStringSize(); |
786 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) { | 795 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) { |
787 // The caret is attached to the boundary. Always return a 1-dip width caret, | 796 // The caret is attached to the boundary. Always return a 1-dip width caret, |
788 // since there is nothing to overtype. | 797 // since there is nothing to overtype. |
789 if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0)) | 798 if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0)) |
(...skipping 20 matching lines...) Expand all Loading... | |
810 size_t RenderText::IndexOfAdjacentGrapheme(size_t index, | 819 size_t RenderText::IndexOfAdjacentGrapheme(size_t index, |
811 LogicalCursorDirection direction) { | 820 LogicalCursorDirection direction) { |
812 if (index > text().length()) | 821 if (index > text().length()) |
813 return text().length(); | 822 return text().length(); |
814 | 823 |
815 EnsureLayout(); | 824 EnsureLayout(); |
816 | 825 |
817 if (direction == CURSOR_FORWARD) { | 826 if (direction == CURSOR_FORWARD) { |
818 while (index < text().length()) { | 827 while (index < text().length()) { |
819 index++; | 828 index++; |
820 if (IsCursorablePosition(index)) | 829 if (IsValidCursorIndex(index)) |
821 return index; | 830 return index; |
822 } | 831 } |
823 return text().length(); | 832 return text().length(); |
824 } | 833 } |
825 | 834 |
826 while (index > 0) { | 835 while (index > 0) { |
827 index--; | 836 index--; |
828 if (IsCursorablePosition(index)) | 837 if (IsValidCursorIndex(index)) |
829 return index; | 838 return index; |
830 } | 839 } |
831 return 0; | 840 return 0; |
832 } | 841 } |
833 | 842 |
834 SelectionModel RenderText::GetSelectionModelForSelectionStart() { | 843 SelectionModel RenderText::GetSelectionModelForSelectionStart() { |
835 const Range& sel = selection(); | 844 const Range& sel = selection(); |
836 if (sel.is_empty()) | 845 if (sel.is_empty()) |
837 return selection_model_; | 846 return selection_model_; |
838 return SelectionModel(sel.start(), | 847 return SelectionModel(sel.start(), |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1101 size_t caret_pos, | 1110 size_t caret_pos, |
1102 LogicalCursorDirection caret_affinity) { | 1111 LogicalCursorDirection caret_affinity) { |
1103 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9). | 1112 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9). |
1104 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ? | 1113 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ? |
1105 caret_pos - 1 : caret_pos + 1; | 1114 caret_pos - 1 : caret_pos + 1; |
1106 return range.Contains(Range(caret_pos, adjacent)); | 1115 return range.Contains(Range(caret_pos, adjacent)); |
1107 } | 1116 } |
1108 | 1117 |
1109 void RenderText::MoveCursorTo(size_t position, bool select) { | 1118 void RenderText::MoveCursorTo(size_t position, bool select) { |
1110 size_t cursor = std::min(position, text().length()); | 1119 size_t cursor = std::min(position, text().length()); |
1111 if (IsCursorablePosition(cursor)) | 1120 if (IsValidCursorIndex(cursor)) |
1112 SetSelectionModel(SelectionModel( | 1121 SetSelectionModel(SelectionModel( |
1113 Range(select ? selection().start() : cursor, cursor), | 1122 Range(select ? selection().start() : cursor, cursor), |
1114 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD)); | 1123 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD)); |
1115 } | 1124 } |
1116 | 1125 |
1117 void RenderText::UpdateLayoutText() { | 1126 void RenderText::UpdateLayoutText() { |
1118 layout_text_.clear(); | 1127 layout_text_.clear(); |
1119 line_breaks_.SetMax(0); | 1128 line_breaks_.SetMax(0); |
1120 | 1129 |
1121 if (obscured_) { | 1130 if (obscured_) { |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1301 cursor_bounds_ += delta_offset; | 1310 cursor_bounds_ += delta_offset; |
1302 } | 1311 } |
1303 | 1312 |
1304 void RenderText::DrawSelection(Canvas* canvas) { | 1313 void RenderText::DrawSelection(Canvas* canvas) { |
1305 const std::vector<Rect> sel = GetSubstringBounds(selection()); | 1314 const std::vector<Rect> sel = GetSubstringBounds(selection()); |
1306 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) | 1315 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) |
1307 canvas->FillRect(*i, selection_background_focused_color_); | 1316 canvas->FillRect(*i, selection_background_focused_color_); |
1308 } | 1317 } |
1309 | 1318 |
1310 } // namespace gfx | 1319 } // namespace gfx |
OLD | NEW |