Chromium Code Reviews| Index: ui/views/controls/styled_label.cc |
| diff --git a/ui/views/controls/styled_label.cc b/ui/views/controls/styled_label.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b04be762828fb0d8d81c9d7c808931f32fe98fe9 |
| --- /dev/null |
| +++ b/ui/views/controls/styled_label.cc |
| @@ -0,0 +1,143 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "ui/views/controls/styled_label.h" |
| + |
| +#include <vector> |
| + |
| +#include "base/string_util.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| +#include "ui/base/text/text_elider.h" |
| +#include "ui/views/controls/label.h" |
| +#include "ui/views/controls/link.h" |
| +#include "ui/views/controls/styled_label_listener.h" |
| + |
| +namespace views { |
| + |
| +bool StyledLabel::LinkRange::operator<(const StyledLabel::LinkRange& other) |
| + const { |
| + // Intentionally reversed so the priority queue is sorted by smallest first. |
| + return range.start() > other.range.start(); |
| +} |
| + |
| +StyledLabel::StyledLabel(const string16& text, StyledLabelListener* listener) |
|
msw
2013/03/13 03:22:07
nit: move the 2nd arg to a new line.
sky
2013/03/13 14:33:00
Not necessary. You only need put each arg on its o
|
| + : text_(text), |
| + listener_(listener) {} |
| + |
| +StyledLabel::~StyledLabel() {} |
| + |
| +void StyledLabel::SetLink(const ui::Range& range) { |
| + DCHECK(ui::Range(0, text_.size()).Contains(range)); |
| + link_ranges_.push(LinkRange(range)); |
|
sky
2013/03/13 14:33:00
Doesn't this need to mark the calculated_size_ inv
Evan Stade
2013/03/14 00:30:46
Done.
|
| +} |
| + |
| +int StyledLabel::GetHeightForWidth(int w) { |
| + if (w != calculated_size_.width()) |
| + calculated_size_ = gfx::Size(w, CalculateAndDoLayout(w, true)); |
| + |
| + return calculated_size_.height(); |
| +} |
| + |
| +void StyledLabel::Layout() { |
| + CalculateAndDoLayout(GetLocalBounds().width(), false); |
|
sky
2013/03/13 14:33:00
Honor the border here and in CalculateAndDoLayout.
Evan Stade
2013/03/14 00:30:46
Done.
|
| +} |
| + |
| +int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { |
| + if (width == 0) |
| + return 0; |
| + |
| + if (!dry_run) { |
| + RemoveAllChildViews(true); |
| + link_targets_.clear(); |
| + } |
| + |
| + gfx::Font font = |
| + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); |
| + |
| + int line_height = font.GetHeight(); |
| + // The index of the line we're on. |
| + int line = 0; |
| + // The x position (in pixels) of the line we're on. |
| + int x = 0; |
| + |
| + string16 remaining_string = text_; |
| + std::priority_queue<LinkRange> link_ranges = link_ranges_; |
| + |
| + // Iterate over the text, creating a bunch of labels and links and laying them |
| + // out in the appropriate positions. |
| + while (!remaining_string.empty()) { |
| + // Don't put whitespace at beginning of a line. |
| + if (x == 0) |
| + TrimWhitespace(remaining_string, TRIM_LEADING, &remaining_string); |
| + |
| + ui::Range range(ui::Range::InvalidRange()); |
| + if (!link_ranges.empty()) |
| + range = link_ranges.top().range; |
| + |
| + gfx::Rect chunk_bounds(x, 0, width - x, 2 * line_height); |
| + std::vector<string16> substrings; |
| + ui::ElideRectangleText(remaining_string, |
| + font, |
| + chunk_bounds.width(), |
| + chunk_bounds.height(), |
| + ui::IGNORE_LONG_WORDS, |
| + &substrings); |
| + |
| + string16 chunk = substrings[0]; |
| + if (chunk.empty()) { |
| + // Nothing fit on this line. Start a new line. If x is 0, there's no room |
| + // for anything. Just abort. |
| + if (x == 0) |
| + break; |
| + |
| + x = 0; |
| + line++; |
| + continue; |
| + } |
| + |
| + scoped_ptr<View> view; |
| + size_t position = text_.size() - remaining_string.size(); |
| + if (position >= range.start()) { |
| + // This chunk is a link. |
| + if (chunk.size() < range.length() && x != 0) { |
| + // Don't wrap links. Try to fit them entirely on one line. |
| + x = 0; |
| + line++; |
| + continue; |
| + } |
| + |
| + chunk = chunk.substr(0, range.length()); |
| + Link* link = new Link(chunk); |
| + link->set_listener(this); |
| + if (!dry_run) |
| + link_targets_[link] = range; |
| + view.reset(link); |
|
sky
2013/03/13 14:33:00
Do links have the same preferred height as labels?
Evan Stade
2013/03/14 00:30:46
nope, fixed by adding a focus border (which should
|
| + link_ranges.pop(); |
| + } else { |
| + // This chunk is normal text. |
| + if (position + chunk.size() > range.start()) |
| + chunk = chunk.substr(0, range.start() - position); |
| + |
| + view.reset(new Label(chunk)); |
| + } |
| + |
| + gfx::Size view_size = view->GetPreferredSize(); |
| + if (!dry_run) { |
| + view->SetBoundsRect(gfx::Rect(gfx::Point(x, line * line_height), |
| + view_size)); |
| + AddChildView(view.release()); |
| + } |
| + x += view_size.width(); |
| + |
| + remaining_string = remaining_string.substr(chunk.size()); |
| + } |
| + |
| + return (line + 1) * line_height; |
| +} |
| + |
| +void StyledLabel::LinkClicked(Link* source, int event_flags) { |
| + listener_->StyledLabelLinkClicked(link_targets_[source], event_flags); |
| +} |
| + |
| +} // namespace views |