OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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_harfbuzz.h" | 5 #include "ui/gfx/render_text_harfbuzz.h" |
6 | 6 |
7 #include <limits> | 7 #include <limits> |
8 #include <set> | 8 #include <set> |
9 | 9 |
10 #include "base/i18n/bidi_line_iterator.h" | 10 #include "base/i18n/bidi_line_iterator.h" |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
284 const Range word_range = words_->GetRange(iter); | 284 const Range word_range = words_->GetRange(iter); |
285 std::vector<internal::LineSegment> word_segments; | 285 std::vector<internal::LineSegment> word_segments; |
286 SkScalar word_width = GetWordWidth(word_range, &word_segments); | 286 SkScalar word_width = GetWordWidth(word_range, &word_segments); |
287 | 287 |
288 // If the last word is '\n', we should advance a new line after adding | 288 // If the last word is '\n', we should advance a new line after adding |
289 // the word to the current line. | 289 // the word to the current line. |
290 bool new_line = false; | 290 bool new_line = false; |
291 if (!word_segments.empty() && | 291 if (!word_segments.empty() && |
292 text_[word_segments.back().char_range.start()] == '\n') { | 292 text_[word_segments.back().char_range.start()] == '\n') { |
293 new_line = true; | 293 new_line = true; |
294 word_width -= word_segments.back().width(); | 294 |
295 word_segments.pop_back(); | 295 // Since the line should at least contain some information regarding the |
| 296 // text range it corresponds to, don't pop the newline segment, if it's |
| 297 // the only segment in the line. This ensures that every line has a non- |
| 298 // empty segments vector (except the last in some cases). |
| 299 if (word_segments.size() != 1u || available_width_ != max_width_) { |
| 300 word_width -= word_segments.back().width(); |
| 301 word_segments.pop_back(); |
| 302 } |
296 } | 303 } |
297 | 304 |
298 // If the word is not the first word in the line and it can't fit into | 305 // If the word is not the first word in the line and it can't fit into |
299 // the current line, advance a new line. | 306 // the current line, advance a new line. |
300 if (word_width > available_width_ && available_width_ != max_width_) | 307 if (word_width > available_width_ && available_width_ != max_width_) |
301 AdvanceLine(); | 308 AdvanceLine(); |
302 if (!word_segments.empty()) | 309 if (!word_segments.empty()) |
303 AddWordToLine(word_segments); | 310 AddWordToLine(word_segments); |
304 if (new_line) | 311 if (new_line) |
305 AdvanceLine(); | 312 AdvanceLine(); |
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
640 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), | 647 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), |
641 true, chars, glyphs); | 648 true, chars, glyphs); |
642 return; | 649 return; |
643 } | 650 } |
644 | 651 |
645 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), | 652 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), |
646 false, chars, glyphs); | 653 false, chars, glyphs); |
647 } | 654 } |
648 | 655 |
649 RangeF TextRunHarfBuzz::GetGraphemeBounds(RenderTextHarfBuzz* render_text, | 656 RangeF TextRunHarfBuzz::GetGraphemeBounds(RenderTextHarfBuzz* render_text, |
650 size_t text_index) { | 657 size_t text_index) const { |
651 DCHECK_LT(text_index, range.end()); | 658 DCHECK_LT(text_index, range.end()); |
652 if (glyph_count == 0) | 659 if (glyph_count == 0) |
653 return RangeF(preceding_run_widths, preceding_run_widths + width); | 660 return RangeF(preceding_run_widths, preceding_run_widths + width); |
654 | 661 |
655 Range chars; | 662 Range chars; |
656 Range glyphs; | 663 Range glyphs; |
657 GetClusterAt(text_index, &chars, &glyphs); | 664 GetClusterAt(text_index, &chars, &glyphs); |
658 const float cluster_begin_x = positions[glyphs.start()].x(); | 665 const float cluster_begin_x = positions[glyphs.start()].x(); |
659 const float cluster_end_x = glyphs.end() < glyph_count ? | 666 const float cluster_end_x = glyphs.end() < glyph_count ? |
660 positions[glyphs.end()].x() : SkFloatToScalar(width); | 667 positions[glyphs.end()].x() : SkFloatToScalar(width); |
(...skipping 29 matching lines...) Expand all Loading... |
690 cluster_width * (before + 1) / static_cast<float>(total)); | 697 cluster_width * (before + 1) / static_cast<float>(total)); |
691 return RangeF(preceding_run_widths + grapheme_begin_x, | 698 return RangeF(preceding_run_widths + grapheme_begin_x, |
692 preceding_run_widths + grapheme_end_x); | 699 preceding_run_widths + grapheme_end_x); |
693 } | 700 } |
694 } | 701 } |
695 | 702 |
696 return RangeF(preceding_run_widths + cluster_begin_x, | 703 return RangeF(preceding_run_widths + cluster_begin_x, |
697 preceding_run_widths + cluster_end_x); | 704 preceding_run_widths + cluster_end_x); |
698 } | 705 } |
699 | 706 |
| 707 float TextRunHarfBuzz::GetGraphemeWidthForCharRange( |
| 708 RenderTextHarfBuzz* render_text, |
| 709 const Range& char_range) const { |
| 710 if (char_range.is_empty()) |
| 711 return 0; |
| 712 DCHECK(!char_range.is_reversed()); |
| 713 DCHECK(range.Contains(char_range)); |
| 714 size_t left_index = is_rtl ? char_range.end() - 1 : char_range.start(); |
| 715 size_t right_index = is_rtl ? char_range.start() : char_range.end() - 1; |
| 716 return GetGraphemeBounds(render_text, right_index).GetMax() - |
| 717 GetGraphemeBounds(render_text, left_index).GetMin(); |
| 718 } |
| 719 |
700 SkScalar TextRunHarfBuzz::GetGlyphWidthForCharRange( | 720 SkScalar TextRunHarfBuzz::GetGlyphWidthForCharRange( |
701 const Range& char_range) const { | 721 const Range& char_range) const { |
702 if (char_range.is_empty()) | 722 if (char_range.is_empty()) |
703 return 0; | 723 return 0; |
704 | 724 |
705 DCHECK(range.Contains(char_range)); | 725 DCHECK(range.Contains(char_range)); |
706 Range glyph_range = CharRangeToGlyphRange(char_range); | 726 Range glyph_range = CharRangeToGlyphRange(char_range); |
707 | 727 |
708 // The |glyph_range| might be empty or invalid on Windows if a multi-character | 728 // The |glyph_range| might be empty or invalid on Windows if a multi-character |
709 // grapheme is divided into different runs (e.g., there are two font sizes or | 729 // grapheme is divided into different runs (e.g., there are two font sizes or |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
808 Size RenderTextHarfBuzz::GetStringSize() { | 828 Size RenderTextHarfBuzz::GetStringSize() { |
809 const SizeF size_f = GetStringSizeF(); | 829 const SizeF size_f = GetStringSizeF(); |
810 return Size(std::ceil(size_f.width()), size_f.height()); | 830 return Size(std::ceil(size_f.width()), size_f.height()); |
811 } | 831 } |
812 | 832 |
813 SizeF RenderTextHarfBuzz::GetStringSizeF() { | 833 SizeF RenderTextHarfBuzz::GetStringSizeF() { |
814 EnsureLayout(); | 834 EnsureLayout(); |
815 return total_size_; | 835 return total_size_; |
816 } | 836 } |
817 | 837 |
818 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { | 838 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& view_point) { |
819 EnsureLayout(); | 839 EnsureLayout(); |
| 840 DCHECK(!lines().empty()); |
820 | 841 |
821 int x = ToTextPoint(point).x(); | 842 int line_index = GetLineContainingYCoord((view_point - GetLineOffset(0)).y()); |
822 float offset = 0; | 843 // Clip line index to a valid value in case kDragToEndIfOutsideVerticalBounds |
823 size_t run_index = GetRunContainingXCoord(x, &offset); | 844 // is false. Else, drag to end. |
| 845 if (line_index < 0) { |
| 846 if (RenderText::kDragToEndIfOutsideVerticalBounds) |
| 847 return EdgeSelectionModel(GetVisualDirectionOfLogicalBeginning()); |
| 848 else |
| 849 line_index = 0; |
| 850 } |
| 851 if (line_index >= static_cast<int>(lines().size())) { |
| 852 if (RenderText::kDragToEndIfOutsideVerticalBounds) |
| 853 return EdgeSelectionModel(GetVisualDirectionOfLogicalEnd()); |
| 854 else |
| 855 line_index = lines().size() - 1; |
| 856 } |
| 857 const internal::Line& line = lines()[line_index]; |
824 | 858 |
825 internal::TextRunList* run_list = GetRunList(); | 859 float point_offset_relative_segment = 0; |
826 if (run_index >= run_list->size()) | 860 const int segment_index = GetLineSegmentContainingXCoord( |
827 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); | 861 line, (view_point - GetLineOffset(line_index)).x(), |
828 const internal::TextRunHarfBuzz& run = *run_list->runs()[run_index]; | 862 &point_offset_relative_segment); |
| 863 if (segment_index < 0) |
| 864 return LineSelectionModel(line_index, CURSOR_LEFT); |
| 865 if (segment_index >= static_cast<int>(line.segments.size())) |
| 866 return LineSelectionModel(line_index, CURSOR_RIGHT); |
| 867 const internal::LineSegment& segment = line.segments[segment_index]; |
| 868 |
| 869 const internal::TextRunHarfBuzz& run = *GetRunList()->runs()[segment.run]; |
| 870 const size_t segment_min_glyph_index = |
| 871 run.CharRangeToGlyphRange(segment.char_range).GetMin(); |
| 872 const float segment_offset_relative_run = |
| 873 segment_min_glyph_index != 0 |
| 874 ? SkScalarToFloat(run.positions[segment_min_glyph_index].x()) |
| 875 : 0; |
| 876 const float point_offset_relative_run = |
| 877 point_offset_relative_segment + segment_offset_relative_run; |
| 878 |
| 879 // TODO(crbug.com/676287): Use offset within the glyph to return the correct |
| 880 // grapheme position within a multi-grapheme glyph. |
829 for (size_t i = 0; i < run.glyph_count; ++i) { | 881 for (size_t i = 0; i < run.glyph_count; ++i) { |
830 const SkScalar end = | 882 const float end = i + 1 == run.glyph_count |
831 i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x(); | 883 ? run.width |
832 const SkScalar middle = (end + run.positions[i].x()) / 2; | 884 : SkScalarToFloat(run.positions[i + 1].x()); |
833 | 885 const float middle = (end + SkScalarToFloat(run.positions[i].x())) / 2; |
834 if (offset < middle) { | 886 const size_t index = DisplayIndexToTextIndex(run.glyph_to_char[i]); |
835 return SelectionModel(DisplayIndexToTextIndex( | 887 if (point_offset_relative_run < middle) { |
836 run.glyph_to_char[i] + (run.is_rtl ? 1 : 0)), | 888 return run.is_rtl ? SelectionModel( |
837 (run.is_rtl ? CURSOR_BACKWARD : CURSOR_FORWARD)); | 889 IndexOfAdjacentGrapheme(index, CURSOR_FORWARD), |
| 890 CURSOR_BACKWARD) |
| 891 : SelectionModel(index, CURSOR_FORWARD); |
838 } | 892 } |
839 if (offset < end) { | 893 if (point_offset_relative_run < end) { |
840 return SelectionModel(DisplayIndexToTextIndex( | 894 return run.is_rtl ? SelectionModel(index, CURSOR_FORWARD) |
841 run.glyph_to_char[i] + (run.is_rtl ? 0 : 1)), | 895 : SelectionModel( |
842 (run.is_rtl ? CURSOR_FORWARD : CURSOR_BACKWARD)); | 896 IndexOfAdjacentGrapheme(index, CURSOR_FORWARD), |
| 897 CURSOR_BACKWARD); |
843 } | 898 } |
844 } | 899 } |
845 return EdgeSelectionModel(CURSOR_RIGHT); | 900 |
| 901 return LineSelectionModel(line_index, CURSOR_RIGHT); |
846 } | 902 } |
847 | 903 |
848 bool RenderTextHarfBuzz::IsSelectionSupported() const { | 904 bool RenderTextHarfBuzz::IsSelectionSupported() const { |
849 // TODO(karandeepb): Support multi-line text selection. | 905 return true; |
850 return !multiline(); | |
851 } | 906 } |
852 | 907 |
853 std::vector<RenderText::FontSpan> RenderTextHarfBuzz::GetFontSpansForTesting() { | 908 std::vector<RenderText::FontSpan> RenderTextHarfBuzz::GetFontSpansForTesting() { |
854 EnsureLayout(); | 909 EnsureLayout(); |
855 | 910 |
856 internal::TextRunList* run_list = GetRunList(); | 911 internal::TextRunList* run_list = GetRunList(); |
857 std::vector<RenderText::FontSpan> spans; | 912 std::vector<RenderText::FontSpan> spans; |
858 for (auto* run : run_list->runs()) { | 913 for (auto* run : run_list->runs()) { |
859 spans.push_back(RenderText::FontSpan( | 914 spans.push_back(RenderText::FontSpan( |
860 run->font, Range(DisplayIndexToTextIndex(run->range.start()), | 915 run->font, Range(DisplayIndexToTextIndex(run->range.start()), |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1005 if (is_forward ? iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor)) | 1060 if (is_forward ? iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor)) |
1006 break; | 1061 break; |
1007 } | 1062 } |
1008 return cur; | 1063 return cur; |
1009 #endif | 1064 #endif |
1010 } | 1065 } |
1011 | 1066 |
1012 std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) { | 1067 std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) { |
1013 DCHECK(!update_display_run_list_); | 1068 DCHECK(!update_display_run_list_); |
1014 DCHECK(Range(0, text().length()).Contains(range)); | 1069 DCHECK(Range(0, text().length()).Contains(range)); |
1015 Range layout_range(TextIndexToDisplayIndex(range.start()), | 1070 const size_t start = |
1016 TextIndexToDisplayIndex(range.end())); | 1071 IsValidCursorIndex(range.GetMin()) |
1017 DCHECK(Range(0, GetDisplayText().length()).Contains(layout_range)); | 1072 ? range.GetMin() |
| 1073 : IndexOfAdjacentGrapheme(range.GetMin(), CURSOR_BACKWARD); |
| 1074 const size_t end = |
| 1075 IsValidCursorIndex(range.GetMax()) |
| 1076 ? range.GetMax() |
| 1077 : IndexOfAdjacentGrapheme(range.GetMax(), CURSOR_FORWARD); |
| 1078 Range display_range(TextIndexToDisplayIndex(start), |
| 1079 TextIndexToDisplayIndex(end)); |
| 1080 DCHECK(Range(0, GetDisplayText().length()).Contains(display_range)); |
1018 | 1081 |
1019 std::vector<Rect> rects; | 1082 std::vector<Rect> rects; |
1020 if (layout_range.is_empty()) | 1083 if (display_range.is_empty()) |
1021 return rects; | 1084 return rects; |
1022 std::vector<Range> bounds; | |
1023 | 1085 |
1024 internal::TextRunList* run_list = GetRunList(); | 1086 internal::TextRunList* run_list = GetRunList(); |
| 1087 for (size_t line_index = 0; line_index < lines().size(); ++line_index) { |
| 1088 const internal::Line& line = lines()[line_index]; |
| 1089 // Only the last line can be empty. |
| 1090 DCHECK(!line.segments.empty() || (line_index == lines().size() - 1)); |
1025 | 1091 |
1026 // Add a Range for each run/selection intersection. | 1092 float line_x = 0; |
1027 for (size_t i = 0; i < run_list->size(); ++i) { | 1093 for (const internal::LineSegment& segment : line.segments) { |
1028 internal::TextRunHarfBuzz* run = | 1094 const Range intersection = segment.char_range.Intersect(display_range); |
1029 run_list->runs()[run_list->visual_to_logical(i)]; | 1095 DCHECK(!intersection.is_reversed()); |
1030 Range intersection = run->range.Intersect(layout_range); | 1096 if (!intersection.is_empty()) { |
1031 if (!intersection.IsValid()) | 1097 const internal::TextRunHarfBuzz& run = *run_list->runs()[segment.run]; |
1032 continue; | 1098 float width = SkScalarToFloat( |
1033 DCHECK(!intersection.is_reversed()); | 1099 run.GetGraphemeWidthForCharRange(this, intersection)); |
1034 const size_t left_index = | 1100 float x = line_x; |
1035 run->is_rtl ? intersection.end() - 1 : intersection.start(); | 1101 if (run.is_rtl) { |
1036 const Range leftmost_character_x = | 1102 x += SkScalarToFloat(run.GetGraphemeWidthForCharRange( |
1037 run->GetGraphemeBounds(this, left_index).Round(); | 1103 this, gfx::Range(intersection.end(), segment.char_range.end()))); |
1038 const size_t right_index = | 1104 } else { |
1039 run->is_rtl ? intersection.start() : intersection.end() - 1; | 1105 x += SkScalarToFloat(run.GetGraphemeWidthForCharRange( |
1040 const Range rightmost_character_x = | 1106 this, |
1041 run->GetGraphemeBounds(this, right_index).Round(); | 1107 gfx::Range(segment.char_range.start(), intersection.start()))); |
1042 Range range_x(leftmost_character_x.start(), rightmost_character_x.end()); | 1108 } |
1043 DCHECK(!range_x.is_reversed()); | 1109 int end_x = std::ceil(x + width); |
1044 if (range_x.is_empty()) | 1110 int start_x = std::ceil(x); |
1045 continue; | 1111 gfx::Rect rect(start_x, 0, end_x - start_x, line.size.height()); |
1046 | 1112 rects.push_back(rect + GetLineOffset(line_index)); |
1047 // Union this with the last range if they're adjacent. | 1113 } |
1048 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); | 1114 line_x += segment.width(); |
1049 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { | |
1050 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); | |
1051 bounds.pop_back(); | |
1052 } | 1115 } |
1053 bounds.push_back(range_x); | |
1054 } | |
1055 for (Range& bound : bounds) { | |
1056 std::vector<Rect> current_rects = TextBoundsToViewBounds(bound); | |
1057 rects.insert(rects.end(), current_rects.begin(), current_rects.end()); | |
1058 } | 1116 } |
1059 return rects; | 1117 return rects; |
1060 } | 1118 } |
1061 | 1119 |
1062 size_t RenderTextHarfBuzz::TextIndexToDisplayIndex(size_t index) { | 1120 size_t RenderTextHarfBuzz::TextIndexToDisplayIndex(size_t index) { |
1063 return TextIndexToGivenTextIndex(GetDisplayText(), index); | 1121 return TextIndexToGivenTextIndex(GetDisplayText(), index); |
1064 } | 1122 } |
1065 | 1123 |
1066 size_t RenderTextHarfBuzz::DisplayIndexToTextIndex(size_t index) { | 1124 size_t RenderTextHarfBuzz::DisplayIndexToTextIndex(size_t index) { |
1067 if (!obscured()) | 1125 if (!obscured()) |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1222 LogicalCursorDirection affinity = caret.caret_affinity(); | 1280 LogicalCursorDirection affinity = caret.caret_affinity(); |
1223 internal::TextRunList* run_list = GetRunList(); | 1281 internal::TextRunList* run_list = GetRunList(); |
1224 for (size_t i = 0; i < run_list->size(); ++i) { | 1282 for (size_t i = 0; i < run_list->size(); ++i) { |
1225 internal::TextRunHarfBuzz* run = run_list->runs()[i]; | 1283 internal::TextRunHarfBuzz* run = run_list->runs()[i]; |
1226 if (RangeContainsCaret(run->range, layout_position, affinity)) | 1284 if (RangeContainsCaret(run->range, layout_position, affinity)) |
1227 return i; | 1285 return i; |
1228 } | 1286 } |
1229 return run_list->size(); | 1287 return run_list->size(); |
1230 } | 1288 } |
1231 | 1289 |
1232 size_t RenderTextHarfBuzz::GetRunContainingXCoord(float x, | 1290 int RenderTextHarfBuzz::GetLineContainingYCoord(float text_y) { |
1233 float* offset) const { | 1291 if (text_y < 0) |
1234 DCHECK(!update_display_run_list_); | 1292 return -1; |
1235 const internal::TextRunList* run_list = GetRunList(); | 1293 |
1236 if (x < 0) | 1294 for (size_t i = 0; i < lines().size(); i++) { |
1237 return run_list->size(); | 1295 const internal::Line& line = lines()[i]; |
1238 // Find the text run containing the argument point (assumed already offset). | 1296 |
1239 float current_x = 0; | 1297 if (text_y <= line.size.height()) |
1240 for (size_t i = 0; i < run_list->size(); ++i) { | 1298 return i; |
1241 size_t run = run_list->visual_to_logical(i); | 1299 text_y -= line.size.height(); |
1242 current_x += run_list->runs()[run]->width; | 1300 } |
1243 if (x < current_x) { | 1301 |
1244 *offset = x - (current_x - run_list->runs()[run]->width); | 1302 return lines().size(); |
1245 return run; | 1303 } |
| 1304 |
| 1305 int RenderTextHarfBuzz::GetLineSegmentContainingXCoord( |
| 1306 const internal::Line& line, |
| 1307 float line_x, |
| 1308 float* offset_relative_segment) { |
| 1309 DCHECK(offset_relative_segment); |
| 1310 |
| 1311 *offset_relative_segment = 0; |
| 1312 if (line_x < 0) |
| 1313 return -1; |
| 1314 for (size_t i = 0; i < line.segments.size(); i++) { |
| 1315 const internal::LineSegment& segment = line.segments[i]; |
| 1316 |
| 1317 // segment.x_range is not used because it is in text space. |
| 1318 if (line_x < segment.width()) { |
| 1319 *offset_relative_segment = line_x; |
| 1320 return i; |
1246 } | 1321 } |
| 1322 line_x -= segment.width(); |
1247 } | 1323 } |
1248 return run_list->size(); | 1324 return line.segments.size(); |
1249 } | 1325 } |
1250 | 1326 |
1251 SelectionModel RenderTextHarfBuzz::FirstSelectionModelInsideRun( | 1327 SelectionModel RenderTextHarfBuzz::FirstSelectionModelInsideRun( |
1252 const internal::TextRunHarfBuzz* run) { | 1328 const internal::TextRunHarfBuzz* run) { |
1253 size_t position = DisplayIndexToTextIndex(run->range.start()); | 1329 size_t position = DisplayIndexToTextIndex(run->range.start()); |
1254 position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD); | 1330 position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD); |
1255 return SelectionModel(position, CURSOR_BACKWARD); | 1331 return SelectionModel(position, CURSOR_BACKWARD); |
1256 } | 1332 } |
1257 | 1333 |
1258 SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun( | 1334 SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun( |
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1626 | 1702 |
1627 attribute.strike = run.strike; | 1703 attribute.strike = run.strike; |
1628 attribute.diagonal_strike = run.diagonal_strike; | 1704 attribute.diagonal_strike = run.diagonal_strike; |
1629 decorated_text->attributes.push_back(attribute); | 1705 decorated_text->attributes.push_back(attribute); |
1630 } | 1706 } |
1631 } | 1707 } |
1632 return true; | 1708 return true; |
1633 } | 1709 } |
1634 | 1710 |
1635 } // namespace gfx | 1711 } // namespace gfx |
OLD | NEW |