Chromium Code Reviews| 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 |