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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "ui/views/controls/styled_label.h"
6
7 #include <vector>
8
9 #include "base/string_util.h"
10 #include "ui/base/text/text_elider.h"
11 #include "ui/views/controls/label.h"
12 #include "ui/views/controls/link.h"
13 #include "ui/views/controls/styled_label_listener.h"
14
15 namespace views {
16
17 namespace {
18
19 // Calculates the height of a line of text. Currently returns the height of
20 // a label.
21 int CalculateLineHeight() {
22 Label label;
23 return label.GetPreferredSize().height();
24 }
25
26 } // namespace
27
28 bool StyledLabel::LinkRange::operator<(
29 const StyledLabel::LinkRange& other) const {
30 // Intentionally reversed so the priority queue is sorted by smallest first.
31 return range.start() > other.range.start();
32 }
33
34 StyledLabel::StyledLabel(const string16& text, StyledLabelListener* listener)
35 : text_(text),
36 listener_(listener) {}
37
38 StyledLabel::~StyledLabel() {}
39
40 void StyledLabel::AddLink(const ui::Range& range) {
41 DCHECK(!range.is_reversed());
42 DCHECK(!range.is_empty());
43 DCHECK(ui::Range(0, text_.size()).Contains(range));
44 link_ranges_.push(LinkRange(range));
45 calculated_size_ = gfx::Size();
46 InvalidateLayout();
47 }
48
49 gfx::Insets StyledLabel::GetInsets() const {
50 gfx::Insets insets = View::GetInsets();
51 const gfx::Insets focus_border_padding(1, 1, 1, 1);
52 insets += focus_border_padding;
53 return insets;
54 }
55
56 int StyledLabel::GetHeightForWidth(int w) {
57 if (w != calculated_size_.width())
58 calculated_size_ = gfx::Size(w, CalculateAndDoLayout(w, true));
59
60 return calculated_size_.height();
61 }
62
63 void StyledLabel::Layout() {
64 CalculateAndDoLayout(GetLocalBounds().width(), false);
65 }
66
67 void StyledLabel::LinkClicked(Link* source, int event_flags) {
68 listener_->StyledLabelLinkClicked(link_targets_[source], event_flags);
69 }
70
71 int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) {
72 if (!dry_run) {
73 RemoveAllChildViews(true);
74 link_targets_.clear();
75 }
76
77 width -= GetInsets().width();
78 if (width <= 0)
79 return 0;
80
81 const int line_height = CalculateLineHeight();
82 // The index of the line we're on.
83 int line = 0;
84 // The x position (in pixels) of the line we're on, relative to content
85 // bounds.
86 int x = 0;
87
88 string16 remaining_string = text_;
89 std::priority_queue<LinkRange> link_ranges = link_ranges_;
90
91 // Iterate over the text, creating a bunch of labels and links and laying them
92 // out in the appropriate positions.
93 while (!remaining_string.empty()) {
94 // Don't put whitespace at beginning of a line.
95 if (x == 0)
96 TrimWhitespace(remaining_string, TRIM_LEADING, &remaining_string);
97
98 ui::Range range(ui::Range::InvalidRange());
99 if (!link_ranges.empty())
100 range = link_ranges.top().range;
101
102 const gfx::Rect chunk_bounds(x, 0, width - x, 2 * line_height);
103 std::vector<string16> substrings;
104 ui::ElideRectangleText(remaining_string,
105 gfx::Font(),
106 chunk_bounds.width(),
107 chunk_bounds.height(),
108 ui::IGNORE_LONG_WORDS,
109 &substrings);
110
111 string16 chunk = substrings[0];
112 if (chunk.empty()) {
113 // Nothing fit on this line. Start a new line. If x is 0, there's no room
114 // for anything. Just abort.
115 if (x == 0)
116 break;
117
118 x = 0;
119 line++;
120 continue;
121 }
122
123 scoped_ptr<View> view;
124 const size_t position = text_.size() - remaining_string.size();
125 if (position >= range.start()) {
126 // This chunk is a link.
127 if (chunk.size() < range.length() && x != 0) {
128 // Don't wrap links. Try to fit them entirely on one line.
129 x = 0;
130 line++;
131 continue;
132 }
133
134 chunk = chunk.substr(0, range.length());
135 Link* link = new Link(chunk);
136 link->set_listener(this);
137 if (!dry_run)
138 link_targets_[link] = range;
139 view.reset(link);
140 link_ranges.pop();
141 } else {
142 // This chunk is normal text.
143 if (position + chunk.size() > range.start())
144 chunk = chunk.substr(0, range.start() - position);
145
146 Label* label = new Label(chunk);
147 // Give the label a focus border so that its preferred size matches
148 // links' preferred sizes.
149 label->SetHasFocusBorder(true);
150 view.reset(label);
151 }
152
153 // Lay out the views to overlap by 1 pixel to compensate for their border
154 // spacing. Otherwise, "<a>link</a>," will render as "link ,".
155 const int overlap = 1;
156 const gfx::Size view_size = view->GetPreferredSize();
157 DCHECK_EQ(line_height, view_size.height() - 2 * overlap);
158 if (!dry_run) {
159 view->SetBoundsRect(gfx::Rect(
160 gfx::Point(GetInsets().left() + x - overlap,
161 GetInsets().top() + line * line_height - overlap),
162 view_size));
163 AddChildView(view.release());
164 }
165 x += view_size.width() - 2 * overlap;
166
167 remaining_string = remaining_string.substr(chunk.size());
168 }
169
170 return (line + 1) * line_height + GetInsets().height();
171 }
172
173 } // namespace views
OLDNEW
« 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