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