Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Unified Diff: ui/views/controls/styled_label.cc

Issue 12543032: Add views::RichLabel, a class which creates multi-line text with mixed (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: compile Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/views/controls/styled_label.h ('k') | ui/views/controls/styled_label_listener.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..f875c7f36c68e6d35158e670d08c27ac97ff2cde
--- /dev/null
+++ b/ui/views/controls/styled_label.cc
@@ -0,0 +1,173 @@
+// 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/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 {
+
+namespace {
+
+// Calculates the height of a line of text. Currently returns the height of
+// a label.
+int CalculateLineHeight() {
+ Label label;
+ return label.GetPreferredSize().height();
+}
+
+} // namespace
+
+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)
+ : text_(text),
+ listener_(listener) {}
+
+StyledLabel::~StyledLabel() {}
+
+void StyledLabel::AddLink(const ui::Range& range) {
+ DCHECK(!range.is_reversed());
+ DCHECK(!range.is_empty());
+ DCHECK(ui::Range(0, text_.size()).Contains(range));
+ link_ranges_.push(LinkRange(range));
+ calculated_size_ = gfx::Size();
+ InvalidateLayout();
+}
+
+gfx::Insets StyledLabel::GetInsets() const {
+ gfx::Insets insets = View::GetInsets();
+ const gfx::Insets focus_border_padding(1, 1, 1, 1);
+ insets += focus_border_padding;
+ return insets;
+}
+
+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);
+}
+
+void StyledLabel::LinkClicked(Link* source, int event_flags) {
+ listener_->StyledLabelLinkClicked(link_targets_[source], event_flags);
+}
+
+int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) {
+ if (!dry_run) {
+ RemoveAllChildViews(true);
+ link_targets_.clear();
+ }
+
+ width -= GetInsets().width();
+ if (width <= 0)
+ return 0;
+
+ const int line_height = CalculateLineHeight();
+ // The index of the line we're on.
+ int line = 0;
+ // The x position (in pixels) of the line we're on, relative to content
+ // bounds.
+ 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;
+
+ const gfx::Rect chunk_bounds(x, 0, width - x, 2 * line_height);
+ std::vector<string16> substrings;
+ ui::ElideRectangleText(remaining_string,
+ gfx::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;
+ 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.
+ 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();
+ } 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);
+ }
+
+ // Lay out the views to overlap by 1 pixel to compensate for their border
+ // spacing. Otherwise, "<a>link</a>," will render as "link ,".
+ const int overlap = 1;
+ const gfx::Size view_size = view->GetPreferredSize();
+ DCHECK_EQ(line_height, view_size.height() - 2 * overlap);
+ if (!dry_run) {
+ view->SetBoundsRect(gfx::Rect(
+ gfx::Point(GetInsets().left() + x - overlap,
+ GetInsets().top() + line * line_height - overlap),
+ view_size));
+ AddChildView(view.release());
+ }
+ x += view_size.width() - 2 * overlap;
+
+ remaining_string = remaining_string.substr(chunk.size());
+ }
+
+ return (line + 1) * line_height + GetInsets().height();
+}
+
+} // namespace views
« no previous file with comments | « ui/views/controls/styled_label.h ('k') | ui/views/controls/styled_label_listener.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698