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

Side by Side Diff: ui/gfx/render_text.cc

Issue 252563003: Fix Views inline autocomplete with multi-char graphemes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Extrapolate on the comment. Created 6 years, 7 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
« no previous file with comments | « ui/gfx/render_text.h ('k') | ui/gfx/render_text_mac.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 #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
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
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 //
776 // Indices within truncated text are disallowed so users can easily interact
777 // with the underlying truncated text using the ellipsis as a proxy. This lets
778 // users select all text, select the truncated text, and transition from the
779 // last rendered glyph to the end of the text without getting invisible cursor
780 // positions nor needing unbounded arrow key presses to traverse the ellipsis.
781 return index == 0 || index == text().length() ||
782 (index < text().length() &&
783 (truncate_length_ == 0 || index < truncate_length_) &&
784 IsValidCodePointIndex(text(), index));
785 }
786
770 Rect RenderText::GetCursorBounds(const SelectionModel& caret, 787 Rect RenderText::GetCursorBounds(const SelectionModel& caret,
771 bool insert_mode) { 788 bool insert_mode) {
772 // TODO(ckocagil): Support multiline. This function should return the height 789 // TODO(ckocagil): Support multiline. This function should return the height
773 // of the line the cursor is on. |GetStringSize()| now returns 790 // of the line the cursor is on. |GetStringSize()| now returns
774 // the multiline size, eliminate its use here. 791 // the multiline size, eliminate its use here.
775 792
776 EnsureLayout(); 793 EnsureLayout();
777
778 size_t caret_pos = caret.caret_pos(); 794 size_t caret_pos = caret.caret_pos();
779 DCHECK(IsCursorablePosition(caret_pos)); 795 DCHECK(IsValidLogicalIndex(caret_pos));
780 // In overtype mode, ignore the affinity and always indicate that we will 796 // In overtype mode, ignore the affinity and always indicate that we will
781 // overtype the next character. 797 // overtype the next character.
782 LogicalCursorDirection caret_affinity = 798 LogicalCursorDirection caret_affinity =
783 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD; 799 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
784 int x = 0, width = 1; 800 int x = 0, width = 1;
785 Size size = GetStringSize(); 801 Size size = GetStringSize();
786 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) { 802 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, 803 // The caret is attached to the boundary. Always return a 1-dip width caret,
788 // since there is nothing to overtype. 804 // since there is nothing to overtype.
789 if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0)) 805 if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0))
(...skipping 20 matching lines...) Expand all
810 size_t RenderText::IndexOfAdjacentGrapheme(size_t index, 826 size_t RenderText::IndexOfAdjacentGrapheme(size_t index,
811 LogicalCursorDirection direction) { 827 LogicalCursorDirection direction) {
812 if (index > text().length()) 828 if (index > text().length())
813 return text().length(); 829 return text().length();
814 830
815 EnsureLayout(); 831 EnsureLayout();
816 832
817 if (direction == CURSOR_FORWARD) { 833 if (direction == CURSOR_FORWARD) {
818 while (index < text().length()) { 834 while (index < text().length()) {
819 index++; 835 index++;
820 if (IsCursorablePosition(index)) 836 if (IsValidCursorIndex(index))
821 return index; 837 return index;
822 } 838 }
823 return text().length(); 839 return text().length();
824 } 840 }
825 841
826 while (index > 0) { 842 while (index > 0) {
827 index--; 843 index--;
828 if (IsCursorablePosition(index)) 844 if (IsValidCursorIndex(index))
829 return index; 845 return index;
830 } 846 }
831 return 0; 847 return 0;
832 } 848 }
833 849
834 SelectionModel RenderText::GetSelectionModelForSelectionStart() { 850 SelectionModel RenderText::GetSelectionModelForSelectionStart() {
835 const Range& sel = selection(); 851 const Range& sel = selection();
836 if (sel.is_empty()) 852 if (sel.is_empty())
837 return selection_model_; 853 return selection_model_;
838 return SelectionModel(sel.start(), 854 return SelectionModel(sel.start(),
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
1101 size_t caret_pos, 1117 size_t caret_pos,
1102 LogicalCursorDirection caret_affinity) { 1118 LogicalCursorDirection caret_affinity) {
1103 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9). 1119 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
1104 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ? 1120 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
1105 caret_pos - 1 : caret_pos + 1; 1121 caret_pos - 1 : caret_pos + 1;
1106 return range.Contains(Range(caret_pos, adjacent)); 1122 return range.Contains(Range(caret_pos, adjacent));
1107 } 1123 }
1108 1124
1109 void RenderText::MoveCursorTo(size_t position, bool select) { 1125 void RenderText::MoveCursorTo(size_t position, bool select) {
1110 size_t cursor = std::min(position, text().length()); 1126 size_t cursor = std::min(position, text().length());
1111 if (IsCursorablePosition(cursor)) 1127 if (IsValidCursorIndex(cursor))
1112 SetSelectionModel(SelectionModel( 1128 SetSelectionModel(SelectionModel(
1113 Range(select ? selection().start() : cursor, cursor), 1129 Range(select ? selection().start() : cursor, cursor),
1114 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD)); 1130 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
1115 } 1131 }
1116 1132
1117 void RenderText::UpdateLayoutText() { 1133 void RenderText::UpdateLayoutText() {
1118 layout_text_.clear(); 1134 layout_text_.clear();
1119 line_breaks_.SetMax(0); 1135 line_breaks_.SetMax(0);
1120 1136
1121 if (obscured_) { 1137 if (obscured_) {
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
1301 cursor_bounds_ += delta_offset; 1317 cursor_bounds_ += delta_offset;
1302 } 1318 }
1303 1319
1304 void RenderText::DrawSelection(Canvas* canvas) { 1320 void RenderText::DrawSelection(Canvas* canvas) {
1305 const std::vector<Rect> sel = GetSubstringBounds(selection()); 1321 const std::vector<Rect> sel = GetSubstringBounds(selection());
1306 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) 1322 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
1307 canvas->FillRect(*i, selection_background_focused_color_); 1323 canvas->FillRect(*i, selection_background_focused_color_);
1308 } 1324 }
1309 1325
1310 } // namespace gfx 1326 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text.h ('k') | ui/gfx/render_text_mac.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698