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 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
392 | 394 |
393 obscured_reveal_index_ = index; | 395 obscured_reveal_index_ = index; |
394 cached_bounds_and_offset_valid_ = false; | 396 cached_bounds_and_offset_valid_ = false; |
395 UpdateLayoutText(); | 397 UpdateLayoutText(); |
396 ResetLayout(); | 398 ResetLayout(); |
397 } | 399 } |
398 | 400 |
399 void RenderText::SetDisplayRect(const Rect& r) { | 401 void RenderText::SetDisplayRect(const Rect& r) { |
400 display_rect_ = r; | 402 display_rect_ = r; |
401 cached_bounds_and_offset_valid_ = false; | 403 cached_bounds_and_offset_valid_ = false; |
404 lines_valid_ = false; | |
402 } | 405 } |
403 | 406 |
404 void RenderText::SetCursorPosition(size_t position) { | 407 void RenderText::SetCursorPosition(size_t position) { |
405 MoveCursorTo(position, false); | 408 MoveCursorTo(position, false); |
406 } | 409 } |
407 | 410 |
408 void RenderText::MoveCursor(BreakType break_type, | 411 void RenderText::MoveCursor(BreakType break_type, |
409 VisualCursorDirection direction, | 412 VisualCursorDirection direction, |
410 bool select) { | 413 bool select) { |
411 SelectionModel position(cursor_position(), selection_model_.caret_affinity()); | 414 SelectionModel position(cursor_position(), selection_model_.caret_affinity()); |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
698 if (insert_mode) { | 701 if (insert_mode) { |
699 x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start(); | 702 x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start(); |
700 } else { // overtype mode | 703 } else { // overtype mode |
701 x = xspan.GetMin(); | 704 x = xspan.GetMin(); |
702 width = xspan.length(); | 705 width = xspan.length(); |
703 } | 706 } |
704 } | 707 } |
705 return Rect(ToViewPoint(Point(x, 0)), Size(width, size.height())); | 708 return Rect(ToViewPoint(Point(x, 0)), Size(width, size.height())); |
706 } | 709 } |
707 | 710 |
711 std::vector<Rect> RenderText::RangeToViewRects(const ui::Range& x) { | |
msw
2013/07/17 06:47:18
This function should be defined in the order it's
ckocagil
2013/07/19 19:40:50
Done.
| |
712 std::vector<Rect> rects; | |
713 | |
714 if (!multiline()) { | |
715 Point start(x.GetMin(), 0); | |
716 rects.push_back(Rect(ToViewPoint(start), Size(x.length(), | |
msw
2013/07/17 06:47:18
nit: inline start and break the line before 'Size(
ckocagil
2013/07/19 19:40:50
Done.
| |
717 GetStringSize().height()))); | |
718 return rects; | |
719 } | |
720 | |
721 EnsureLayout(); | |
722 | |
723 // Each line segments keeps its position in text coordinates. Traverse all | |
724 // line segments and if the segment intersects with the given range, add the | |
725 // view rect corresponding to the intersection to |rects|. | |
726 for (size_t line = 0; line < lines().size(); ++line) { | |
727 int line_x = 0; | |
728 Vector2d offset = GetLineOffset(line); | |
729 for (size_t i = 0; i < lines()[line].segments.size(); ++i) { | |
730 const internal::LineSegment* segment = lines()[line].segments[i]; | |
731 ui::Range intersection = segment->x_pos.Intersect(x); | |
732 if (!intersection.is_empty()) { | |
733 Rect rect(line_x + intersection.start() - segment->x_pos.start(), | |
734 0, intersection.length(), lines()[line].height); | |
735 rect += offset; | |
736 rects.push_back(rect); | |
737 } | |
738 line_x += segment->x_pos.length(); | |
739 } | |
740 } | |
741 | |
742 return rects; | |
743 } | |
744 | |
708 const Rect& RenderText::GetUpdatedCursorBounds() { | 745 const Rect& RenderText::GetUpdatedCursorBounds() { |
709 UpdateCachedBoundsAndOffset(); | 746 UpdateCachedBoundsAndOffset(); |
710 return cursor_bounds_; | 747 return cursor_bounds_; |
711 } | 748 } |
712 | 749 |
713 size_t RenderText::IndexOfAdjacentGrapheme(size_t index, | 750 size_t RenderText::IndexOfAdjacentGrapheme(size_t index, |
714 LogicalCursorDirection direction) { | 751 LogicalCursorDirection direction) { |
715 if (index > text().length()) | 752 if (index > text().length()) |
716 return text().length(); | 753 return text().length(); |
717 | 754 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
758 selection_color_(kDefaultColor), | 795 selection_color_(kDefaultColor), |
759 selection_background_focused_color_(kDefaultSelectionBackgroundColor), | 796 selection_background_focused_color_(kDefaultSelectionBackgroundColor), |
760 focused_(false), | 797 focused_(false), |
761 composition_range_(ui::Range::InvalidRange()), | 798 composition_range_(ui::Range::InvalidRange()), |
762 colors_(kDefaultColor), | 799 colors_(kDefaultColor), |
763 styles_(NUM_TEXT_STYLES), | 800 styles_(NUM_TEXT_STYLES), |
764 composition_and_selection_styles_applied_(false), | 801 composition_and_selection_styles_applied_(false), |
765 obscured_(false), | 802 obscured_(false), |
766 obscured_reveal_index_(-1), | 803 obscured_reveal_index_(-1), |
767 truncate_length_(0), | 804 truncate_length_(0), |
805 multiline_(false), | |
768 fade_head_(false), | 806 fade_head_(false), |
769 fade_tail_(false), | 807 fade_tail_(false), |
770 background_is_transparent_(false), | 808 background_is_transparent_(false), |
771 clip_to_display_rect_(true), | 809 clip_to_display_rect_(true), |
772 cached_bounds_and_offset_valid_(false) { | 810 cached_bounds_and_offset_valid_(false), |
811 lines_valid_(false) { | |
773 } | 812 } |
774 | 813 |
775 const Vector2d& RenderText::GetUpdatedDisplayOffset() { | 814 const Vector2d& RenderText::GetUpdatedDisplayOffset() { |
776 UpdateCachedBoundsAndOffset(); | 815 UpdateCachedBoundsAndOffset(); |
777 return display_offset_; | 816 return display_offset_; |
778 } | 817 } |
779 | 818 |
780 SelectionModel RenderText::GetAdjacentSelectionModel( | 819 SelectionModel RenderText::GetAdjacentSelectionModel( |
781 const SelectionModel& current, | 820 const SelectionModel& current, |
782 BreakType break_type, | 821 BreakType break_type, |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
827 } | 866 } |
828 | 867 |
829 void RenderText::UndoCompositionAndSelectionStyles() { | 868 void RenderText::UndoCompositionAndSelectionStyles() { |
830 // Restore the underline and color breaks to undo the temporary styles. | 869 // Restore the underline and color breaks to undo the temporary styles. |
831 DCHECK(composition_and_selection_styles_applied_); | 870 DCHECK(composition_and_selection_styles_applied_); |
832 colors_ = saved_colors_; | 871 colors_ = saved_colors_; |
833 styles_[UNDERLINE] = saved_underlines_; | 872 styles_[UNDERLINE] = saved_underlines_; |
834 composition_and_selection_styles_applied_ = false; | 873 composition_and_selection_styles_applied_ = false; |
835 } | 874 } |
836 | 875 |
837 Vector2d RenderText::GetTextOffset() { | 876 Vector2d RenderText::GetLineOffset(int line) { |
838 Vector2d offset = display_rect().OffsetFromOrigin(); | 877 Vector2d offset = display_rect().OffsetFromOrigin(); |
839 offset.Add(GetUpdatedDisplayOffset()); | 878 if (!multiline()) |
840 offset.Add(GetAlignmentOffset()); | 879 offset.Add(GetUpdatedDisplayOffset()); |
880 else | |
881 offset.Add(Vector2d(0, lines()[line].preceding_heights)); | |
msw
2013/07/17 06:47:18
Does this need to add a per-line horizontal offset
ckocagil
2013/07/19 19:40:50
Which horizontal offset? I don't see your point.
msw
2013/08/15 02:44:17
Nevermind! The line's alignment offset is added ri
| |
882 offset.Add(GetAlignmentOffset(line)); | |
841 return offset; | 883 return offset; |
842 } | 884 } |
843 | 885 |
844 Point RenderText::ToTextPoint(const Point& point) { | 886 Point RenderText::ToTextPoint(const Point& point) { |
845 return point - GetTextOffset(); | 887 return point - GetLineOffset(0); |
888 // TODO(ckocagil): implement multiline. | |
846 } | 889 } |
847 | 890 |
848 Point RenderText::ToViewPoint(const Point& point) { | 891 Point RenderText::ToViewPoint(const Point& point) { |
849 return point + GetTextOffset(); | 892 if (!multiline()) |
893 return point + GetLineOffset(0); | |
894 | |
895 // TODO(ckocagil): refactor this to traverse all line segments to support rtl. | |
896 DCHECK(lines_valid_); | |
897 int x = point.x(); | |
898 unsigned int line = 0; | |
899 while (line < lines_.size() && x > lines()[line].width) { | |
900 x -= lines()[line].width; | |
901 ++line; | |
902 } | |
903 return Point(x, point.y()) + GetLineOffset(line); | |
850 } | 904 } |
851 | 905 |
852 Vector2d RenderText::GetAlignmentOffset() { | 906 Vector2d RenderText::GetAlignmentOffset(int line) { |
907 int width = lines()[line].width; | |
msw
2013/07/17 06:47:18
nit: make this const, maybe DCHECK lines_ validity
ckocagil
2013/07/19 19:40:50
Done.
| |
853 Vector2d offset; | 908 Vector2d offset; |
854 if (horizontal_alignment_ != ALIGN_LEFT) { | 909 if (horizontal_alignment_ != ALIGN_LEFT) { |
855 offset.set_x(display_rect().width() - GetContentWidth()); | 910 offset.set_x(display_rect().width() - width); |
856 if (horizontal_alignment_ == ALIGN_CENTER) | 911 if (horizontal_alignment_ == ALIGN_CENTER) |
857 offset.set_x(offset.x() / 2); | 912 offset.set_x(offset.x() / 2); |
858 } | 913 } |
859 if (vertical_alignment_ != ALIGN_TOP) { | 914 if (vertical_alignment_ != ALIGN_TOP) { |
860 offset.set_y(display_rect().height() - GetStringSize().height()); | 915 offset.set_y(display_rect().height() - GetMultilineTextSize().height()); |
861 if (vertical_alignment_ == ALIGN_VCENTER) | 916 if (vertical_alignment_ == ALIGN_VCENTER) |
862 offset.set_y(offset.y() / 2); | 917 offset.set_y(offset.y() / 2); |
863 } | 918 } |
864 return offset; | 919 return offset; |
865 } | 920 } |
866 | 921 |
867 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { | 922 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { |
868 if (!fade_head() && !fade_tail()) | 923 if (multiline() || !fade_head() && !fade_tail()) |
869 return; | 924 return; |
870 | 925 |
871 const int text_width = GetStringSize().width(); | |
872 const int display_width = display_rect().width(); | 926 const int display_width = display_rect().width(); |
873 | 927 |
874 // If the text fits as-is, no need to fade. | 928 // If the text fits as-is, no need to fade. |
875 if (text_width <= display_width) | 929 if (GetStringSize().width() <= display_width) |
876 return; | 930 return; |
877 | 931 |
878 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); | 932 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); |
879 if (gradient_width == 0) | 933 if (gradient_width == 0) |
880 return; | 934 return; |
881 | 935 |
882 bool fade_left = fade_head(); | 936 bool fade_left = fade_head(); |
883 bool fade_right = fade_tail(); | 937 bool fade_right = fade_tail(); |
884 // Under RTL, |fade_right| == |fade_head|. | 938 // Under RTL, |fade_right| == |fade_head|. |
885 // TODO(asvitkine): This is currently not based on GetTextDirection() because | 939 // TODO(asvitkine): This is currently not based on GetTextDirection() because |
(...skipping 10 matching lines...) Expand all Loading... | |
896 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); | 950 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); |
897 solid_part.Inset(gradient_width, 0, 0, 0); | 951 solid_part.Inset(gradient_width, 0, 0, 0); |
898 } | 952 } |
899 if (fade_right) { | 953 if (fade_right) { |
900 right_part = solid_part; | 954 right_part = solid_part; |
901 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); | 955 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); |
902 solid_part.Inset(0, 0, gradient_width, 0); | 956 solid_part.Inset(0, 0, gradient_width, 0); |
903 } | 957 } |
904 | 958 |
905 Rect text_rect = display_rect(); | 959 Rect text_rect = display_rect(); |
906 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0); | 960 text_rect.Inset(GetAlignmentOffset(0).x(), 0, 0, 0); |
907 | 961 |
908 // TODO(msw): Use the actual text colors corresponding to each faded part. | 962 // TODO(msw): Use the actual text colors corresponding to each faded part. |
909 skia::RefPtr<SkShader> shader = CreateFadeShader( | 963 skia::RefPtr<SkShader> shader = CreateFadeShader( |
910 text_rect, left_part, right_part, colors_.breaks().front().second); | 964 text_rect, left_part, right_part, colors_.breaks().front().second); |
911 if (shader) | 965 if (shader) |
912 renderer->SetShader(shader.get(), display_rect()); | 966 renderer->SetShader(shader.get(), display_rect()); |
913 } | 967 } |
914 | 968 |
915 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { | 969 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { |
916 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_); | 970 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
960 } | 1014 } |
961 } | 1015 } |
962 | 1016 |
963 const base::string16& text = obscured_ ? layout_text_ : text_; | 1017 const base::string16& text = obscured_ ? layout_text_ : text_; |
964 if (truncate_length_ > 0 && truncate_length_ < text.length()) { | 1018 if (truncate_length_ > 0 && truncate_length_ < text.length()) { |
965 // Truncate the text at a valid character break and append an ellipsis. | 1019 // Truncate the text at a valid character break and append an ellipsis. |
966 icu::StringCharacterIterator iter(text.c_str()); | 1020 icu::StringCharacterIterator iter(text.c_str()); |
967 iter.setIndex32(truncate_length_ - 1); | 1021 iter.setIndex32(truncate_length_ - 1); |
968 layout_text_.assign(text.substr(0, iter.getIndex()) + ui::kEllipsisUTF16); | 1022 layout_text_.assign(text.substr(0, iter.getIndex()) + ui::kEllipsisUTF16); |
969 } | 1023 } |
1024 | |
1025 line_breaks_.clear(); | |
1026 base::i18n::BreakIterator iter(GetLayoutText(), | |
1027 base::i18n::BreakIterator::BREAK_LINE); | |
1028 bool success = iter.Init(); | |
1029 CHECK(success); | |
1030 do { | |
1031 line_breaks_.push_back(iter.pos()); | |
1032 } while (iter.Advance()); | |
970 } | 1033 } |
971 | 1034 |
972 void RenderText::UpdateCachedBoundsAndOffset() { | 1035 void RenderText::UpdateCachedBoundsAndOffset() { |
973 if (cached_bounds_and_offset_valid_) | 1036 if (cached_bounds_and_offset_valid_) |
974 return; | 1037 return; |
975 | 1038 |
1039 // TODO(ckocagil): Add vertical offset support for scrolling multi-line text. | |
1040 | |
976 // First, set the valid flag true to calculate the current cursor bounds using | 1041 // First, set the valid flag true to calculate the current cursor bounds using |
977 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 1042 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
978 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 1043 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
979 cached_bounds_and_offset_valid_ = true; | 1044 cached_bounds_and_offset_valid_ = true; |
980 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); | 1045 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
981 | 1046 |
982 // Update |display_offset_| to ensure the current cursor is visible. | 1047 // Update |display_offset_| to ensure the current cursor is visible. |
983 const int display_width = display_rect_.width(); | 1048 const int display_width = display_rect_.width(); |
984 const int content_width = GetContentWidth(); | 1049 const int content_width = GetContentWidth(); |
985 | 1050 |
(...skipping 30 matching lines...) Expand all Loading... | |
1016 cursor_bounds_ += delta_offset; | 1081 cursor_bounds_ += delta_offset; |
1017 } | 1082 } |
1018 | 1083 |
1019 void RenderText::DrawSelection(Canvas* canvas) { | 1084 void RenderText::DrawSelection(Canvas* canvas) { |
1020 const std::vector<Rect> sel = GetSubstringBounds(selection()); | 1085 const std::vector<Rect> sel = GetSubstringBounds(selection()); |
1021 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) | 1086 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) |
1022 canvas->FillRect(*i, selection_background_focused_color_); | 1087 canvas->FillRect(*i, selection_background_focused_color_); |
1023 } | 1088 } |
1024 | 1089 |
1025 } // namespace gfx | 1090 } // namespace gfx |
OLD | NEW |