| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/views/controls/styled_label.h" | 5 #include "ui/views/controls/styled_label.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/strings/string_util.h" | 9 #include "base/strings/string_util.h" |
| 10 #include "ui/base/text/text_elider.h" | 10 #include "ui/base/text/text_elider.h" |
| 11 #include "ui/native_theme/native_theme.h" |
| 11 #include "ui/views/controls/label.h" | 12 #include "ui/views/controls/label.h" |
| 12 #include "ui/views/controls/link.h" | 13 #include "ui/views/controls/link.h" |
| 13 #include "ui/views/controls/styled_label_listener.h" | 14 #include "ui/views/controls/styled_label_listener.h" |
| 14 | 15 |
| 15 namespace views { | 16 namespace views { |
| 16 | 17 |
| 17 namespace { | 18 namespace { |
| 18 | 19 |
| 19 // Calculates the height of a line of text. Currently returns the height of | 20 // Calculates the height of a line of text. Currently returns the height of |
| 20 // a label. | 21 // a label. |
| 21 int CalculateLineHeight() { | 22 int CalculateLineHeight() { |
| 22 Label label; | 23 Label label; |
| 23 return label.GetPreferredSize().height(); | 24 return label.GetPreferredSize().height(); |
| 24 } | 25 } |
| 25 | 26 |
| 26 scoped_ptr<View> CreateLabelRange(const string16& text, | 27 scoped_ptr<Label> CreateLabelRange( |
| 27 const StyledLabel::RangeStyleInfo& style_info, | 28 const string16& text, |
| 28 views::LinkListener* link_listener) { | 29 const StyledLabel::RangeStyleInfo& style_info, |
| 30 views::LinkListener* link_listener) { |
| 29 scoped_ptr<Label> result; | 31 scoped_ptr<Label> result; |
| 30 | 32 |
| 31 if (style_info.is_link) { | 33 if (style_info.is_link) { |
| 32 Link* link = new Link(text); | 34 Link* link = new Link(text); |
| 33 link->set_listener(link_listener); | 35 link->set_listener(link_listener); |
| 34 link->SetUnderline((style_info.font_style & gfx::Font::UNDERLINE) != 0); | 36 link->SetUnderline((style_info.font_style & gfx::Font::UNDERLINE) != 0); |
| 35 result.reset(link); | 37 result.reset(link); |
| 36 } else { | 38 } else { |
| 37 Label* label = new Label(text); | 39 Label* label = new Label(text); |
| 38 // Give the label a focus border so that its preferred size matches | 40 // Give the label a focus border so that its preferred size matches |
| 39 // links' preferred sizes | 41 // links' preferred sizes |
| 40 label->SetHasFocusBorder(true); | 42 label->SetHasFocusBorder(true); |
| 41 | 43 |
| 42 result.reset(label); | 44 result.reset(label); |
| 43 } | 45 } |
| 44 | 46 |
| 47 result->SetEnabledColor(style_info.color); |
| 48 |
| 45 if (!style_info.tooltip.empty()) | 49 if (!style_info.tooltip.empty()) |
| 46 result->SetTooltipText(style_info.tooltip); | 50 result->SetTooltipText(style_info.tooltip); |
| 47 if (style_info.font_style != gfx::Font::NORMAL) | 51 if (style_info.font_style != gfx::Font::NORMAL) |
| 48 result->SetFont(result->font().DeriveFont(0, style_info.font_style)); | 52 result->SetFont(result->font().DeriveFont(0, style_info.font_style)); |
| 49 | 53 |
| 50 return scoped_ptr<View>(result.release()); | 54 return scoped_ptr<Label>(result.release()); |
| 51 } | 55 } |
| 52 | 56 |
| 53 } // namespace | 57 } // namespace |
| 54 | 58 |
| 55 | 59 |
| 56 StyledLabel::RangeStyleInfo::RangeStyleInfo() | 60 StyledLabel::RangeStyleInfo::RangeStyleInfo() |
| 57 : font_style(gfx::Font::NORMAL), | 61 : font_style(gfx::Font::NORMAL), |
| 62 color(ui::NativeTheme::instance()->GetSystemColor( |
| 63 ui::NativeTheme::kColorId_LabelEnabledColor)), |
| 58 disable_line_wrapping(false), | 64 disable_line_wrapping(false), |
| 59 is_link(false) { | 65 is_link(false) { |
| 60 } | 66 } |
| 61 | 67 |
| 62 StyledLabel::RangeStyleInfo::~RangeStyleInfo() {} | 68 StyledLabel::RangeStyleInfo::~RangeStyleInfo() {} |
| 63 | 69 |
| 64 // static | 70 // static |
| 65 StyledLabel::RangeStyleInfo StyledLabel::RangeStyleInfo::CreateForLink() { | 71 StyledLabel::RangeStyleInfo StyledLabel::RangeStyleInfo::CreateForLink() { |
| 66 RangeStyleInfo result; | 72 RangeStyleInfo result; |
| 67 result.disable_line_wrapping = true; | 73 result.disable_line_wrapping = true; |
| 68 result.is_link = true; | 74 result.is_link = true; |
| 69 result.font_style = gfx::Font::UNDERLINE; | 75 result.font_style = gfx::Font::UNDERLINE; |
| 76 result.color = Link::GetDefaultEnabledColor(); |
| 70 return result; | 77 return result; |
| 71 } | 78 } |
| 72 | 79 |
| 73 bool StyledLabel::StyleRange::operator<( | 80 bool StyledLabel::StyleRange::operator<( |
| 74 const StyledLabel::StyleRange& other) const { | 81 const StyledLabel::StyleRange& other) const { |
| 75 // Intentionally reversed so the priority queue is sorted by smallest first. | 82 // Intentionally reversed so the priority queue is sorted by smallest first. |
| 76 return range.start() > other.range.start(); | 83 return range.start() > other.range.start(); |
| 77 } | 84 } |
| 78 | 85 |
| 79 StyledLabel::StyledLabel(const string16& text, StyledLabelListener* listener) | 86 StyledLabel::StyledLabel(const string16& text, StyledLabelListener* listener) |
| 80 : listener_(listener) { | 87 : listener_(listener), |
| 88 auto_color_readability_(true), |
| 89 background_color_set_(false) { |
| 81 TrimWhitespace(text, TRIM_TRAILING, &text_); | 90 TrimWhitespace(text, TRIM_TRAILING, &text_); |
| 82 } | 91 } |
| 83 | 92 |
| 84 StyledLabel::~StyledLabel() {} | 93 StyledLabel::~StyledLabel() {} |
| 85 | 94 |
| 86 void StyledLabel::SetText(const string16& text) { | 95 void StyledLabel::SetText(const string16& text) { |
| 87 text_ = text; | 96 text_ = text; |
| 88 calculated_size_ = gfx::Size(); | 97 calculated_size_ = gfx::Size(); |
| 89 style_ranges_ = std::priority_queue<StyleRange>(); | 98 style_ranges_ = std::priority_queue<StyleRange>(); |
| 90 RemoveAllChildViews(true); | 99 RemoveAllChildViews(true); |
| 91 PreferredSizeChanged(); | 100 PreferredSizeChanged(); |
| 92 } | 101 } |
| 93 | 102 |
| 94 void StyledLabel::AddStyleRange(const ui::Range& range, | 103 void StyledLabel::AddStyleRange(const ui::Range& range, |
| 95 const RangeStyleInfo& style_info) { | 104 const RangeStyleInfo& style_info) { |
| 96 DCHECK(!range.is_reversed()); | 105 DCHECK(!range.is_reversed()); |
| 97 DCHECK(!range.is_empty()); | 106 DCHECK(!range.is_empty()); |
| 98 DCHECK(ui::Range(0, text_.size()).Contains(range)); | 107 DCHECK(ui::Range(0, text_.size()).Contains(range)); |
| 99 | 108 |
| 100 style_ranges_.push(StyleRange(range, style_info)); | 109 style_ranges_.push(StyleRange(range, style_info)); |
| 101 | 110 |
| 102 calculated_size_ = gfx::Size(); | 111 calculated_size_ = gfx::Size(); |
| 103 PreferredSizeChanged(); | 112 PreferredSizeChanged(); |
| 104 } | 113 } |
| 105 | 114 |
| 115 void StyledLabel::SetAutoColorReadabilityEnabled(bool enabled) { |
| 116 auto_color_readability_ = enabled; |
| 117 } |
| 118 |
| 119 void StyledLabel::SetBackgroundColor(SkColor color) { |
| 120 background_color_ = color; |
| 121 background_color_set_ = true; |
| 122 } |
| 123 |
| 106 gfx::Insets StyledLabel::GetInsets() const { | 124 gfx::Insets StyledLabel::GetInsets() const { |
| 107 gfx::Insets insets = View::GetInsets(); | 125 gfx::Insets insets = View::GetInsets(); |
| 108 const gfx::Insets focus_border_padding(1, 1, 1, 1); | 126 const gfx::Insets focus_border_padding(1, 1, 1, 1); |
| 109 insets += focus_border_padding; | 127 insets += focus_border_padding; |
| 110 return insets; | 128 return insets; |
| 111 } | 129 } |
| 112 | 130 |
| 113 int StyledLabel::GetHeightForWidth(int w) { | 131 int StyledLabel::GetHeightForWidth(int w) { |
| 114 if (w != calculated_size_.width()) | 132 if (w != calculated_size_.width()) |
| 115 calculated_size_ = gfx::Size(w, CalculateAndDoLayout(w, true)); | 133 calculated_size_ = gfx::Size(w, CalculateAndDoLayout(w, true)); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 continue; | 207 continue; |
| 190 } | 208 } |
| 191 break; | 209 break; |
| 192 } | 210 } |
| 193 | 211 |
| 194 x = 0; | 212 x = 0; |
| 195 line++; | 213 line++; |
| 196 continue; | 214 continue; |
| 197 } | 215 } |
| 198 | 216 |
| 199 scoped_ptr<View> view; | 217 scoped_ptr<Label> label; |
| 200 if (position >= range.start()) { | 218 if (position >= range.start()) { |
| 201 const RangeStyleInfo& style_info = style_ranges.top().style_info; | 219 const RangeStyleInfo& style_info = style_ranges.top().style_info; |
| 202 | 220 |
| 203 if (style_info.disable_line_wrapping && chunk.size() < range.length() && | 221 if (style_info.disable_line_wrapping && chunk.size() < range.length() && |
| 204 position == range.start() && x != 0) { | 222 position == range.start() && x != 0) { |
| 205 // If the chunk should not be wrapped, try to fit it entirely on the | 223 // If the chunk should not be wrapped, try to fit it entirely on the |
| 206 // next line. | 224 // next line. |
| 207 x = 0; | 225 x = 0; |
| 208 line++; | 226 line++; |
| 209 continue; | 227 continue; |
| 210 } | 228 } |
| 211 | 229 |
| 212 chunk = chunk.substr(0, std::min(chunk.size(), range.end() - position)); | 230 chunk = chunk.substr(0, std::min(chunk.size(), range.end() - position)); |
| 213 | 231 |
| 214 view = CreateLabelRange(chunk, style_info, this); | 232 label = CreateLabelRange(chunk, style_info, this); |
| 215 | 233 |
| 216 if (style_info.is_link && !dry_run) | 234 if (style_info.is_link && !dry_run) |
| 217 link_targets_[view.get()] = range; | 235 link_targets_[label.get()] = range; |
| 218 | 236 |
| 219 if (position + chunk.size() >= range.end()) | 237 if (position + chunk.size() >= range.end()) |
| 220 style_ranges.pop(); | 238 style_ranges.pop(); |
| 221 } else { | 239 } else { |
| 222 // This chunk is normal text. | 240 // This chunk is normal text. |
| 223 if (position + chunk.size() > range.start()) | 241 if (position + chunk.size() > range.start()) |
| 224 chunk = chunk.substr(0, range.start() - position); | 242 chunk = chunk.substr(0, range.start() - position); |
| 225 view = CreateLabelRange(chunk, RangeStyleInfo(), this); | 243 label = CreateLabelRange(chunk, RangeStyleInfo(), this); |
| 226 } | 244 } |
| 227 | 245 |
| 246 label->SetAutoColorReadabilityEnabled(auto_color_readability_); |
| 247 if (background_color_set_) |
| 248 label->SetBackgroundColor(background_color_); |
| 249 |
| 228 // Lay out the views to overlap by 1 pixel to compensate for their border | 250 // Lay out the views to overlap by 1 pixel to compensate for their border |
| 229 // spacing. Otherwise, "<a>link</a>," will render as "link ,". | 251 // spacing. Otherwise, "<a>link</a>," will render as "link ,". |
| 230 const int overlap = 1; | 252 const int overlap = 1; |
| 231 const gfx::Size view_size = view->GetPreferredSize(); | 253 const gfx::Size view_size = label->GetPreferredSize(); |
| 232 DCHECK_EQ(line_height, view_size.height() - 2 * overlap); | 254 DCHECK_EQ(line_height, view_size.height() - 2 * overlap); |
| 233 if (!dry_run) { | 255 if (!dry_run) { |
| 234 view->SetBoundsRect(gfx::Rect( | 256 label->SetBoundsRect(gfx::Rect( |
| 235 gfx::Point(GetInsets().left() + x - overlap, | 257 gfx::Point(GetInsets().left() + x - overlap, |
| 236 GetInsets().top() + line * line_height - overlap), | 258 GetInsets().top() + line * line_height - overlap), |
| 237 view_size)); | 259 view_size)); |
| 238 AddChildView(view.release()); | 260 AddChildView(label.release()); |
| 239 } | 261 } |
| 240 x += view_size.width() - 2 * overlap; | 262 x += view_size.width() - 2 * overlap; |
| 241 | 263 |
| 242 remaining_string = remaining_string.substr(chunk.size()); | 264 remaining_string = remaining_string.substr(chunk.size()); |
| 243 } | 265 } |
| 244 | 266 |
| 245 return (line + 1) * line_height + GetInsets().height(); | 267 return (line + 1) * line_height + GetInsets().height(); |
| 246 } | 268 } |
| 247 | 269 |
| 248 } // namespace views | 270 } // namespace views |
| OLD | NEW |