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/i18n/break_iterator.h" | 9 #include "base/i18n/break_iterator.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 range = range.Intersect(styles_[i].GetRange(style_[i])); | 294 range = range.Intersect(styles_[i].GetRange(style_[i])); |
295 return range; | 295 return range; |
296 } | 296 } |
297 | 297 |
298 void StyleIterator::UpdatePosition(size_t position) { | 298 void StyleIterator::UpdatePosition(size_t position) { |
299 color_ = colors_.GetBreak(position); | 299 color_ = colors_.GetBreak(position); |
300 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) | 300 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) |
301 style_[i] = styles_[i].GetBreak(position); | 301 style_[i] = styles_[i].GetBreak(position); |
302 } | 302 } |
303 | 303 |
| 304 Line::Line() : width(0), height(0), preceding_heights(0), baseline(0) {} |
| 305 |
| 306 Line::Line(Line& other) { |
| 307 *this = other; |
| 308 } |
| 309 |
| 310 Line& Line::operator=(Line& other) { |
| 311 width = other.width; |
| 312 height = other.height; |
| 313 preceding_heights = other.preceding_heights; |
| 314 baseline = other.baseline; |
| 315 segments.swap(other.segments); |
| 316 return *this; |
| 317 } |
| 318 |
304 } // namespace internal | 319 } // namespace internal |
305 | 320 |
306 RenderText::~RenderText() { | 321 RenderText::~RenderText() { |
307 } | 322 } |
308 | 323 |
309 void RenderText::SetText(const base::string16& text) { | 324 void RenderText::SetText(const base::string16& text) { |
310 DCHECK(!composition_range_.IsValid()); | 325 DCHECK(!composition_range_.IsValid()); |
| 326 if (text_ == text) |
| 327 return; |
311 text_ = text; | 328 text_ = text; |
312 | 329 |
313 // Adjust ranged styles and colors to accommodate a new text length. | 330 // Adjust ranged styles and colors to accommodate a new text length. |
314 const size_t text_length = text_.length(); | 331 const size_t text_length = text_.length(); |
315 colors_.SetMax(text_length); | 332 colors_.SetMax(text_length); |
316 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) | 333 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) |
317 styles_[style].SetMax(text_length); | 334 styles_[style].SetMax(text_length); |
318 cached_bounds_and_offset_valid_ = false; | 335 cached_bounds_and_offset_valid_ = false; |
319 | 336 |
320 // Reset selection model. SetText should always followed by SetSelectionModel | 337 // Reset selection model. SetText should always followed by SetSelectionModel |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 void RenderText::SetObscured(bool obscured) { | 394 void RenderText::SetObscured(bool obscured) { |
378 if (obscured != obscured_) { | 395 if (obscured != obscured_) { |
379 obscured_ = obscured; | 396 obscured_ = obscured; |
380 obscured_reveal_index_ = -1; | 397 obscured_reveal_index_ = -1; |
381 cached_bounds_and_offset_valid_ = false; | 398 cached_bounds_and_offset_valid_ = false; |
382 UpdateLayoutText(); | 399 UpdateLayoutText(); |
383 ResetLayout(); | 400 ResetLayout(); |
384 } | 401 } |
385 } | 402 } |
386 | 403 |
| 404 void RenderText::SetMultiline(bool multiline) { |
| 405 if (multiline != multiline_) { |
| 406 multiline_ = multiline; |
| 407 cached_bounds_and_offset_valid_ = false; |
| 408 ResetLayout(); |
| 409 } |
| 410 } |
| 411 |
387 void RenderText::SetObscuredRevealIndex(int index) { | 412 void RenderText::SetObscuredRevealIndex(int index) { |
388 if (obscured_reveal_index_ == index) | 413 if (obscured_reveal_index_ == index) |
389 return; | 414 return; |
390 | 415 |
391 obscured_reveal_index_ = index; | 416 obscured_reveal_index_ = index; |
392 cached_bounds_and_offset_valid_ = false; | 417 cached_bounds_and_offset_valid_ = false; |
393 UpdateLayoutText(); | 418 UpdateLayoutText(); |
394 ResetLayout(); | 419 ResetLayout(); |
395 } | 420 } |
396 | 421 |
397 void RenderText::SetDisplayRect(const Rect& r) { | 422 void RenderText::SetDisplayRect(const Rect& r) { |
398 display_rect_ = r; | 423 display_rect_ = r; |
399 cached_bounds_and_offset_valid_ = false; | 424 cached_bounds_and_offset_valid_ = false; |
| 425 lines_.clear(); |
400 } | 426 } |
401 | 427 |
402 void RenderText::SetCursorPosition(size_t position) { | 428 void RenderText::SetCursorPosition(size_t position) { |
403 MoveCursorTo(position, false); | 429 MoveCursorTo(position, false); |
404 } | 430 } |
405 | 431 |
406 void RenderText::MoveCursor(BreakType break_type, | 432 void RenderText::MoveCursor(BreakType break_type, |
407 VisualCursorDirection direction, | 433 VisualCursorDirection direction, |
408 bool select) { | 434 bool select) { |
409 SelectionModel position(cursor_position(), selection_model_.caret_affinity()); | 435 SelectionModel position(cursor_position(), selection_model_.caret_affinity()); |
(...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
756 selection_color_(kDefaultColor), | 782 selection_color_(kDefaultColor), |
757 selection_background_focused_color_(kDefaultSelectionBackgroundColor), | 783 selection_background_focused_color_(kDefaultSelectionBackgroundColor), |
758 focused_(false), | 784 focused_(false), |
759 composition_range_(ui::Range::InvalidRange()), | 785 composition_range_(ui::Range::InvalidRange()), |
760 colors_(kDefaultColor), | 786 colors_(kDefaultColor), |
761 styles_(NUM_TEXT_STYLES), | 787 styles_(NUM_TEXT_STYLES), |
762 composition_and_selection_styles_applied_(false), | 788 composition_and_selection_styles_applied_(false), |
763 obscured_(false), | 789 obscured_(false), |
764 obscured_reveal_index_(-1), | 790 obscured_reveal_index_(-1), |
765 truncate_length_(0), | 791 truncate_length_(0), |
| 792 multiline_(false), |
766 fade_head_(false), | 793 fade_head_(false), |
767 fade_tail_(false), | 794 fade_tail_(false), |
768 background_is_transparent_(false), | 795 background_is_transparent_(false), |
769 clip_to_display_rect_(true), | 796 clip_to_display_rect_(true), |
770 cached_bounds_and_offset_valid_(false) { | 797 cached_bounds_and_offset_valid_(false) { |
771 } | 798 } |
772 | 799 |
773 const Vector2d& RenderText::GetUpdatedDisplayOffset() { | 800 const Vector2d& RenderText::GetUpdatedDisplayOffset() { |
774 UpdateCachedBoundsAndOffset(); | 801 UpdateCachedBoundsAndOffset(); |
775 return display_offset_; | 802 return display_offset_; |
(...skipping 23 matching lines...) Expand all Loading... |
799 void RenderText::SetSelectionModel(const SelectionModel& model) { | 826 void RenderText::SetSelectionModel(const SelectionModel& model) { |
800 DCHECK_LE(model.selection().GetMax(), text().length()); | 827 DCHECK_LE(model.selection().GetMax(), text().length()); |
801 selection_model_ = model; | 828 selection_model_ = model; |
802 cached_bounds_and_offset_valid_ = false; | 829 cached_bounds_and_offset_valid_ = false; |
803 } | 830 } |
804 | 831 |
805 const base::string16& RenderText::GetLayoutText() const { | 832 const base::string16& RenderText::GetLayoutText() const { |
806 return layout_text_.empty() ? text_ : layout_text_; | 833 return layout_text_.empty() ? text_ : layout_text_; |
807 } | 834 } |
808 | 835 |
| 836 const BreakList<size_t>& RenderText::GetLineBreaks() { |
| 837 if (line_breaks_.max() == 0) { |
| 838 const string16& layout_text = GetLayoutText(); |
| 839 line_breaks_.SetValue(0); |
| 840 line_breaks_.SetMax(layout_text.length()); |
| 841 base::i18n::BreakIterator iter(layout_text, |
| 842 base::i18n::BreakIterator::BREAK_LINE); |
| 843 bool success = iter.Init(); |
| 844 CHECK(success); |
| 845 do { |
| 846 line_breaks_.ApplyValue(iter.pos(), ui::Range(iter.pos(), |
| 847 layout_text.length())); |
| 848 } while (iter.Advance()); |
| 849 } |
| 850 return line_breaks_; |
| 851 } |
| 852 |
809 void RenderText::ApplyCompositionAndSelectionStyles() { | 853 void RenderText::ApplyCompositionAndSelectionStyles() { |
810 // Save the underline and color breaks to undo the temporary styles later. | 854 // Save the underline and color breaks to undo the temporary styles later. |
811 DCHECK(!composition_and_selection_styles_applied_); | 855 DCHECK(!composition_and_selection_styles_applied_); |
812 saved_colors_ = colors_; | 856 saved_colors_ = colors_; |
813 saved_underlines_ = styles_[UNDERLINE]; | 857 saved_underlines_ = styles_[UNDERLINE]; |
814 | 858 |
815 // Apply an underline to the composition range in |underlines|. | 859 // Apply an underline to the composition range in |underlines|. |
816 if (composition_range_.IsValid() && !composition_range_.is_empty()) | 860 if (composition_range_.IsValid() && !composition_range_.is_empty()) |
817 styles_[UNDERLINE].ApplyValue(true, composition_range_); | 861 styles_[UNDERLINE].ApplyValue(true, composition_range_); |
818 | 862 |
819 // Apply the selected text color to the [un-reversed] selection range. | 863 // Apply the selected text color to the [un-reversed] selection range. |
820 if (!selection().is_empty()) { | 864 if (!selection().is_empty()) { |
821 const ui::Range range(selection().GetMin(), selection().GetMax()); | 865 const ui::Range range(selection().GetMin(), selection().GetMax()); |
822 colors_.ApplyValue(selection_color_, range); | 866 colors_.ApplyValue(selection_color_, range); |
823 } | 867 } |
824 composition_and_selection_styles_applied_ = true; | 868 composition_and_selection_styles_applied_ = true; |
825 } | 869 } |
826 | 870 |
827 void RenderText::UndoCompositionAndSelectionStyles() { | 871 void RenderText::UndoCompositionAndSelectionStyles() { |
828 // Restore the underline and color breaks to undo the temporary styles. | 872 // Restore the underline and color breaks to undo the temporary styles. |
829 DCHECK(composition_and_selection_styles_applied_); | 873 DCHECK(composition_and_selection_styles_applied_); |
830 colors_ = saved_colors_; | 874 colors_ = saved_colors_; |
831 styles_[UNDERLINE] = saved_underlines_; | 875 styles_[UNDERLINE] = saved_underlines_; |
832 composition_and_selection_styles_applied_ = false; | 876 composition_and_selection_styles_applied_ = false; |
833 } | 877 } |
834 | 878 |
835 Vector2d RenderText::GetTextOffset() { | 879 Vector2d RenderText::GetLineOffset(size_t line_number) { |
836 Vector2d offset = display_rect().OffsetFromOrigin(); | 880 Vector2d offset = display_rect().OffsetFromOrigin(); |
837 offset.Add(GetUpdatedDisplayOffset()); | 881 if (!multiline()) |
838 offset.Add(GetAlignmentOffset()); | 882 offset.Add(GetUpdatedDisplayOffset()); |
| 883 else |
| 884 offset.Add(Vector2d(0, lines()[line_number].preceding_heights)); |
| 885 offset.Add(GetAlignmentOffset(line_number)); |
839 return offset; | 886 return offset; |
840 } | 887 } |
841 | 888 |
842 Point RenderText::ToTextPoint(const Point& point) { | 889 Point RenderText::ToTextPoint(const Point& point) { |
843 return point - GetTextOffset(); | 890 return point - GetLineOffset(0); |
| 891 // TODO(ckocagil): implement multiline. |
844 } | 892 } |
845 | 893 |
846 Point RenderText::ToViewPoint(const Point& point) { | 894 Point RenderText::ToViewPoint(const Point& point) { |
847 return point + GetTextOffset(); | 895 if (!multiline()) |
| 896 return point + GetLineOffset(0); |
| 897 |
| 898 // TODO(ckocagil): refactor this to traverse all line segments to support rtl. |
| 899 DCHECK(!lines_.empty()); |
| 900 int x = point.x(); |
| 901 unsigned int line = 0; |
| 902 while (line < lines_.size() && x > lines()[line].width) { |
| 903 x -= lines()[line].width; |
| 904 ++line; |
| 905 } |
| 906 return Point(x, point.y()) + GetLineOffset(line); |
848 } | 907 } |
849 | 908 |
850 Vector2d RenderText::GetAlignmentOffset() { | 909 std::vector<Rect> RenderText::TextBoundsToViewBounds(const ui::Range& x) { |
| 910 std::vector<Rect> rects; |
| 911 |
| 912 if (!multiline()) { |
| 913 rects.push_back(Rect(ToViewPoint(Point(x.GetMin(), 0)), |
| 914 Size(x.length(), GetStringSize().height()))); |
| 915 return rects; |
| 916 } |
| 917 |
| 918 EnsureLayout(); |
| 919 |
| 920 // Each line segments keeps its position in text coordinates. Traverse all |
| 921 // line segments and if the segment intersects with the given range, add the |
| 922 // view rect corresponding to the intersection to |rects|. |
| 923 for (size_t line = 0; line < lines().size(); ++line) { |
| 924 int line_x = 0; |
| 925 Vector2d offset = GetLineOffset(line); |
| 926 for (size_t i = 0; i < lines()[line].segments.size(); ++i) { |
| 927 const internal::LineSegment* segment = lines()[line].segments[i]; |
| 928 ui::Range intersection = segment->x_pos.Intersect(x); |
| 929 if (!intersection.is_empty()) { |
| 930 Rect rect(line_x + intersection.start() - segment->x_pos.start(), |
| 931 0, intersection.length(), lines()[line].height); |
| 932 rect += offset; |
| 933 rects.push_back(rect); |
| 934 } |
| 935 line_x += segment->x_pos.length(); |
| 936 } |
| 937 } |
| 938 |
| 939 return rects; |
| 940 } |
| 941 |
| 942 |
| 943 Vector2d RenderText::GetAlignmentOffset(size_t line_number) { |
| 944 DCHECK(!lines_.empty()); |
| 945 const int width = lines()[line_number].width; |
851 Vector2d offset; | 946 Vector2d offset; |
852 if (horizontal_alignment_ != ALIGN_LEFT) { | 947 if (horizontal_alignment_ != ALIGN_LEFT) { |
853 offset.set_x(display_rect().width() - GetContentWidth()); | 948 offset.set_x(display_rect().width() - width); |
854 if (horizontal_alignment_ == ALIGN_CENTER) | 949 if (horizontal_alignment_ == ALIGN_CENTER) |
855 offset.set_x(offset.x() / 2); | 950 offset.set_x(offset.x() / 2); |
856 } | 951 } |
857 if (vertical_alignment_ != ALIGN_TOP) { | 952 if (vertical_alignment_ != ALIGN_TOP) { |
858 offset.set_y(display_rect().height() - GetStringSize().height()); | 953 offset.set_y(display_rect().height() - GetMultilineTextSize().height()); |
859 if (vertical_alignment_ == ALIGN_VCENTER) | 954 if (vertical_alignment_ == ALIGN_VCENTER) |
860 offset.set_y(offset.y() / 2); | 955 offset.set_y(offset.y() / 2); |
861 } | 956 } |
862 return offset; | 957 return offset; |
863 } | 958 } |
864 | 959 |
865 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { | 960 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { |
866 if (!fade_head() && !fade_tail()) | 961 if (multiline() || !fade_head() && !fade_tail()) |
867 return; | 962 return; |
868 | 963 |
869 const int text_width = GetStringSize().width(); | |
870 const int display_width = display_rect().width(); | 964 const int display_width = display_rect().width(); |
871 | 965 |
872 // If the text fits as-is, no need to fade. | 966 // If the text fits as-is, no need to fade. |
873 if (text_width <= display_width) | 967 if (GetStringSize().width() <= display_width) |
874 return; | 968 return; |
875 | 969 |
876 int gradient_width = CalculateFadeGradientWidth(GetPrimaryFont(), | 970 int gradient_width = CalculateFadeGradientWidth(GetPrimaryFont(), |
877 display_width); | 971 display_width); |
878 if (gradient_width == 0) | 972 if (gradient_width == 0) |
879 return; | 973 return; |
880 | 974 |
881 bool fade_left = fade_head(); | 975 bool fade_left = fade_head(); |
882 bool fade_right = fade_tail(); | 976 bool fade_right = fade_tail(); |
883 // Under RTL, |fade_right| == |fade_head|. | 977 // Under RTL, |fade_right| == |fade_head|. |
(...skipping 11 matching lines...) Expand all Loading... |
895 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); | 989 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); |
896 solid_part.Inset(gradient_width, 0, 0, 0); | 990 solid_part.Inset(gradient_width, 0, 0, 0); |
897 } | 991 } |
898 if (fade_right) { | 992 if (fade_right) { |
899 right_part = solid_part; | 993 right_part = solid_part; |
900 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); | 994 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); |
901 solid_part.Inset(0, 0, gradient_width, 0); | 995 solid_part.Inset(0, 0, gradient_width, 0); |
902 } | 996 } |
903 | 997 |
904 Rect text_rect = display_rect(); | 998 Rect text_rect = display_rect(); |
905 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0); | 999 text_rect.Inset(GetAlignmentOffset(0).x(), 0, 0, 0); |
906 | 1000 |
907 // TODO(msw): Use the actual text colors corresponding to each faded part. | 1001 // TODO(msw): Use the actual text colors corresponding to each faded part. |
908 skia::RefPtr<SkShader> shader = CreateFadeShader( | 1002 skia::RefPtr<SkShader> shader = CreateFadeShader( |
909 text_rect, left_part, right_part, colors_.breaks().front().second); | 1003 text_rect, left_part, right_part, colors_.breaks().front().second); |
910 if (shader) | 1004 if (shader) |
911 renderer->SetShader(shader.get(), display_rect()); | 1005 renderer->SetShader(shader.get(), display_rect()); |
912 } | 1006 } |
913 | 1007 |
914 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { | 1008 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { |
915 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_); | 1009 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
959 } | 1053 } |
960 } | 1054 } |
961 | 1055 |
962 const base::string16& text = obscured_ ? layout_text_ : text_; | 1056 const base::string16& text = obscured_ ? layout_text_ : text_; |
963 if (truncate_length_ > 0 && truncate_length_ < text.length()) { | 1057 if (truncate_length_ > 0 && truncate_length_ < text.length()) { |
964 // Truncate the text at a valid character break and append an ellipsis. | 1058 // Truncate the text at a valid character break and append an ellipsis. |
965 icu::StringCharacterIterator iter(text.c_str()); | 1059 icu::StringCharacterIterator iter(text.c_str()); |
966 iter.setIndex32(truncate_length_ - 1); | 1060 iter.setIndex32(truncate_length_ - 1); |
967 layout_text_.assign(text.substr(0, iter.getIndex()) + ui::kEllipsisUTF16); | 1061 layout_text_.assign(text.substr(0, iter.getIndex()) + ui::kEllipsisUTF16); |
968 } | 1062 } |
| 1063 |
| 1064 line_breaks_.SetMax(0); |
969 } | 1065 } |
970 | 1066 |
971 void RenderText::UpdateCachedBoundsAndOffset() { | 1067 void RenderText::UpdateCachedBoundsAndOffset() { |
972 if (cached_bounds_and_offset_valid_) | 1068 if (cached_bounds_and_offset_valid_) |
973 return; | 1069 return; |
974 | 1070 |
| 1071 // TODO(ckocagil): Add vertical offset support for scrolling multi-line text. |
| 1072 |
975 // First, set the valid flag true to calculate the current cursor bounds using | 1073 // First, set the valid flag true to calculate the current cursor bounds using |
976 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 1074 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
977 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 1075 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
978 cached_bounds_and_offset_valid_ = true; | 1076 cached_bounds_and_offset_valid_ = true; |
979 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); | 1077 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
980 | 1078 |
981 // Update |display_offset_| to ensure the current cursor is visible. | 1079 // Update |display_offset_| to ensure the current cursor is visible. |
982 const int display_width = display_rect_.width(); | 1080 const int display_width = display_rect_.width(); |
983 const int content_width = GetContentWidth(); | 1081 const int content_width = GetContentWidth(); |
984 | 1082 |
(...skipping 30 matching lines...) Expand all Loading... |
1015 cursor_bounds_ += delta_offset; | 1113 cursor_bounds_ += delta_offset; |
1016 } | 1114 } |
1017 | 1115 |
1018 void RenderText::DrawSelection(Canvas* canvas) { | 1116 void RenderText::DrawSelection(Canvas* canvas) { |
1019 const std::vector<Rect> sel = GetSubstringBounds(selection()); | 1117 const std::vector<Rect> sel = GetSubstringBounds(selection()); |
1020 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) | 1118 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) |
1021 canvas->FillRect(*i, selection_background_focused_color_); | 1119 canvas->FillRect(*i, selection_background_focused_color_); |
1022 } | 1120 } |
1023 | 1121 |
1024 } // namespace gfx | 1122 } // namespace gfx |
OLD | NEW |