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

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

Issue 2541313002: RenderTextHarfBuzz: Add support for multi line text selection. (Closed)
Patch Set: Nits. Created 4 years 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
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 <limits.h> 7 #include <limits.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <climits> 10 #include <climits>
(...skipping 405 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 params.subpixel_rendering != FontRenderParams::SUBPIXEL_RENDERING_NONE); 416 params.subpixel_rendering != FontRenderParams::SUBPIXEL_RENDERING_NONE);
417 paint->setSubpixelText(params.subpixel_positioning); 417 paint->setSubpixelText(params.subpixel_positioning);
418 paint->setAutohinted(params.autohinter); 418 paint->setAutohinted(params.autohinter);
419 paint->setHinting(FontRenderParamsHintingToSkPaintHinting(params.hinting)); 419 paint->setHinting(FontRenderParamsHintingToSkPaintHinting(params.hinting));
420 } 420 }
421 421
422 } // namespace internal 422 } // namespace internal
423 423
424 // static 424 // static
425 constexpr base::char16 RenderText::kPasswordReplacementChar; 425 constexpr base::char16 RenderText::kPasswordReplacementChar;
426 constexpr bool RenderText::kDragToEndIfOutsideVerticalBounds;
426 427
427 RenderText::~RenderText() { 428 RenderText::~RenderText() {
428 } 429 }
429 430
430 // static 431 // static
431 RenderText* RenderText::CreateInstance() { 432 RenderText* RenderText::CreateInstance() {
432 #if defined(OS_MACOSX) 433 #if defined(OS_MACOSX)
433 const bool use_native = !base::CommandLine::ForCurrentProcess()->HasSwitch( 434 const bool use_native = !base::CommandLine::ForCurrentProcess()->HasSwitch(
434 switches::kEnableHarfBuzzRenderText); 435 switches::kEnableHarfBuzzRenderText);
435 if (use_native) 436 if (use_native)
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after
808 text_direction_ = base::i18n::UNKNOWN_DIRECTION; 809 text_direction_ = base::i18n::UNKNOWN_DIRECTION;
809 cached_bounds_and_offset_valid_ = false; 810 cached_bounds_and_offset_valid_ = false;
810 OnLayoutTextAttributeChanged(false); 811 OnLayoutTextAttributeChanged(false);
811 } 812 }
812 813
813 base::i18n::TextDirection RenderText::GetDisplayTextDirection() { 814 base::i18n::TextDirection RenderText::GetDisplayTextDirection() {
814 return GetTextDirection(GetDisplayText()); 815 return GetTextDirection(GetDisplayText());
815 } 816 }
816 817
817 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() { 818 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
818 return GetDisplayTextDirection() == base::i18n::LEFT_TO_RIGHT ? 819 return GetDisplayTextDirection() == base::i18n::LEFT_TO_RIGHT ? CURSOR_RIGHT
819 CURSOR_RIGHT : CURSOR_LEFT; 820 : CURSOR_LEFT;
821 }
822
823 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalBeginning() {
824 return GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT ? CURSOR_RIGHT
825 : CURSOR_LEFT;
820 } 826 }
821 827
822 SizeF RenderText::GetStringSizeF() { 828 SizeF RenderText::GetStringSizeF() {
823 return SizeF(GetStringSize()); 829 return SizeF(GetStringSize());
824 } 830 }
825 831
826 float RenderText::GetContentWidthF() { 832 float RenderText::GetContentWidthF() {
827 const float string_size = GetStringSizeF().width(); 833 const float string_size = GetStringSizeF().width();
828 // The cursor is drawn one pixel beyond the int-enclosed text bounds. 834 // The cursor is drawn one pixel beyond the int-enclosed text bounds.
829 return cursor_enabled_ ? std::ceil(string_size) + 1 : string_size; 835 return cursor_enabled_ ? std::ceil(string_size) + 1 : string_size;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
887 (index < text().length() && 893 (index < text().length() &&
888 (truncate_length_ == 0 || index < truncate_length_) && 894 (truncate_length_ == 0 || index < truncate_length_) &&
889 IsValidCodePointIndex(text(), index)); 895 IsValidCodePointIndex(text(), index));
890 } 896 }
891 897
892 Rect RenderText::GetCursorBounds(const SelectionModel& caret, 898 Rect RenderText::GetCursorBounds(const SelectionModel& caret,
893 bool insert_mode) { 899 bool insert_mode) {
894 // TODO(ckocagil): Support multiline. This function should return the height 900 // TODO(ckocagil): Support multiline. This function should return the height
895 // of the line the cursor is on. |GetStringSize()| now returns 901 // of the line the cursor is on. |GetStringSize()| now returns
896 // the multiline size, eliminate its use here. 902 // the multiline size, eliminate its use here.
897 903 DCHECK(!multiline_);
898 EnsureLayout(); 904 EnsureLayout();
899 size_t caret_pos = caret.caret_pos(); 905 size_t caret_pos = caret.caret_pos();
900 DCHECK(IsValidLogicalIndex(caret_pos)); 906 DCHECK(IsValidLogicalIndex(caret_pos));
901 // In overtype mode, ignore the affinity and always indicate that we will 907 // In overtype mode, ignore the affinity and always indicate that we will
902 // overtype the next character. 908 // overtype the next character.
903 LogicalCursorDirection caret_affinity = 909 LogicalCursorDirection caret_affinity =
904 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD; 910 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
905 int x = 0, width = 1; 911 int x = 0, width = 1;
906 Size size = GetStringSize(); 912 Size size = GetStringSize();
907 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) { 913 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
955 } 961 }
956 962
957 SelectionModel RenderText::GetSelectionModelForSelectionStart() const { 963 SelectionModel RenderText::GetSelectionModelForSelectionStart() const {
958 const Range& sel = selection(); 964 const Range& sel = selection();
959 if (sel.is_empty()) 965 if (sel.is_empty())
960 return selection_model_; 966 return selection_model_;
961 return SelectionModel(sel.start(), 967 return SelectionModel(sel.start(),
962 sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD); 968 sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
963 } 969 }
964 970
971 std::vector<Rect> RenderText::GetSubstringBoundsForTesting(
972 const gfx::Range& range) {
973 return GetSubstringBounds(range);
974 }
975
965 const Vector2d& RenderText::GetUpdatedDisplayOffset() { 976 const Vector2d& RenderText::GetUpdatedDisplayOffset() {
966 UpdateCachedBoundsAndOffset(); 977 UpdateCachedBoundsAndOffset();
967 return display_offset_; 978 return display_offset_;
968 } 979 }
969 980
970 void RenderText::SetDisplayOffset(int horizontal_offset) { 981 void RenderText::SetDisplayOffset(int horizontal_offset) {
971 const int extra_content = GetContentWidth() - display_rect_.width(); 982 const int extra_content = GetContentWidth() - display_rect_.width();
972 const int cursor_width = cursor_enabled_ ? 1 : 0; 983 const int cursor_width = cursor_enabled_ ? 1 : 0;
973 984
974 int min_offset = 0; 985 int min_offset = 0;
(...skipping 22 matching lines...) Expand all
997 horizontal_offset = min_offset; 1008 horizontal_offset = min_offset;
998 else if (horizontal_offset > max_offset) 1009 else if (horizontal_offset > max_offset)
999 horizontal_offset = max_offset; 1010 horizontal_offset = max_offset;
1000 1011
1001 cached_bounds_and_offset_valid_ = true; 1012 cached_bounds_and_offset_valid_ = true;
1002 display_offset_.set_x(horizontal_offset); 1013 display_offset_.set_x(horizontal_offset);
1003 cursor_bounds_ = GetCursorBounds(selection_model_, true); 1014 cursor_bounds_ = GetCursorBounds(selection_model_, true);
1004 } 1015 }
1005 1016
1006 Vector2d RenderText::GetLineOffset(size_t line_number) { 1017 Vector2d RenderText::GetLineOffset(size_t line_number) {
1018 EnsureLayout();
1007 Vector2d offset = display_rect().OffsetFromOrigin(); 1019 Vector2d offset = display_rect().OffsetFromOrigin();
1008 // TODO(ckocagil): Apply the display offset for multiline scrolling. 1020 // TODO(ckocagil): Apply the display offset for multiline scrolling.
1009 if (!multiline()) 1021 if (!multiline()) {
1010 offset.Add(GetUpdatedDisplayOffset()); 1022 offset.Add(GetUpdatedDisplayOffset());
1011 else 1023 } else {
1024 DCHECK_LT(line_number, lines().size());
1012 offset.Add(Vector2d(0, lines_[line_number].preceding_heights)); 1025 offset.Add(Vector2d(0, lines_[line_number].preceding_heights));
1026 }
1013 offset.Add(GetAlignmentOffset(line_number)); 1027 offset.Add(GetAlignmentOffset(line_number));
1014 return offset; 1028 return offset;
1015 } 1029 }
1016 1030
1017 bool RenderText::GetDecoratedWordAtPoint(const Point& point, 1031 bool RenderText::GetDecoratedWordAtPoint(const Point& point,
1018 DecoratedText* decorated_word, 1032 DecoratedText* decorated_word,
1019 Point* baseline_point) { 1033 Point* baseline_point) {
1020 // FindCursorPosition doesn't currently support multiline. See 1034 // FindCursorPosition doesn't currently support multiline. See
1021 // http://crbug.com/650120. 1035 // http://crbug.com/650120.
1022 if (multiline() || obscured()) 1036 if (multiline() || obscured())
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
1098 return AdjacentWordSelectionModel(current, direction); 1112 return AdjacentWordSelectionModel(current, direction);
1099 } 1113 }
1100 1114
1101 SelectionModel RenderText::EdgeSelectionModel( 1115 SelectionModel RenderText::EdgeSelectionModel(
1102 VisualCursorDirection direction) { 1116 VisualCursorDirection direction) {
1103 if (direction == GetVisualDirectionOfLogicalEnd()) 1117 if (direction == GetVisualDirectionOfLogicalEnd())
1104 return SelectionModel(text().length(), CURSOR_FORWARD); 1118 return SelectionModel(text().length(), CURSOR_FORWARD);
1105 return SelectionModel(0, CURSOR_BACKWARD); 1119 return SelectionModel(0, CURSOR_BACKWARD);
1106 } 1120 }
1107 1121
1122 SelectionModel RenderText::LineSelectionModel(size_t line_index,
1123 VisualCursorDirection direction) {
1124 const internal::Line& line = lines()[line_index];
1125 if (line.segments.empty()) {
1126 // Only the last line can be empty.
1127 DCHECK_EQ(lines().size() - 1, line_index);
1128 return EdgeSelectionModel(GetVisualDirectionOfLogicalEnd());
1129 }
1130
1131 size_t max_index = 0;
1132 size_t min_index = text().length();
1133 for (const auto& segment : line.segments) {
1134 min_index = std::min<size_t>(min_index, segment.char_range.GetMin());
1135 max_index = std::max<size_t>(max_index, segment.char_range.GetMax());
1136 }
1137
1138 return direction == GetVisualDirectionOfLogicalEnd()
1139 ? SelectionModel(DisplayIndexToTextIndex(max_index),
1140 CURSOR_FORWARD)
1141 : SelectionModel(DisplayIndexToTextIndex(min_index),
1142 CURSOR_BACKWARD);
1143 }
1144
1108 void RenderText::SetSelectionModel(const SelectionModel& model) { 1145 void RenderText::SetSelectionModel(const SelectionModel& model) {
1109 DCHECK_LE(model.selection().GetMax(), text().length()); 1146 DCHECK_LE(model.selection().GetMax(), text().length());
1110 selection_model_ = model; 1147 selection_model_ = model;
1111 cached_bounds_and_offset_valid_ = false; 1148 cached_bounds_and_offset_valid_ = false;
1112 } 1149 }
1113 1150
1114 void RenderText::OnTextColorChanged() { 1151 void RenderText::OnTextColorChanged() {
1115 } 1152 }
1116 1153
1117 void RenderText::UpdateDisplayText(float text_width) { 1154 void RenderText::UpdateDisplayText(float text_width) {
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
1206 } 1243 }
1207 1244
1208 void RenderText::UndoCompositionAndSelectionStyles() { 1245 void RenderText::UndoCompositionAndSelectionStyles() {
1209 // Restore the underline and color breaks to undo the temporary styles. 1246 // Restore the underline and color breaks to undo the temporary styles.
1210 DCHECK(composition_and_selection_styles_applied_); 1247 DCHECK(composition_and_selection_styles_applied_);
1211 colors_ = saved_colors_; 1248 colors_ = saved_colors_;
1212 styles_[UNDERLINE] = saved_underlines_; 1249 styles_[UNDERLINE] = saved_underlines_;
1213 composition_and_selection_styles_applied_ = false; 1250 composition_and_selection_styles_applied_ = false;
1214 } 1251 }
1215 1252
1216 Point RenderText::ToTextPoint(const Point& point) {
1217 return point - GetLineOffset(0);
1218 // TODO(ckocagil): Convert multiline view space points to text space.
1219 }
1220
1221 Point RenderText::ToViewPoint(const Point& point) { 1253 Point RenderText::ToViewPoint(const Point& point) {
1222 if (!multiline()) 1254 if (!multiline())
1223 return point + GetLineOffset(0); 1255 return point + GetLineOffset(0);
1224 1256
1225 // TODO(ckocagil): Traverse individual line segments for RTL support. 1257 // TODO(ckocagil): Traverse individual line segments for RTL support.
1226 DCHECK(!lines_.empty()); 1258 DCHECK(!lines_.empty());
1227 int x = point.x(); 1259 int x = point.x();
1228 size_t line = 0; 1260 size_t line = 0;
1229 for (; line < lines_.size() && x > lines_[line].size.width(); ++line) 1261 for (; line < lines_.size() && x > lines_[line].size.width(); ++line)
1230 x -= lines_[line].size.width(); 1262 x -= lines_[line].size.width();
1263
1264 // If |point| is outside the text space, clip it to the end of the last line.
1265 if (line == lines_.size())
1266 x = lines_[--line].size.width();
1231 return Point(x, point.y()) + GetLineOffset(line); 1267 return Point(x, point.y()) + GetLineOffset(line);
1232 } 1268 }
1233 1269
1234 std::vector<Rect> RenderText::TextBoundsToViewBounds(const Range& x) {
1235 std::vector<Rect> rects;
1236
1237 if (!multiline()) {
1238 rects.push_back(Rect(ToViewPoint(Point(x.GetMin(), 0)),
1239 Size(x.length(), GetStringSize().height())));
1240 return rects;
1241 }
1242
1243 EnsureLayout();
1244
1245 // Each line segment keeps its position in text coordinates. Traverse all line
1246 // segments and if the segment intersects with the given range, add the view
1247 // rect corresponding to the intersection to |rects|.
1248 for (size_t line = 0; line < lines_.size(); ++line) {
1249 int line_x = 0;
1250 const Vector2d offset = GetLineOffset(line);
1251 for (size_t i = 0; i < lines_[line].segments.size(); ++i) {
1252 const internal::LineSegment* segment = &lines_[line].segments[i];
1253 const Range intersection = segment->x_range.Intersect(x).Ceil();
1254 if (!intersection.is_empty()) {
1255 Rect rect(line_x + intersection.start() - segment->x_range.start(),
1256 0, intersection.length(), lines_[line].size.height());
1257 rects.push_back(rect + offset);
1258 }
1259 line_x += segment->x_range.length();
1260 }
1261 }
1262
1263 return rects;
1264 }
1265
1266 HorizontalAlignment RenderText::GetCurrentHorizontalAlignment() { 1270 HorizontalAlignment RenderText::GetCurrentHorizontalAlignment() {
1267 if (horizontal_alignment_ != ALIGN_TO_HEAD) 1271 if (horizontal_alignment_ != ALIGN_TO_HEAD)
1268 return horizontal_alignment_; 1272 return horizontal_alignment_;
1269 return GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT ? 1273 return GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT ?
1270 ALIGN_RIGHT : ALIGN_LEFT; 1274 ALIGN_RIGHT : ALIGN_LEFT;
1271 } 1275 }
1272 1276
1273 Vector2d RenderText::GetAlignmentOffset(size_t line_number) { 1277 Vector2d RenderText::GetAlignmentOffset(size_t line_number) {
1274 // TODO(ckocagil): Enable |lines_| usage on RenderTextMac. 1278 // TODO(ckocagil): Enable |lines_| usage on RenderTextMac.
1275 if (MultilineSupported() && multiline_) 1279 if (MultilineSupported() && multiline_)
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after
1688 1692
1689 for (; range_max < length; ++range_max) 1693 for (; range_max < length; ++range_max)
1690 if (iter.IsEndOfWord(range_max) || iter.IsStartOfWord(range_max)) 1694 if (iter.IsEndOfWord(range_max) || iter.IsStartOfWord(range_max))
1691 break; 1695 break;
1692 1696
1693 return range.is_reversed() ? Range(range_max, range_min) 1697 return range.is_reversed() ? Range(range_max, range_min)
1694 : Range(range_min, range_max); 1698 : Range(range_min, range_max);
1695 } 1699 }
1696 1700
1697 } // namespace gfx 1701 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698