| 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 <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <limits> | 9 #include <limits> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 125 } | 125 } |
| 126 | 126 |
| 127 void StyledLabel::AddStyleRange(const gfx::Range& range, | 127 void StyledLabel::AddStyleRange(const gfx::Range& range, |
| 128 const RangeStyleInfo& style_info) { | 128 const RangeStyleInfo& style_info) { |
| 129 DCHECK(!range.is_reversed()); | 129 DCHECK(!range.is_reversed()); |
| 130 DCHECK(!range.is_empty()); | 130 DCHECK(!range.is_empty()); |
| 131 DCHECK(gfx::Range(0, text_.size()).Contains(range)); | 131 DCHECK(gfx::Range(0, text_.size()).Contains(range)); |
| 132 | 132 |
| 133 // Insert the new range in sorted order. | 133 // Insert the new range in sorted order. |
| 134 StyleRanges new_range; | 134 StyleRanges new_range; |
| 135 new_range.push_front(StyleRange(range, style_info)); | 135 new_range.push_front(StyleRange(range, style_info, nullptr)); |
| 136 style_ranges_.merge(new_range); | 136 style_ranges_.merge(new_range); |
| 137 | 137 |
| 138 PreferredSizeChanged(); | 138 PreferredSizeChanged(); |
| 139 } |
| 140 |
| 141 void StyledLabel::AddEmbeddedView(uint32_t position, scoped_ptr<View> view) { |
| 142 DCHECK_LE(position, text_.size()); |
| 143 DCHECK(!view->parent()); |
| 144 |
| 145 // Set the view as owned by the StyledLabel so the Views hierarchy doesn't |
| 146 // clean it up. |
| 147 view->set_owned_by_client(); |
| 148 |
| 149 // Insert the new range in sorted order. |
| 150 StyleRanges new_range; |
| 151 embedded_views_.push_back(std::move(view)); |
| 152 new_range.push_front(StyleRange(gfx::Range(position, position), |
| 153 RangeStyleInfo(), |
| 154 embedded_views_.back().get())); |
| 155 style_ranges_.merge(new_range); |
| 156 |
| 157 PreferredSizeChanged(); |
| 139 } | 158 } |
| 140 | 159 |
| 141 void StyledLabel::SetDefaultStyle(const RangeStyleInfo& style_info) { | 160 void StyledLabel::SetDefaultStyle(const RangeStyleInfo& style_info) { |
| 142 default_style_info_ = style_info; | 161 default_style_info_ = style_info; |
| 143 PreferredSizeChanged(); | 162 PreferredSizeChanged(); |
| 144 } | 163 } |
| 145 | 164 |
| 146 void StyledLabel::SetLineHeight(int line_height) { | 165 void StyledLabel::SetLineHeight(int line_height) { |
| 147 specified_line_height_ = line_height; | 166 specified_line_height_ = line_height; |
| 148 PreferredSizeChanged(); | 167 PreferredSizeChanged(); |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 width -= GetInsets().width(); | 252 width -= GetInsets().width(); |
| 234 | 253 |
| 235 if (!dry_run) { | 254 if (!dry_run) { |
| 236 RemoveAllChildViews(true); | 255 RemoveAllChildViews(true); |
| 237 link_targets_.clear(); | 256 link_targets_.clear(); |
| 238 } | 257 } |
| 239 | 258 |
| 240 if (width <= 0 || text_.empty()) | 259 if (width <= 0 || text_.empty()) |
| 241 return gfx::Size(); | 260 return gfx::Size(); |
| 242 | 261 |
| 243 const int line_height = specified_line_height_ > 0 ? specified_line_height_ | 262 const int usual_line_height = specified_line_height_ > 0 |
| 244 : CalculateLineHeight(font_list_); | 263 ? specified_line_height_ |
| 264 : CalculateLineHeight(font_list_); |
| 245 // The index of the line we're on. | 265 // The index of the line we're on. |
| 246 int line = 0; | 266 int line = 0; |
| 247 // The x position (in pixels) of the line we're on, relative to content | 267 // The x, y position (in pixels) of the line we're on, relative to content |
| 248 // bounds. | 268 // bounds. |
| 249 int x = 0; | 269 int x = 0, y = 0; |
| 250 // The width that was actually used. Guaranteed to be no larger than |width|. | 270 // The width that was actually used. Guaranteed to be no larger than |width|. |
| 251 int used_width = 0; | 271 int used_width = 0; |
| 272 // The height of the actual current line. Guaranteed to be no smaller than |
| 273 // |usual_line_height|. |
| 274 int line_height = usual_line_height; |
| 252 | 275 |
| 253 base::string16 remaining_string = text_; | 276 base::string16 remaining_string = text_; |
| 254 StyleRanges::const_iterator current_range = style_ranges_.begin(); | 277 StyleRanges::const_iterator current_range = style_ranges_.begin(); |
| 255 | 278 |
| 256 // Iterate over the text, creating a bunch of labels and links and laying them | 279 // Iterate over the text, creating a bunch of labels and links and laying them |
| 257 // out in the appropriate positions. | 280 // out in the appropriate positions. |
| 258 while (!remaining_string.empty()) { | 281 while (!remaining_string.empty()) { |
| 259 // Don't put whitespace at beginning of a line with an exception for the | 282 // Don't put whitespace at beginning of a line with an exception for the |
| 260 // first line (so the text's leading whitespace is respected). | 283 // first line (so the text's leading whitespace is respected). |
| 261 if (x == 0 && line > 0) { | 284 if (x == 0 && line > 0) { |
| 262 base::TrimWhitespace(remaining_string, base::TRIM_LEADING, | 285 base::TrimWhitespace(remaining_string, base::TRIM_LEADING, |
| 263 &remaining_string); | 286 &remaining_string); |
| 264 } | 287 } |
| 265 | 288 |
| 266 gfx::Range range(gfx::Range::InvalidRange()); | 289 gfx::Range range(gfx::Range::InvalidRange()); |
| 267 if (current_range != style_ranges_.end()) | 290 if (current_range != style_ranges_.end()) |
| 268 range = current_range->range; | 291 range = current_range->range; |
| 269 | 292 |
| 270 const size_t position = text_.size() - remaining_string.size(); | 293 const size_t position = text_.size() - remaining_string.size(); |
| 271 | 294 |
| 272 const gfx::Rect chunk_bounds(x, 0, width - x, 2 * line_height); | 295 const gfx::Rect chunk_bounds(x, 0, width - x, 2 * usual_line_height); |
| 273 std::vector<base::string16> substrings; | 296 std::vector<base::string16> substrings; |
| 274 gfx::FontList text_font_list = font_list_; | 297 gfx::FontList text_font_list = font_list_; |
| 275 // If the start of the remaining text is inside a styled range, the font | 298 // If the start of the remaining text is inside a styled range, the font |
| 276 // style may differ from the base font. The font specified by the range | 299 // style may differ from the base font. The font specified by the range |
| 277 // should be used when eliding text. | 300 // should be used when eliding text. |
| 278 if (position >= range.start()) { | 301 if (position >= range.start()) { |
| 279 text_font_list = text_font_list.DeriveWithStyle( | 302 text_font_list = text_font_list.DeriveWithStyle( |
| 280 current_range->style_info.font_style); | 303 current_range->style_info.font_style); |
| 281 } | 304 } |
| 282 gfx::ElideRectangleText(remaining_string, | 305 gfx::ElideRectangleText(remaining_string, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 294 if (x == 0) { | 317 if (x == 0) { |
| 295 if (line == 0) { | 318 if (line == 0) { |
| 296 base::TrimWhitespace(remaining_string, base::TRIM_LEADING, | 319 base::TrimWhitespace(remaining_string, base::TRIM_LEADING, |
| 297 &remaining_string); | 320 &remaining_string); |
| 298 continue; | 321 continue; |
| 299 } | 322 } |
| 300 break; | 323 break; |
| 301 } | 324 } |
| 302 | 325 |
| 303 x = 0; | 326 x = 0; |
| 327 y += line_height; |
| 328 line_height = usual_line_height; |
| 304 line++; | 329 line++; |
| 305 continue; | 330 continue; |
| 306 } | 331 } |
| 307 | 332 |
| 308 base::string16 chunk = substrings[0]; | 333 base::string16 chunk = substrings[0]; |
| 309 | 334 |
| 335 View* view = current_range->embedded_view; |
| 310 scoped_ptr<Label> label; | 336 scoped_ptr<Label> label; |
| 311 if (position >= range.start()) { | 337 if (position >= range.start()) { |
| 338 if (view) |
| 339 line_height = std::max(line_height, view->GetPreferredSize().height()); |
| 340 |
| 312 const RangeStyleInfo& style_info = current_range->style_info; | 341 const RangeStyleInfo& style_info = current_range->style_info; |
| 313 | 342 |
| 314 if (style_info.disable_line_wrapping && chunk.size() < range.length() && | 343 if (style_info.disable_line_wrapping && chunk.size() < range.length() && |
| 315 position == range.start() && x != 0) { | 344 position == range.start() && x != 0) { |
| 316 // If the chunk should not be wrapped, try to fit it entirely on the | 345 // If the chunk should not be wrapped, try to fit it entirely on the |
| 317 // next line. | 346 // next line. |
| 318 x = 0; | 347 x = 0; |
| 348 y += line_height; |
| 349 line_height = usual_line_height; |
| 319 line++; | 350 line++; |
| 320 continue; | 351 continue; |
| 321 } | 352 } |
| 322 | 353 |
| 323 if (chunk.size() > range.end() - position) | 354 if (chunk.size() > range.end() - position) |
| 324 chunk = chunk.substr(0, range.end() - position); | 355 chunk = chunk.substr(0, range.end() - position); |
| 325 | 356 |
| 326 label = CreateLabelRange(chunk, font_list_, style_info, this); | 357 if (!view) { |
| 358 label = CreateLabelRange(chunk, font_list_, style_info, this); |
| 327 | 359 |
| 328 if (style_info.is_link && !dry_run) | 360 if (style_info.is_link && !dry_run) |
| 329 link_targets_[label.get()] = range; | 361 link_targets_[label.get()] = range; |
| 362 } |
| 330 | 363 |
| 331 if (position + chunk.size() >= range.end()) | 364 if (position + chunk.size() >= range.end()) |
| 332 ++current_range; | 365 ++current_range; |
| 333 } else { | 366 } else { |
| 334 // This chunk is normal text. | 367 // This chunk is normal text. |
| 335 if (position + chunk.size() > range.start()) | 368 if (position + chunk.size() > range.start()) |
| 336 chunk = chunk.substr(0, range.start() - position); | 369 chunk = chunk.substr(0, range.start() - position); |
| 337 label = CreateLabelRange(chunk, font_list_, default_style_info_, this); | 370 label = CreateLabelRange(chunk, font_list_, default_style_info_, this); |
| 338 } | 371 } |
| 339 | 372 |
| 340 if (displayed_on_background_color_set_) | 373 if (label) { |
| 341 label->SetBackgroundColor(displayed_on_background_color_); | 374 if (displayed_on_background_color_set_) |
| 342 label->SetAutoColorReadabilityEnabled(auto_color_readability_enabled_); | 375 label->SetBackgroundColor(displayed_on_background_color_); |
| 376 label->SetAutoColorReadabilityEnabled(auto_color_readability_enabled_); |
| 377 |
| 378 // |view| temporarily owns the pointer to the Label. This will be owned by |
| 379 // the views hierarchy. |
| 380 view = label.release(); |
| 381 } |
| 343 | 382 |
| 344 // Calculate the size of the optional focus border, and overlap by that | 383 // Calculate the size of the optional focus border, and overlap by that |
| 345 // amount. Otherwise, "<a>link</a>," will render as "link ,". | 384 // amount. Otherwise, "<a>link</a>," will render as "link ,". |
| 346 gfx::Insets focus_border_insets(label->GetInsets()); | 385 gfx::Insets focus_border_insets(view->GetInsets()); |
| 347 focus_border_insets += -label->View::GetInsets(); | 386 focus_border_insets += -view->View::GetInsets(); |
| 348 const gfx::Size view_size = label->GetPreferredSize(); | 387 const gfx::Size view_size = view->GetPreferredSize(); |
| 349 if (!dry_run) { | 388 if (!dry_run) { |
| 350 label->SetBoundsRect(gfx::Rect( | 389 view->SetBoundsRect(gfx::Rect( |
| 351 gfx::Point(GetInsets().left() + x - focus_border_insets.left(), | 390 gfx::Point(GetInsets().left() + x - focus_border_insets.left(), |
| 352 GetInsets().top() + line * line_height - | 391 GetInsets().top() + y - focus_border_insets.top()), |
| 353 focus_border_insets.top()), | |
| 354 view_size)); | 392 view_size)); |
| 355 AddChildView(label.release()); | 393 |
| 394 // Ownership: If |view| came from a Label created above, it will be owned |
| 395 // by the views hierarchy from now on. If |view| came from an externally |
| 396 // supplied embedded view, it will continue to be owned by the |
| 397 // |embedded_views_| vector. |
| 398 AddChildView(view); |
| 356 } | 399 } |
| 357 x += view_size.width() - focus_border_insets.width(); | 400 x += view_size.width() - focus_border_insets.width(); |
| 358 used_width = std::max(used_width, x); | 401 used_width = std::max(used_width, x); |
| 359 | 402 |
| 360 // If |gfx::ElideRectangleText| returned more than one substring, that | 403 // If |gfx::ElideRectangleText| returned more than one substring, that |
| 361 // means the whole text did not fit into remaining line width, with text | 404 // means the whole text did not fit into remaining line width, with text |
| 362 // after |susbtring[0]| spilling into next line. If whole |substring[0]| | 405 // after |susbtring[0]| spilling into next line. If whole |substring[0]| |
| 363 // was added to the current line (this may not be the case if part of the | 406 // was added to the current line (this may not be the case if part of the |
| 364 // substring has different style), proceed to the next line. | 407 // substring has different style), proceed to the next line. |
| 365 if (substrings.size() > 1 && chunk.size() == substrings[0].size()) { | 408 if (substrings.size() > 1 && chunk.size() == substrings[0].size()) { |
| 366 x = 0; | 409 x = 0; |
| 410 y += line_height; |
| 411 line_height = usual_line_height; |
| 367 ++line; | 412 ++line; |
| 368 } | 413 } |
| 369 | 414 |
| 370 remaining_string = remaining_string.substr(chunk.size()); | 415 remaining_string = remaining_string.substr(chunk.size()); |
| 371 } | 416 } |
| 372 | 417 |
| 373 DCHECK_LE(used_width, width); | 418 DCHECK_LE(used_width, width); |
| 374 // The user-specified line height only applies to interline spacing, so the | 419 // The user-specified line height only applies to interline spacing, so the |
| 375 // final line's height is unaffected. | 420 // final line's height is unaffected. |
| 376 int total_height = line * line_height + | 421 // TODO(mgiuca): I think this makes a bad assumption about the final line. |
| 377 CalculateLineHeight(font_list_) + GetInsets().height(); | 422 int total_height = y + CalculateLineHeight(font_list_) + GetInsets().height(); |
| 378 calculated_size_ = gfx::Size(used_width + GetInsets().width(), total_height); | 423 calculated_size_ = gfx::Size(used_width + GetInsets().width(), total_height); |
| 379 return calculated_size_; | 424 return calculated_size_; |
| 380 } | 425 } |
| 381 | 426 |
| 382 } // namespace views | 427 } // namespace views |
| OLD | NEW |