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 <limits> |
7 #include <vector> | 8 #include <vector> |
8 | 9 |
9 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
10 #include "ui/gfx/font_list.h" | 11 #include "ui/gfx/font_list.h" |
11 #include "ui/gfx/text_elider.h" | 12 #include "ui/gfx/text_elider.h" |
12 #include "ui/native_theme/native_theme.h" | 13 #include "ui/native_theme/native_theme.h" |
13 #include "ui/views/controls/label.h" | 14 #include "ui/views/controls/label.h" |
14 #include "ui/views/controls/link.h" | 15 #include "ui/views/controls/link.h" |
15 #include "ui/views/controls/styled_label_listener.h" | 16 #include "ui/views/controls/styled_label_listener.h" |
16 | 17 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
89 return range.start() < other.range.start(); | 90 return range.start() < other.range.start(); |
90 } | 91 } |
91 | 92 |
92 | 93 |
93 // StyledLabel ---------------------------------------------------------------- | 94 // StyledLabel ---------------------------------------------------------------- |
94 | 95 |
95 StyledLabel::StyledLabel(const base::string16& text, | 96 StyledLabel::StyledLabel(const base::string16& text, |
96 StyledLabelListener* listener) | 97 StyledLabelListener* listener) |
97 : specified_line_height_(0), | 98 : specified_line_height_(0), |
98 listener_(listener), | 99 listener_(listener), |
| 100 width_at_last_size_calculation_(0), |
99 width_at_last_layout_(0), | 101 width_at_last_layout_(0), |
100 displayed_on_background_color_(SkColorSetRGB(0xFF, 0xFF, 0xFF)), | 102 displayed_on_background_color_(SkColorSetRGB(0xFF, 0xFF, 0xFF)), |
101 displayed_on_background_color_set_(false), | 103 displayed_on_background_color_set_(false), |
102 auto_color_readability_enabled_(true) { | 104 auto_color_readability_enabled_(true) { |
103 base::TrimWhitespace(text, base::TRIM_TRAILING, &text_); | 105 base::TrimWhitespace(text, base::TRIM_TRAILING, &text_); |
104 } | 106 } |
105 | 107 |
106 StyledLabel::~StyledLabel() {} | 108 StyledLabel::~StyledLabel() {} |
107 | 109 |
108 void StyledLabel::SetText(const base::string16& text) { | 110 void StyledLabel::SetText(const base::string16& text) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 displayed_on_background_color_ = color; | 151 displayed_on_background_color_ = color; |
150 displayed_on_background_color_set_ = true; | 152 displayed_on_background_color_set_ = true; |
151 | 153 |
152 for (int i = 0, count = child_count(); i < count; ++i) { | 154 for (int i = 0, count = child_count(); i < count; ++i) { |
153 DCHECK((child_at(i)->GetClassName() == Label::kViewClassName) || | 155 DCHECK((child_at(i)->GetClassName() == Label::kViewClassName) || |
154 (child_at(i)->GetClassName() == Link::kViewClassName)); | 156 (child_at(i)->GetClassName() == Link::kViewClassName)); |
155 static_cast<Label*>(child_at(i))->SetBackgroundColor(color); | 157 static_cast<Label*>(child_at(i))->SetBackgroundColor(color); |
156 } | 158 } |
157 } | 159 } |
158 | 160 |
| 161 void StyledLabel::SizeToFit(int max_width) { |
| 162 if (max_width == 0) |
| 163 max_width = std::numeric_limits<int>::max(); |
| 164 |
| 165 SetSize(CalculateAndDoLayout(max_width, true)); |
| 166 } |
| 167 |
| 168 |
159 gfx::Insets StyledLabel::GetInsets() const { | 169 gfx::Insets StyledLabel::GetInsets() const { |
160 gfx::Insets insets = View::GetInsets(); | 170 gfx::Insets insets = View::GetInsets(); |
161 | 171 |
162 // We need a focus border iff we contain a link that will have a focus border. | 172 // We need a focus border iff we contain a link that will have a focus border. |
163 // That in turn will be true only if the link is non-empty. | 173 // That in turn will be true only if the link is non-empty. |
164 for (StyleRanges::const_iterator i(style_ranges_.begin()); | 174 for (StyleRanges::const_iterator i(style_ranges_.begin()); |
165 i != style_ranges_.end(); ++i) { | 175 i != style_ranges_.end(); ++i) { |
166 if (i->style_info.is_link && !i->range.is_empty()) { | 176 if (i->style_info.is_link && !i->range.is_empty()) { |
167 const gfx::Insets focus_border_padding( | 177 const gfx::Insets focus_border_padding( |
168 Label::kFocusBorderPadding, Label::kFocusBorderPadding, | 178 Label::kFocusBorderPadding, Label::kFocusBorderPadding, |
169 Label::kFocusBorderPadding, Label::kFocusBorderPadding); | 179 Label::kFocusBorderPadding, Label::kFocusBorderPadding); |
170 insets += focus_border_padding; | 180 insets += focus_border_padding; |
171 break; | 181 break; |
172 } | 182 } |
173 } | 183 } |
174 | 184 |
175 return insets; | 185 return insets; |
176 } | 186 } |
177 | 187 |
| 188 gfx::Size StyledLabel::GetPreferredSize() const { |
| 189 return calculated_size_; |
| 190 } |
| 191 |
178 int StyledLabel::GetHeightForWidth(int w) const { | 192 int StyledLabel::GetHeightForWidth(int w) const { |
179 // TODO(erg): Munge the const-ness of the style label. CalculateAndDoLayout | 193 // TODO(erg): Munge the const-ness of the style label. CalculateAndDoLayout |
180 // doesn't actually make any changes to member variables when |dry_run| is | 194 // doesn't actually make any changes to member variables when |dry_run| is |
181 // set to true. In general, the mutating and non-mutating parts shouldn't | 195 // set to true. In general, the mutating and non-mutating parts shouldn't |
182 // be in the same codepath. | 196 // be in the same codepath. |
183 calculated_size_ = | 197 return const_cast<StyledLabel*>(this)->CalculateAndDoLayout(w, true).height(); |
184 const_cast<StyledLabel*>(this)->CalculateAndDoLayout(w, true); | |
185 return calculated_size_.height(); | |
186 } | 198 } |
187 | 199 |
188 void StyledLabel::Layout() { | 200 void StyledLabel::Layout() { |
189 calculated_size_ = CalculateAndDoLayout(GetLocalBounds().width(), false); | 201 CalculateAndDoLayout(GetLocalBounds().width(), false); |
190 width_at_last_layout_ = calculated_size_.width(); | |
191 } | 202 } |
192 | 203 |
193 void StyledLabel::PreferredSizeChanged() { | 204 void StyledLabel::PreferredSizeChanged() { |
194 calculated_size_ = gfx::Size(); | 205 calculated_size_ = gfx::Size(); |
| 206 width_at_last_size_calculation_ = 0; |
| 207 width_at_last_layout_ = 0; |
195 View::PreferredSizeChanged(); | 208 View::PreferredSizeChanged(); |
196 } | 209 } |
197 | 210 |
198 void StyledLabel::LinkClicked(Link* source, int event_flags) { | 211 void StyledLabel::LinkClicked(Link* source, int event_flags) { |
199 if (listener_) | 212 if (listener_) |
200 listener_->StyledLabelLinkClicked(link_targets_[source], event_flags); | 213 listener_->StyledLabelLinkClicked(link_targets_[source], event_flags); |
201 } | 214 } |
202 | 215 |
203 gfx::Size StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { | 216 gfx::Size StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { |
| 217 if (!dry_run) |
| 218 width_at_last_layout_ = width; |
| 219 |
| 220 if (width == width_at_last_size_calculation_ && |
| 221 (dry_run || width == width_at_last_layout_)) |
| 222 return calculated_size_; |
| 223 |
204 width -= GetInsets().width(); | 224 width -= GetInsets().width(); |
205 if (width == calculated_size_.width() && | |
206 (dry_run || width_at_last_layout_ == width)) | |
207 return calculated_size_; | |
208 | 225 |
209 if (!dry_run) { | 226 if (!dry_run) { |
210 RemoveAllChildViews(true); | 227 RemoveAllChildViews(true); |
211 link_targets_.clear(); | 228 link_targets_.clear(); |
212 } | 229 } |
213 | 230 |
214 if (width <= 0 || text_.empty()) | 231 if (width <= 0 || text_.empty()) |
215 return gfx::Size(); | 232 return gfx::Size(); |
216 | 233 |
217 const int line_height = specified_line_height_ > 0 ? specified_line_height_ | 234 const int line_height = specified_line_height_ > 0 ? specified_line_height_ |
218 : CalculateLineHeight(font_list_); | 235 : CalculateLineHeight(font_list_); |
219 // The index of the line we're on. | 236 // The index of the line we're on. |
220 int line = 0; | 237 int line = 0; |
221 // The x position (in pixels) of the line we're on, relative to content | 238 // The x position (in pixels) of the line we're on, relative to content |
222 // bounds. | 239 // bounds. |
223 int x = 0; | 240 int x = 0; |
| 241 // The width that was actually used. Guaranteed to be no larger than |width|. |
| 242 int used_width = 0; |
224 | 243 |
225 base::string16 remaining_string = text_; | 244 base::string16 remaining_string = text_; |
226 StyleRanges::const_iterator current_range = style_ranges_.begin(); | 245 StyleRanges::const_iterator current_range = style_ranges_.begin(); |
227 | 246 |
228 // Iterate over the text, creating a bunch of labels and links and laying them | 247 // Iterate over the text, creating a bunch of labels and links and laying them |
229 // out in the appropriate positions. | 248 // out in the appropriate positions. |
230 while (!remaining_string.empty()) { | 249 while (!remaining_string.empty()) { |
231 // Don't put whitespace at beginning of a line with an exception for the | 250 // Don't put whitespace at beginning of a line with an exception for the |
232 // first line (so the text's leading whitespace is respected). | 251 // first line (so the text's leading whitespace is respected). |
233 if (x == 0 && line > 0) { | 252 if (x == 0 && line > 0) { |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
319 const gfx::Size view_size = label->GetPreferredSize(); | 338 const gfx::Size view_size = label->GetPreferredSize(); |
320 if (!dry_run) { | 339 if (!dry_run) { |
321 label->SetBoundsRect(gfx::Rect( | 340 label->SetBoundsRect(gfx::Rect( |
322 gfx::Point(GetInsets().left() + x - focus_border_insets.left(), | 341 gfx::Point(GetInsets().left() + x - focus_border_insets.left(), |
323 GetInsets().top() + line * line_height - | 342 GetInsets().top() + line * line_height - |
324 focus_border_insets.top()), | 343 focus_border_insets.top()), |
325 view_size)); | 344 view_size)); |
326 AddChildView(label.release()); | 345 AddChildView(label.release()); |
327 } | 346 } |
328 x += view_size.width() - focus_border_insets.width(); | 347 x += view_size.width() - focus_border_insets.width(); |
| 348 used_width = std::max(used_width, x); |
329 | 349 |
330 remaining_string = remaining_string.substr(chunk.size()); | 350 remaining_string = remaining_string.substr(chunk.size()); |
331 } | 351 } |
332 | 352 |
| 353 DCHECK_LE(used_width, width); |
333 // The user-specified line height only applies to interline spacing, so the | 354 // The user-specified line height only applies to interline spacing, so the |
334 // final line's height is unaffected. | 355 // final line's height is unaffected. |
335 int total_height = line * line_height + | 356 int total_height = line * line_height + |
336 CalculateLineHeight(font_list_) + GetInsets().height(); | 357 CalculateLineHeight(font_list_) + GetInsets().height(); |
337 return gfx::Size(width, total_height); | 358 calculated_size_ = gfx::Size(used_width + GetInsets().width(), total_height); |
| 359 return calculated_size_; |
338 } | 360 } |
339 | 361 |
340 } // namespace views | 362 } // namespace views |
OLD | NEW |