| Index: ui/views/controls/styled_label.cc
|
| diff --git a/ui/views/controls/styled_label.cc b/ui/views/controls/styled_label.cc
|
| index d0fda3de315db4386b7c033f6a440112ae119226..b0e46a45ec3016f85b3f58f6f6358b8129c27027 100644
|
| --- a/ui/views/controls/styled_label.cc
|
| +++ b/ui/views/controls/styled_label.cc
|
| @@ -23,33 +23,82 @@ int CalculateLineHeight() {
|
| return label.GetPreferredSize().height();
|
| }
|
|
|
| +scoped_ptr<View> CreateLabelRange(const string16& text,
|
| + const StyledLabel::RangeStyleInfo& style_info,
|
| + views::LinkListener* link_listener) {
|
| + scoped_ptr<Label> result;
|
| +
|
| + if (style_info.is_link) {
|
| + Link* link = new Link(text);
|
| + link->set_listener(link_listener);
|
| + link->SetUnderline((style_info.font_style & gfx::Font::UNDERLINE) != 0);
|
| + result.reset(link);
|
| + } else {
|
| + Label* label = new Label(text);
|
| + // Give the label a focus border so that its preferred size matches
|
| + // links' preferred sizes
|
| + label->SetHasFocusBorder(true);
|
| +
|
| + result.reset(label);
|
| + }
|
| +
|
| + if (!style_info.tooltip.empty())
|
| + result->SetTooltipText(style_info.tooltip);
|
| + if (style_info.font_style != gfx::Font::NORMAL)
|
| + result->SetFont(result->font().DeriveFont(0, style_info.font_style));
|
| +
|
| + return scoped_ptr<View>(result.release());
|
| +}
|
| +
|
| } // namespace
|
|
|
| -bool StyledLabel::LinkRange::operator<(
|
| - const StyledLabel::LinkRange& other) const {
|
| +
|
| +StyledLabel::RangeStyleInfo::RangeStyleInfo()
|
| + : font_style(gfx::Font::NORMAL),
|
| + disable_line_wrapping(false),
|
| + is_link(false) {
|
| +}
|
| +
|
| +StyledLabel::RangeStyleInfo::~RangeStyleInfo() {}
|
| +
|
| +// static
|
| +StyledLabel::RangeStyleInfo StyledLabel::RangeStyleInfo::CreateForLink() {
|
| + RangeStyleInfo result;
|
| + result.disable_line_wrapping = true;
|
| + result.is_link = true;
|
| + result.font_style = gfx::Font::UNDERLINE;
|
| + return result;
|
| +}
|
| +
|
| +bool StyledLabel::StyleRange::operator<(
|
| + const StyledLabel::StyleRange& 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)
|
| - : text_(text),
|
| - listener_(listener) {}
|
| + : listener_(listener) {
|
| + TrimWhitespace(text, TRIM_TRAILING, &text_);
|
| +}
|
|
|
| StyledLabel::~StyledLabel() {}
|
|
|
| void StyledLabel::SetText(const string16& text) {
|
| text_ = text;
|
| calculated_size_ = gfx::Size();
|
| - link_ranges_ = std::priority_queue<LinkRange>();
|
| + style_ranges_ = std::priority_queue<StyleRange>();
|
| RemoveAllChildViews(true);
|
| PreferredSizeChanged();
|
| }
|
|
|
| -void StyledLabel::AddLink(const ui::Range& range) {
|
| +void StyledLabel::AddStyleRange(const ui::Range& range,
|
| + const RangeStyleInfo& style_info) {
|
| DCHECK(!range.is_reversed());
|
| DCHECK(!range.is_empty());
|
| DCHECK(ui::Range(0, text_.size()).Contains(range));
|
| - link_ranges_.push(LinkRange(range));
|
| +
|
| + style_ranges_.push(StyleRange(range, style_info));
|
| +
|
| calculated_size_ = gfx::Size();
|
| PreferredSizeChanged();
|
| }
|
| @@ -94,34 +143,53 @@ int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) {
|
| int x = 0;
|
|
|
| string16 remaining_string = text_;
|
| - std::priority_queue<LinkRange> link_ranges = link_ranges_;
|
| + std::priority_queue<StyleRange> style_ranges = style_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)
|
| + // Don't put whitespace at beginning of a line with an exception for the
|
| + // first line (so the text's leading whitespace is respected).
|
| + if (x == 0 && line > 0)
|
| TrimWhitespace(remaining_string, TRIM_LEADING, &remaining_string);
|
|
|
| ui::Range range(ui::Range::InvalidRange());
|
| - if (!link_ranges.empty())
|
| - range = link_ranges.top().range;
|
| + if (!style_ranges.empty())
|
| + range = style_ranges.top().range;
|
| +
|
| + const size_t position = text_.size() - remaining_string.size();
|
|
|
| const gfx::Rect chunk_bounds(x, 0, width - x, 2 * line_height);
|
| std::vector<string16> substrings;
|
| + gfx::Font text_font;
|
| + // If the start of the remaining text is inside a styled range, the font
|
| + // style may differ from the base font. The font specified by the range
|
| + // should be used when eliding text.
|
| + if (position >= range.start()) {
|
| + text_font =
|
| + text_font.DeriveFont(0, style_ranges.top().style_info.font_style);
|
| + }
|
| ui::ElideRectangleText(remaining_string,
|
| - gfx::Font(),
|
| + text_font,
|
| chunk_bounds.width(),
|
| chunk_bounds.height(),
|
| ui::IGNORE_LONG_WORDS,
|
| &substrings);
|
|
|
| + DCHECK(!substrings.empty());
|
| 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)
|
| + // Nothing fits on this line. Start a new line.
|
| + // If x is 0, first line may have leading whitespace that doesn't fit in a
|
| + // single line, so try trimming those. Otherwise there is no room for
|
| + // anything; abort.
|
| + if (x == 0) {
|
| + if (line == 0) {
|
| + TrimWhitespace(remaining_string, TRIM_LEADING, &remaining_string);
|
| + continue;
|
| + }
|
| break;
|
| + }
|
|
|
| x = 0;
|
| line++;
|
| @@ -129,33 +197,32 @@ int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) {
|
| }
|
|
|
| scoped_ptr<View> view;
|
| - const 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.
|
| + const RangeStyleInfo& style_info = style_ranges.top().style_info;
|
| +
|
| + if (style_info.disable_line_wrapping && chunk.size() < range.length() &&
|
| + position == range.start() && x != 0) {
|
| + // If the chunk should not be wrapped, try to fit it entirely on the
|
| + // next 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);
|
| - link_ranges.pop();
|
| + chunk = chunk.substr(0, std::min(chunk.size(), range.end() - position));
|
| +
|
| + view = CreateLabelRange(chunk, style_info, this);
|
| +
|
| + if (style_info.is_link && !dry_run)
|
| + link_targets_[view.get()] = range;
|
| +
|
| + if (position + chunk.size() >= range.end())
|
| + style_ranges.pop();
|
| } else {
|
| // This chunk is normal text.
|
| if (position + chunk.size() > range.start())
|
| chunk = chunk.substr(0, range.start() - position);
|
| -
|
| - Label* label = new Label(chunk);
|
| - // Give the label a focus border so that its preferred size matches
|
| - // links' preferred sizes.
|
| - label->SetHasFocusBorder(true);
|
| - view.reset(label);
|
| + view = CreateLabelRange(chunk, RangeStyleInfo(), this);
|
| }
|
|
|
| // Lay out the views to overlap by 1 pixel to compensate for their border
|
|
|