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 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
390 | 392 |
391 obscured_reveal_index_ = index; | 393 obscured_reveal_index_ = index; |
392 cached_bounds_and_offset_valid_ = false; | 394 cached_bounds_and_offset_valid_ = false; |
393 UpdateLayoutText(); | 395 UpdateLayoutText(); |
394 ResetLayout(); | 396 ResetLayout(); |
395 } | 397 } |
396 | 398 |
397 void RenderText::SetDisplayRect(const Rect& r) { | 399 void RenderText::SetDisplayRect(const Rect& r) { |
398 display_rect_ = r; | 400 display_rect_ = r; |
399 cached_bounds_and_offset_valid_ = false; | 401 cached_bounds_and_offset_valid_ = false; |
402 lines_.clear(); | |
400 } | 403 } |
401 | 404 |
402 void RenderText::SetCursorPosition(size_t position) { | 405 void RenderText::SetCursorPosition(size_t position) { |
403 MoveCursorTo(position, false); | 406 MoveCursorTo(position, false); |
404 } | 407 } |
405 | 408 |
406 void RenderText::MoveCursor(BreakType break_type, | 409 void RenderText::MoveCursor(BreakType break_type, |
407 VisualCursorDirection direction, | 410 VisualCursorDirection direction, |
408 bool select) { | 411 bool select) { |
409 SelectionModel position(cursor_position(), selection_model_.caret_affinity()); | 412 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), | 759 selection_color_(kDefaultColor), |
757 selection_background_focused_color_(kDefaultSelectionBackgroundColor), | 760 selection_background_focused_color_(kDefaultSelectionBackgroundColor), |
758 focused_(false), | 761 focused_(false), |
759 composition_range_(ui::Range::InvalidRange()), | 762 composition_range_(ui::Range::InvalidRange()), |
760 colors_(kDefaultColor), | 763 colors_(kDefaultColor), |
761 styles_(NUM_TEXT_STYLES), | 764 styles_(NUM_TEXT_STYLES), |
762 composition_and_selection_styles_applied_(false), | 765 composition_and_selection_styles_applied_(false), |
763 obscured_(false), | 766 obscured_(false), |
764 obscured_reveal_index_(-1), | 767 obscured_reveal_index_(-1), |
765 truncate_length_(0), | 768 truncate_length_(0), |
769 multiline_(false), | |
766 fade_head_(false), | 770 fade_head_(false), |
767 fade_tail_(false), | 771 fade_tail_(false), |
768 background_is_transparent_(false), | 772 background_is_transparent_(false), |
769 clip_to_display_rect_(true), | 773 clip_to_display_rect_(true), |
770 cached_bounds_and_offset_valid_(false) { | 774 cached_bounds_and_offset_valid_(false) { |
771 } | 775 } |
772 | 776 |
773 const Vector2d& RenderText::GetUpdatedDisplayOffset() { | 777 const Vector2d& RenderText::GetUpdatedDisplayOffset() { |
774 UpdateCachedBoundsAndOffset(); | 778 UpdateCachedBoundsAndOffset(); |
775 return display_offset_; | 779 return display_offset_; |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
825 } | 829 } |
826 | 830 |
827 void RenderText::UndoCompositionAndSelectionStyles() { | 831 void RenderText::UndoCompositionAndSelectionStyles() { |
828 // Restore the underline and color breaks to undo the temporary styles. | 832 // Restore the underline and color breaks to undo the temporary styles. |
829 DCHECK(composition_and_selection_styles_applied_); | 833 DCHECK(composition_and_selection_styles_applied_); |
830 colors_ = saved_colors_; | 834 colors_ = saved_colors_; |
831 styles_[UNDERLINE] = saved_underlines_; | 835 styles_[UNDERLINE] = saved_underlines_; |
832 composition_and_selection_styles_applied_ = false; | 836 composition_and_selection_styles_applied_ = false; |
833 } | 837 } |
834 | 838 |
835 Vector2d RenderText::GetTextOffset() { | 839 Vector2d RenderText::GetLineOffset(size_t line_number) { |
836 Vector2d offset = display_rect().OffsetFromOrigin(); | 840 Vector2d offset = display_rect().OffsetFromOrigin(); |
837 offset.Add(GetUpdatedDisplayOffset()); | 841 if (!multiline()) |
838 offset.Add(GetAlignmentOffset()); | 842 offset.Add(GetUpdatedDisplayOffset()); |
843 else | |
844 offset.Add(Vector2d(0, lines()[line_number].preceding_heights)); | |
845 offset.Add(GetAlignmentOffset(line_number)); | |
839 return offset; | 846 return offset; |
840 } | 847 } |
841 | 848 |
842 Point RenderText::ToTextPoint(const Point& point) { | 849 Point RenderText::ToTextPoint(const Point& point) { |
843 return point - GetTextOffset(); | 850 return point - GetLineOffset(0); |
851 // TODO(ckocagil): implement multiline. | |
844 } | 852 } |
845 | 853 |
846 Point RenderText::ToViewPoint(const Point& point) { | 854 Point RenderText::ToViewPoint(const Point& point) { |
847 return point + GetTextOffset(); | 855 if (!multiline()) |
856 return point + GetLineOffset(0); | |
857 | |
858 // TODO(ckocagil): refactor this to traverse all line segments to support rtl. | |
859 DCHECK(!lines_.empty()); | |
860 int x = point.x(); | |
861 unsigned int line = 0; | |
862 while (line < lines_.size() && x > lines()[line].width) { | |
863 x -= lines()[line].width; | |
864 ++line; | |
865 } | |
866 return Point(x, point.y()) + GetLineOffset(line); | |
848 } | 867 } |
849 | 868 |
850 Vector2d RenderText::GetAlignmentOffset() { | 869 std::vector<Rect> RenderText::TextBoundsToViewBounds(const ui::Range& x) { |
870 std::vector<Rect> rects; | |
871 | |
872 if (!multiline()) { | |
873 rects.push_back(Rect(ToViewPoint(Point(x.GetMin(), 0)), | |
874 Size(x.length(), GetStringSize().height()))); | |
875 return rects; | |
876 } | |
877 | |
878 EnsureLayout(); | |
879 | |
880 // Each line segments keeps its position in text coordinates. Traverse all | |
881 // line segments and if the segment intersects with the given range, add the | |
882 // view rect corresponding to the intersection to |rects|. | |
883 for (size_t line = 0; line < lines().size(); ++line) { | |
884 int line_x = 0; | |
885 Vector2d offset = GetLineOffset(line); | |
886 for (size_t i = 0; i < lines()[line].segments.size(); ++i) { | |
887 const internal::LineSegment* segment = lines()[line].segments[i]; | |
888 ui::Range intersection = segment->x_pos.Intersect(x); | |
889 if (!intersection.is_empty()) { | |
890 Rect rect(line_x + intersection.start() - segment->x_pos.start(), | |
891 0, intersection.length(), lines()[line].height); | |
892 rect += offset; | |
893 rects.push_back(rect); | |
894 } | |
895 line_x += segment->x_pos.length(); | |
896 } | |
897 } | |
898 | |
899 return rects; | |
900 } | |
901 | |
902 | |
903 Vector2d RenderText::GetAlignmentOffset(size_t line_number) { | |
904 DCHECK(!lines_.empty()); | |
905 const int width = lines()[line_number].width; | |
851 Vector2d offset; | 906 Vector2d offset; |
852 if (horizontal_alignment_ != ALIGN_LEFT) { | 907 if (horizontal_alignment_ != ALIGN_LEFT) { |
853 offset.set_x(display_rect().width() - GetContentWidth()); | 908 offset.set_x(display_rect().width() - width); |
854 if (horizontal_alignment_ == ALIGN_CENTER) | 909 if (horizontal_alignment_ == ALIGN_CENTER) |
855 offset.set_x(offset.x() / 2); | 910 offset.set_x(offset.x() / 2); |
856 } | 911 } |
857 if (vertical_alignment_ != ALIGN_TOP) { | 912 if (vertical_alignment_ != ALIGN_TOP) { |
858 offset.set_y(display_rect().height() - GetStringSize().height()); | 913 offset.set_y(display_rect().height() - GetMultilineTextSize().height()); |
859 if (vertical_alignment_ == ALIGN_VCENTER) | 914 if (vertical_alignment_ == ALIGN_VCENTER) |
860 offset.set_y(offset.y() / 2); | 915 offset.set_y(offset.y() / 2); |
861 } | 916 } |
862 return offset; | 917 return offset; |
863 } | 918 } |
864 | 919 |
865 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { | 920 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { |
866 if (!fade_head() && !fade_tail()) | 921 if (multiline() || !fade_head() && !fade_tail()) |
867 return; | 922 return; |
868 | 923 |
869 const int text_width = GetStringSize().width(); | |
870 const int display_width = display_rect().width(); | 924 const int display_width = display_rect().width(); |
871 | 925 |
872 // If the text fits as-is, no need to fade. | 926 // If the text fits as-is, no need to fade. |
873 if (text_width <= display_width) | 927 if (GetStringSize().width() <= display_width) |
874 return; | 928 return; |
875 | 929 |
876 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); | 930 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); |
877 if (gradient_width == 0) | 931 if (gradient_width == 0) |
878 return; | 932 return; |
879 | 933 |
880 bool fade_left = fade_head(); | 934 bool fade_left = fade_head(); |
881 bool fade_right = fade_tail(); | 935 bool fade_right = fade_tail(); |
882 // Under RTL, |fade_right| == |fade_head|. | 936 // Under RTL, |fade_right| == |fade_head|. |
883 // TODO(asvitkine): This is currently not based on GetTextDirection() because | 937 // TODO(asvitkine): This is currently not based on GetTextDirection() because |
(...skipping 10 matching lines...) Expand all Loading... | |
894 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); | 948 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); |
895 solid_part.Inset(gradient_width, 0, 0, 0); | 949 solid_part.Inset(gradient_width, 0, 0, 0); |
896 } | 950 } |
897 if (fade_right) { | 951 if (fade_right) { |
898 right_part = solid_part; | 952 right_part = solid_part; |
899 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); | 953 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); |
900 solid_part.Inset(0, 0, gradient_width, 0); | 954 solid_part.Inset(0, 0, gradient_width, 0); |
901 } | 955 } |
902 | 956 |
903 Rect text_rect = display_rect(); | 957 Rect text_rect = display_rect(); |
904 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0); | 958 text_rect.Inset(GetAlignmentOffset(0).x(), 0, 0, 0); |
905 | 959 |
906 // TODO(msw): Use the actual text colors corresponding to each faded part. | 960 // TODO(msw): Use the actual text colors corresponding to each faded part. |
907 skia::RefPtr<SkShader> shader = CreateFadeShader( | 961 skia::RefPtr<SkShader> shader = CreateFadeShader( |
908 text_rect, left_part, right_part, colors_.breaks().front().second); | 962 text_rect, left_part, right_part, colors_.breaks().front().second); |
909 if (shader) | 963 if (shader) |
910 renderer->SetShader(shader.get(), display_rect()); | 964 renderer->SetShader(shader.get(), display_rect()); |
911 } | 965 } |
912 | 966 |
913 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { | 967 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { |
914 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_); | 968 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
958 } | 1012 } |
959 } | 1013 } |
960 | 1014 |
961 const base::string16& text = obscured_ ? layout_text_ : text_; | 1015 const base::string16& text = obscured_ ? layout_text_ : text_; |
962 if (truncate_length_ > 0 && truncate_length_ < text.length()) { | 1016 if (truncate_length_ > 0 && truncate_length_ < text.length()) { |
963 // Truncate the text at a valid character break and append an ellipsis. | 1017 // Truncate the text at a valid character break and append an ellipsis. |
964 icu::StringCharacterIterator iter(text.c_str()); | 1018 icu::StringCharacterIterator iter(text.c_str()); |
965 iter.setIndex32(truncate_length_ - 1); | 1019 iter.setIndex32(truncate_length_ - 1); |
966 layout_text_.assign(text.substr(0, iter.getIndex()) + ui::kEllipsisUTF16); | 1020 layout_text_.assign(text.substr(0, iter.getIndex()) + ui::kEllipsisUTF16); |
967 } | 1021 } |
1022 | |
1023 const string16& layout_text = GetLayoutText(); | |
1024 line_breaks_.SetValue(0); | |
1025 line_breaks_.SetMax(layout_text.length()); | |
1026 base::i18n::BreakIterator iter(layout_text, | |
1027 base::i18n::BreakIterator::BREAK_LINE); | |
Alexei Svitkine (slow)
2013/07/22 20:52:05
There's a comment in download_item_view.cc that BR
ckocagil
2013/08/07 12:59:20
Done, it's completely lazy now.
| |
1028 bool success = iter.Init(); | |
1029 CHECK(success); | |
1030 do { | |
1031 line_breaks_.ApplyValue(iter.pos(), | |
1032 ui::Range(iter.pos(), layout_text.length())); | |
1033 } while (iter.Advance()); | |
968 } | 1034 } |
969 | 1035 |
970 void RenderText::UpdateCachedBoundsAndOffset() { | 1036 void RenderText::UpdateCachedBoundsAndOffset() { |
971 if (cached_bounds_and_offset_valid_) | 1037 if (cached_bounds_and_offset_valid_) |
972 return; | 1038 return; |
973 | 1039 |
1040 // TODO(ckocagil): Add vertical offset support for scrolling multi-line text. | |
1041 | |
974 // First, set the valid flag true to calculate the current cursor bounds using | 1042 // First, set the valid flag true to calculate the current cursor bounds using |
975 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 1043 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
976 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 1044 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
977 cached_bounds_and_offset_valid_ = true; | 1045 cached_bounds_and_offset_valid_ = true; |
978 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); | 1046 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
979 | 1047 |
980 // Update |display_offset_| to ensure the current cursor is visible. | 1048 // Update |display_offset_| to ensure the current cursor is visible. |
981 const int display_width = display_rect_.width(); | 1049 const int display_width = display_rect_.width(); |
982 const int content_width = GetContentWidth(); | 1050 const int content_width = GetContentWidth(); |
983 | 1051 |
(...skipping 30 matching lines...) Expand all Loading... | |
1014 cursor_bounds_ += delta_offset; | 1082 cursor_bounds_ += delta_offset; |
1015 } | 1083 } |
1016 | 1084 |
1017 void RenderText::DrawSelection(Canvas* canvas) { | 1085 void RenderText::DrawSelection(Canvas* canvas) { |
1018 const std::vector<Rect> sel = GetSubstringBounds(selection()); | 1086 const std::vector<Rect> sel = GetSubstringBounds(selection()); |
1019 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) | 1087 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) |
1020 canvas->FillRect(*i, selection_background_focused_color_); | 1088 canvas->FillRect(*i, selection_background_focused_color_); |
1021 } | 1089 } |
1022 | 1090 |
1023 } // namespace gfx | 1091 } // namespace gfx |
OLD | NEW |