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 |
| index f875c7f36c68e6d35158e670d08c27ac97ff2cde..d29d76e17dfb422d051ec38696bb96a785048123 100644 |
| --- a/ui/views/controls/styled_label.cc |
| +++ b/ui/views/controls/styled_label.cc |
| @@ -23,25 +23,74 @@ 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); |
| + 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::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(); |
| InvalidateLayout(); |
| } |
| @@ -86,34 +135,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) |
|
Evan Stade
2013/03/29 19:35:48
I don't think you need to make this change. |posit
tbarzic
2013/03/29 20:00:57
yeah, but this way we actually respect leading whi
Evan Stade
2013/03/29 23:14:58
ok, lgtm
|
| 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++; |
| @@ -121,33 +189,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 |