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

Side by Side Diff: ui/views/controls/label.cc

Issue 413823002: Cache gfx::RenderText instances in views::Label. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 5 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/label.h ('k') | ui/views/controls/label_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/controls/label.h" 5 #include "ui/views/controls/label.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 #include <limits> 9 #include <limits>
10 #include <vector> 10 #include <vector>
11 11
12 #include "base/i18n/rtl.h" 12 #include "base/i18n/rtl.h"
13 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/strings/string_split.h" 14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
17 #include "ui/accessibility/ax_view_state.h" 16 #include "ui/accessibility/ax_view_state.h"
18 #include "ui/base/resource/resource_bundle.h" 17 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/gfx/canvas.h" 18 #include "ui/gfx/canvas.h"
20 #include "ui/gfx/color_utils.h" 19 #include "ui/gfx/color_utils.h"
21 #include "ui/gfx/insets.h"
22 #include "ui/gfx/text_elider.h" 20 #include "ui/gfx/text_elider.h"
23 #include "ui/gfx/text_utils.h"
24 #include "ui/gfx/utf16_indexing.h"
25 #include "ui/native_theme/native_theme.h" 21 #include "ui/native_theme/native_theme.h"
26 #include "ui/views/background.h"
27
28 namespace {
29
30 const int kCachedSizeLimit = 10;
31 const base::char16 kPasswordReplacementChar = '*';
32
33 } // namespace
34 22
35 namespace views { 23 namespace views {
36 24
37 // static 25 // static
38 const char Label::kViewClassName[] = "Label"; 26 const char Label::kViewClassName[] = "Label";
39 const int Label::kFocusBorderPadding = 1; 27 const int Label::kFocusBorderPadding = 1;
40 28
41 Label::Label() { 29 Label::Label() {
42 Init(base::string16(), gfx::FontList()); 30 Init(base::string16(), gfx::FontList());
43 } 31 }
44 32
45 Label::Label(const base::string16& text) { 33 Label::Label(const base::string16& text) {
46 Init(text, gfx::FontList()); 34 Init(text, gfx::FontList());
47 } 35 }
48 36
49 Label::Label(const base::string16& text, const gfx::FontList& font_list) { 37 Label::Label(const base::string16& text, const gfx::FontList& font_list) {
50 Init(text, font_list); 38 Init(text, font_list);
51 } 39 }
52 40
53 Label::~Label() { 41 Label::~Label() {
54 } 42 }
55 43
56 void Label::SetFontList(const gfx::FontList& font_list) { 44 void Label::SetFontList(const gfx::FontList& font_list) {
57 font_list_ = font_list; 45 render_text_->SetFontList(font_list);
58 ResetCachedSize(); 46 ResetLayout();
59 PreferredSizeChanged();
60 SchedulePaint();
61 } 47 }
62 48
63 void Label::SetText(const base::string16& text) { 49 void Label::SetText(const base::string16& text) {
64 if (text != text_) 50 render_text_->SetText(text);
65 SetTextInternal(text); 51 ResetLayout();
66 }
67
68 void Label::SetTextInternal(const base::string16& text) {
69 text_ = text;
70
71 if (obscured_) {
72 size_t obscured_text_length =
73 static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, text_.length()));
74 layout_text_.assign(obscured_text_length, kPasswordReplacementChar);
75 } else {
76 layout_text_ = text_;
77 }
78
79 ResetCachedSize();
80 PreferredSizeChanged();
81 SchedulePaint();
82 } 52 }
83 53
84 void Label::SetAutoColorReadabilityEnabled(bool enabled) { 54 void Label::SetAutoColorReadabilityEnabled(bool enabled) {
85 auto_color_readability_ = enabled; 55 auto_color_readability_ = enabled;
86 RecalculateColors(); 56 RecalculateColors();
87 } 57 }
88 58
89 void Label::SetEnabledColor(SkColor color) { 59 void Label::SetEnabledColor(SkColor color) {
90 requested_enabled_color_ = color; 60 requested_enabled_color_ = color;
91 enabled_color_set_ = true; 61 enabled_color_set_ = true;
92 RecalculateColors(); 62 RecalculateColors();
93 } 63 }
94 64
95 void Label::SetDisabledColor(SkColor color) { 65 void Label::SetDisabledColor(SkColor color) {
96 requested_disabled_color_ = color; 66 requested_disabled_color_ = color;
97 disabled_color_set_ = true; 67 disabled_color_set_ = true;
98 RecalculateColors(); 68 RecalculateColors();
99 } 69 }
100 70
101 void Label::SetBackgroundColor(SkColor color) { 71 void Label::SetBackgroundColor(SkColor color) {
102 background_color_ = color; 72 background_color_ = color;
103 background_color_set_ = true; 73 background_color_set_ = true;
104 RecalculateColors(); 74 RecalculateColors();
105 } 75 }
106 76
107 void Label::SetShadows(const gfx::ShadowValues& shadows) { 77 void Label::SetShadows(const gfx::ShadowValues& shadows) {
108 shadows_ = shadows; 78 render_text_->set_shadows(shadows);
109 text_size_valid_ = false; 79 ResetLayout();
110 } 80 }
111 81
112 void Label::SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled) { 82 void Label::SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled) {
113 subpixel_rendering_enabled_ = subpixel_rendering_enabled; 83 subpixel_rendering_enabled_ = subpixel_rendering_enabled;
84 RecalculateColors();
114 } 85 }
115 86
116 void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { 87 void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
117 // If the UI layout is right-to-left, flip the alignment direction. 88 // If the UI layout is right-to-left, flip the alignment direction.
118 if (base::i18n::IsRTL() && 89 if (base::i18n::IsRTL() &&
119 (alignment == gfx::ALIGN_LEFT || alignment == gfx::ALIGN_RIGHT)) { 90 (alignment == gfx::ALIGN_LEFT || alignment == gfx::ALIGN_RIGHT)) {
120 alignment = (alignment == gfx::ALIGN_LEFT) ? 91 alignment = (alignment == gfx::ALIGN_LEFT) ?
121 gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; 92 gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT;
122 } 93 }
123 if (horizontal_alignment_ != alignment) { 94 render_text_->SetHorizontalAlignment(alignment);
124 horizontal_alignment_ = alignment; 95 ResetLayout();
125 SchedulePaint();
126 }
127 }
128
129 gfx::HorizontalAlignment Label::GetHorizontalAlignment() const {
130 if (horizontal_alignment_ != gfx::ALIGN_TO_HEAD)
131 return horizontal_alignment_;
132
133 const base::i18n::TextDirection dir =
134 base::i18n::GetFirstStrongCharacterDirection(layout_text_);
135 return dir == base::i18n::RIGHT_TO_LEFT ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT;
136 } 96 }
137 97
138 void Label::SetLineHeight(int height) { 98 void Label::SetLineHeight(int height) {
139 if (height != line_height_) { 99 if (line_height_ == height)
140 line_height_ = height; 100 return;
141 ResetCachedSize(); 101
142 PreferredSizeChanged(); 102 line_height_ = height;
143 SchedulePaint(); 103 ResetLayout();
144 }
145 } 104 }
146 105
147 void Label::SetMultiLine(bool multi_line) { 106 void Label::SetMultiLine(bool multi_line) {
148 DCHECK(!multi_line || (elide_behavior_ == gfx::ELIDE_TAIL || 107 if (multi_line_ == multi_line)
149 elide_behavior_ == gfx::NO_ELIDE)); 108 return;
150 if (multi_line != multi_line_) { 109
151 multi_line_ = multi_line; 110 multi_line_ = multi_line;
152 ResetCachedSize(); 111 render_text_->SetReplaceNewlineCharsWithSymbols(!multi_line);
153 PreferredSizeChanged(); 112 ResetLayout();
154 SchedulePaint();
155 }
156 } 113 }
157 114
158 void Label::SetObscured(bool obscured) { 115 void Label::SetObscured(bool obscured) {
159 if (obscured != obscured_) { 116 render_text_->SetObscured(obscured);
160 obscured_ = obscured; 117 ResetLayout();
161 SetTextInternal(text_);
162 }
163 } 118 }
164 119
165 void Label::SetAllowCharacterBreak(bool allow_character_break) { 120 void Label::SetAllowCharacterBreak(bool allow_character_break) {
166 if (allow_character_break != allow_character_break_) { 121 if (allow_character_break_ == allow_character_break)
167 allow_character_break_ = allow_character_break; 122 return;
168 ResetCachedSize(); 123
169 PreferredSizeChanged(); 124 allow_character_break_ = allow_character_break;
170 SchedulePaint(); 125 ResetLayout();
171 }
172 } 126 }
173 127
174 void Label::SetElideBehavior(gfx::ElideBehavior elide_behavior) { 128 void Label::SetElideBehavior(gfx::ElideBehavior elide_behavior) {
175 DCHECK(!multi_line_ || (elide_behavior_ == gfx::ELIDE_TAIL || 129 if (elide_behavior_ == elide_behavior)
176 elide_behavior_ == gfx::NO_ELIDE)); 130 return;
177 if (elide_behavior != elide_behavior_) { 131
178 elide_behavior_ = elide_behavior; 132 elide_behavior_ = elide_behavior;
179 ResetCachedSize(); 133 ResetLayout();
180 PreferredSizeChanged();
181 SchedulePaint();
182 }
183 } 134 }
184 135
185 void Label::SetTooltipText(const base::string16& tooltip_text) { 136 void Label::SetTooltipText(const base::string16& tooltip_text) {
186 tooltip_text_ = tooltip_text; 137 tooltip_text_ = tooltip_text;
187 } 138 }
188 139
189 void Label::SizeToFit(int max_width) { 140 void Label::SizeToFit(int max_width) {
190 DCHECK(multi_line_); 141 DCHECK(multi_line_);
191 142 max_width_ = max_width;
192 std::vector<base::string16> lines;
193 base::SplitString(layout_text_, '\n', &lines);
194
195 int label_width = 0;
196 for (std::vector<base::string16>::const_iterator iter = lines.begin();
197 iter != lines.end(); ++iter) {
198 label_width = std::max(label_width, gfx::GetStringWidth(*iter, font_list_));
199 }
200
201 label_width += GetInsets().width();
202
203 if (max_width > 0)
204 label_width = std::min(label_width, max_width);
205
206 SetBounds(x(), y(), label_width, 0);
207 SizeToPreferredSize(); 143 SizeToPreferredSize();
208 } 144 }
209 145
210 const base::string16& Label::GetLayoutTextForTesting() const { 146 const base::string16& Label::GetLayoutTextForTesting() const {
211 return layout_text_; 147 return render_text_->layout_text();
212 } 148 }
213 149
214 gfx::Insets Label::GetInsets() const { 150 gfx::Insets Label::GetInsets() const {
215 gfx::Insets insets = View::GetInsets(); 151 gfx::Insets insets = View::GetInsets();
216 if (focusable()) { 152 if (focusable()) {
217 insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding, 153 insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding,
218 kFocusBorderPadding, kFocusBorderPadding); 154 kFocusBorderPadding, kFocusBorderPadding);
219 } 155 }
220 return insets; 156 return insets;
221 } 157 }
222 158
223 int Label::GetBaseline() const { 159 int Label::GetBaseline() const {
224 return GetInsets().top() + font_list_.GetBaseline(); 160 return GetInsets().top() + font_list().GetBaseline();
225 } 161 }
226 162
227 gfx::Size Label::GetPreferredSize() const { 163 gfx::Size Label::GetPreferredSize() const {
228 // Return a size of (0, 0) if the label is not visible and if the
229 // collapse_when_hidden_ flag is set.
230 // TODO(munjal): This logic probably belongs to the View class. But for now,
231 // put it here since putting it in View class means all inheriting classes
232 // need ot respect the collapse_when_hidden_ flag.
233 if (!visible() && collapse_when_hidden_) 164 if (!visible() && collapse_when_hidden_)
234 return gfx::Size(); 165 return gfx::Size();
235 166
167 if (multi_line_ && max_width_ != 0 && !text().empty())
168 return gfx::Size(max_width_, GetHeightForWidth(max_width_));
169
236 gfx::Size size(GetTextSize()); 170 gfx::Size size(GetTextSize());
237 gfx::Insets insets = GetInsets(); 171 const gfx::Insets insets = GetInsets();
238 size.Enlarge(insets.width(), insets.height()); 172 size.Enlarge(insets.width(), insets.height());
239 return size; 173 return size;
240 } 174 }
241 175
242 gfx::Size Label::GetMinimumSize() const { 176 gfx::Size Label::GetMinimumSize() const {
243 gfx::Size text_size(GetTextSize()); 177 if (!visible() && collapse_when_hidden_)
244 if ((!visible() && collapse_when_hidden_) || text_size.IsEmpty())
245 return gfx::Size(); 178 return gfx::Size();
246 179
247 gfx::Size size(gfx::GetStringWidth(base::string16(gfx::kEllipsisUTF16), 180 gfx::Size size(0, font_list().GetHeight());
248 font_list_), 181 if (elide_behavior_ == gfx::ELIDE_HEAD ||
249 font_list_.GetHeight()); 182 elide_behavior_ == gfx::ELIDE_MIDDLE ||
250 size.SetToMin(text_size); // The actual text may be shorter than an ellipsis. 183 elide_behavior_ == gfx::ELIDE_TAIL ||
251 gfx::Insets insets = GetInsets(); 184 elide_behavior_ == gfx::ELIDE_EMAIL) {
252 size.Enlarge(insets.width(), insets.height()); 185 size.set_width(gfx::Canvas::GetStringWidth(
186 base::string16(gfx::kEllipsisUTF16), font_list()));
187 }
188 if (!multi_line_)
189 size.SetToMin(GetTextSize());
190 size.Enlarge(GetInsets().width(), GetInsets().height());
253 return size; 191 return size;
254 } 192 }
255 193
256 int Label::GetHeightForWidth(int w) const { 194 int Label::GetHeightForWidth(int w) const {
257 if (!multi_line_) 195 w -= GetInsets().width();
258 return View::GetHeightForWidth(w); 196 if (!multi_line_ || text().empty() || w <= 0)
197 return std::max(line_height_, font_list().GetHeight());
259 198
260 w = std::max(0, w - GetInsets().width()); 199 std::vector<base::string16> lines = GetLinesForWidth(w);
200 int height = lines.size() * std::max(line_height_, font_list().GetHeight());
201 height -= gfx::ShadowValue::GetMargin(render_text_->shadows()).height();
202 return height + GetInsets().height();
203 }
261 204
262 for (size_t i = 0; i < cached_heights_.size(); ++i) { 205 void Label::Layout() {
263 const gfx::Size& s = cached_heights_[i]; 206 lines_.clear();
264 if (s.width() == w) 207 gfx::Rect rect = GetContentsBounds();
265 return s.height() + GetInsets().height(); 208 if (rect.width() == 0 || rect.height() == 0)
209 return;
210
211 std::vector<base::string16> lines;
212 gfx::HorizontalAlignment alignment = horizontal_alignment();
213 gfx::DirectionalityMode directionality = render_text_->directionality_mode();
214 if (multi_line_) {
215 // Force the directionality and alignment of the first line on other lines.
216 bool rtl = render_text_->GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
217 if (alignment == gfx::ALIGN_TO_HEAD)
218 alignment = rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT;
219 directionality =
220 rtl ? gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR;
221 lines = GetLinesForWidth(rect.width());
222 if (lines.size() > 1)
223 rect.set_height(std::max(line_height_, font_list().GetHeight()));
224 } else {
225 lines.push_back(render_text_->layout_text());
266 } 226 }
267 227
268 int cache_width = w; 228 const int bottom = GetContentsBounds().bottom();
269 229 for (size_t i = 0; i < lines.size() && rect.y() <= bottom; ++i) {
270 int h = font_list_.GetHeight(); 230 scoped_ptr<gfx::RenderText> line(gfx::RenderText::CreateInstance());
271 const int flags = ComputeDrawStringFlags(); 231 line->SetHorizontalAlignment(alignment);
272 gfx::Canvas::SizeStringInt( 232 line->SetDirectionalityMode(directionality);
273 layout_text_, font_list_, &w, &h, line_height_, flags); 233 line->SetElideBehavior(elide_behavior_);
274 cached_heights_[cached_heights_cursor_] = gfx::Size(cache_width, h); 234 line->SetFontList(font_list());
275 cached_heights_cursor_ = (cached_heights_cursor_ + 1) % kCachedSizeLimit; 235 line->set_shadows(shadows());
276 return h + GetInsets().height(); 236 line->SetCursorEnabled(false);
237 line->SetText(lines[i]);
238 line->SetDisplayRect(rect);
239 lines_.push_back(line.release());
240 rect.set_y(rect.y() + rect.height());
241 }
242 for (size_t i = lines_.size(); i < lines.size(); ++i)
243 lines_.back()->SetText(lines_.back()->text() + lines[i]);
244 RecalculateColors();
277 } 245 }
278 246
279 const char* Label::GetClassName() const { 247 const char* Label::GetClassName() const {
280 return kViewClassName; 248 return kViewClassName;
281 } 249 }
282 250
283 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) { 251 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) {
284 if (tooltip_text_.empty() && !ShouldShowDefaultTooltip()) 252 if (tooltip_text_.empty() && !ShouldShowDefaultTooltip())
285 return NULL; 253 return NULL;
286 254
287 return HitTestPoint(point) ? this : NULL; 255 return HitTestPoint(point) ? this : NULL;
288 } 256 }
289 257
290 bool Label::CanProcessEventsWithinSubtree() const { 258 bool Label::CanProcessEventsWithinSubtree() const {
291 // Send events to the parent view for handling. 259 // Send events to the parent view for handling.
292 return false; 260 return false;
293 } 261 }
294 262
295 void Label::GetAccessibleState(ui::AXViewState* state) { 263 void Label::GetAccessibleState(ui::AXViewState* state) {
296 state->role = ui::AX_ROLE_STATIC_TEXT; 264 state->role = ui::AX_ROLE_STATIC_TEXT;
297 state->AddStateFlag(ui::AX_STATE_READ_ONLY); 265 state->AddStateFlag(ui::AX_STATE_READ_ONLY);
298 state->name = layout_text_; 266 state->name = render_text_->layout_text();
299 } 267 }
300 268
301 bool Label::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { 269 bool Label::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const {
302 if (!tooltip_text_.empty()) { 270 if (!tooltip_text_.empty()) {
303 tooltip->assign(tooltip_text_); 271 tooltip->assign(tooltip_text_);
304 return true; 272 return true;
305 } 273 }
306 274
307 if (ShouldShowDefaultTooltip()) { 275 if (ShouldShowDefaultTooltip()) {
308 *tooltip = layout_text_; 276 tooltip->assign(render_text_->layout_text());
309 return true; 277 return true;
310 } 278 }
311 279
312 return false; 280 return false;
313 } 281 }
314 282
315 void Label::PaintText(gfx::Canvas* canvas, 283 void Label::OnEnabledChanged() {
316 const base::string16& text, 284 RecalculateColors();
317 const gfx::Rect& text_bounds, 285 }
318 int flags) {
319 SkColor color = enabled() ? actual_enabled_color_ : actual_disabled_color_;
320 if (elide_behavior_ == gfx::FADE_TAIL) {
321 canvas->DrawFadedString(text, font_list_, color, text_bounds, flags);
322 } else {
323 canvas->DrawStringRectWithShadows(text, font_list_, color, text_bounds,
324 line_height_, flags, shadows_);
325 }
326 286
287 void Label::PaintText(gfx::Canvas* canvas) {
288 for (size_t i = 0; i < lines_.size(); ++i)
289 lines_[i]->Draw(canvas);
290 }
291
292 void Label::OnBoundsChanged(const gfx::Rect& previous_bounds) {
293 if (previous_bounds.size() != size())
294 InvalidateLayout();
295 }
296
297 void Label::OnPaint(gfx::Canvas* canvas) {
298 View::OnPaint(canvas);
327 if (HasFocus()) { 299 if (HasFocus()) {
328 gfx::Rect focus_bounds = text_bounds; 300 gfx::Rect focus_bounds = GetLocalBounds();
329 focus_bounds.Inset(-kFocusBorderPadding, -kFocusBorderPadding); 301 focus_bounds.Inset(-kFocusBorderPadding, -kFocusBorderPadding);
330 canvas->DrawFocusRect(focus_bounds); 302 canvas->DrawFocusRect(focus_bounds);
331 } 303 }
332 } 304 PaintText(canvas);
333
334 gfx::Size Label::GetTextSize() const {
335 if (!text_size_valid_) {
336 // For single-line strings, we supply the largest possible width, because
337 // while adding NO_ELLIPSIS to the flags works on Windows for forcing
338 // SizeStringInt() to calculate the desired width, it doesn't seem to work
339 // on Linux.
340 int w = multi_line_ ?
341 GetAvailableRect().width() : std::numeric_limits<int>::max();
342 int h = font_list_.GetHeight();
343 // For single-line strings, ignore the available width and calculate how
344 // wide the text wants to be.
345 int flags = ComputeDrawStringFlags();
346 if (!multi_line_)
347 flags |= gfx::Canvas::NO_ELLIPSIS;
348 gfx::Canvas::SizeStringInt(
349 layout_text_, font_list_, &w, &h, line_height_, flags);
350 text_size_.SetSize(w, h);
351 const gfx::Insets shadow_margin = -gfx::ShadowValue::GetMargin(shadows_);
352 text_size_.Enlarge(shadow_margin.width(), shadow_margin.height());
353 text_size_valid_ = true;
354 }
355
356 return text_size_;
357 }
358
359 void Label::OnBoundsChanged(const gfx::Rect& previous_bounds) {
360 text_size_valid_ &= !multi_line_;
361 }
362
363 void Label::OnPaint(gfx::Canvas* canvas) {
364 OnPaintBackground(canvas);
365 // We skip painting the focus border because it is being handled seperately by
366 // some subclasses of Label. We do not want View's focus border painting to
367 // interfere with that.
368 OnPaintBorder(canvas);
369
370 base::string16 paint_text;
371 gfx::Rect text_bounds;
372 int flags = 0;
373 CalculateDrawStringParams(&paint_text, &text_bounds, &flags);
374 PaintText(canvas, paint_text, text_bounds, flags);
375 } 305 }
376 306
377 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { 307 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) {
378 UpdateColorsFromTheme(theme); 308 UpdateColorsFromTheme(theme);
379 } 309 }
380 310
381 void Label::Init(const base::string16& text, const gfx::FontList& font_list) { 311 void Label::Init(const base::string16& text, const gfx::FontList& font_list) {
382 font_list_ = font_list; 312 render_text_.reset(gfx::RenderText::CreateInstance());
313 render_text_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
314 render_text_->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT);
315 render_text_->SetElideBehavior(gfx::NO_ELIDE);
316 render_text_->SetFontList(font_list);
317 render_text_->SetCursorEnabled(false);
318
319 elide_behavior_ = gfx::ELIDE_TAIL;
383 enabled_color_set_ = disabled_color_set_ = background_color_set_ = false; 320 enabled_color_set_ = disabled_color_set_ = background_color_set_ = false;
384 subpixel_rendering_enabled_ = true; 321 subpixel_rendering_enabled_ = true;
385 auto_color_readability_ = true; 322 auto_color_readability_ = true;
386 UpdateColorsFromTheme(ui::NativeTheme::instance()); 323 UpdateColorsFromTheme(ui::NativeTheme::instance());
387 horizontal_alignment_ = gfx::ALIGN_CENTER; 324 collapse_when_hidden_ = false;
325 allow_character_break_ = false;
326 multi_line_ = false;
388 line_height_ = 0; 327 line_height_ = 0;
389 multi_line_ = false; 328 max_width_ = 0;
390 obscured_ = false; 329 SetText(text);
391 allow_character_break_ = false; 330 }
392 elide_behavior_ = gfx::ELIDE_TAIL;
393 collapse_when_hidden_ = false;
394 cached_heights_.resize(kCachedSizeLimit);
395 ResetCachedSize();
396 331
397 SetText(text); 332 void Label::ResetLayout() {
333 InvalidateLayout();
334 PreferredSizeChanged();
335 SchedulePaint();
336 }
337
338 std::vector<base::string16> Label::GetLinesForWidth(int width) const {
339 std::vector<base::string16> lines;
340 const gfx::WordWrapBehavior wrap =
341 allow_character_break_ ? gfx::WRAP_LONG_WORDS : gfx::TRUNCATE_LONG_WORDS;
342 gfx::ElideRectangleText(render_text_->layout_text(),
343 font_list(),
344 width,
345 std::numeric_limits<int>::max(),
346 wrap,
347 &lines);
348 return lines;
349 }
350
351 gfx::Size Label::GetTextSize() const {
352 gfx::Size size;
353 if (text().empty()) {
354 size = gfx::Size(0, std::max(line_height_, font_list().GetHeight()));
355 } else if (!multi_line_) {
356 size = render_text_->GetStringSize();
357 } else {
358 // Get the natural text size, unelided and only wrapped on newlines.
359 std::vector<base::string16> lines;
360 base::SplitString(render_text_->layout_text(), '\n', &lines);
361 scoped_ptr<gfx::RenderText> render_text(gfx::RenderText::CreateInstance());
362 render_text->SetFontList(font_list());
363 for (size_t i = 0; i < lines.size(); ++i) {
364 render_text->SetText(lines[i]);
365 const gfx::Size line = render_text->GetStringSize();
366 size.set_width(std::max(size.width(), line.width()));
367 size.set_height(size.height() + std::max(line_height_, line.height()));
368 }
369 }
370 const gfx::Insets shadow_margin = -gfx::ShadowValue::GetMargin(shadows());
371 size.Enlarge(shadow_margin.width(), shadow_margin.height());
372 return size;
398 } 373 }
399 374
400 void Label::RecalculateColors() { 375 void Label::RecalculateColors() {
401 actual_enabled_color_ = auto_color_readability_ ? 376 actual_enabled_color_ = auto_color_readability_ ?
402 color_utils::GetReadableColor(requested_enabled_color_, 377 color_utils::GetReadableColor(requested_enabled_color_,
403 background_color_) : 378 background_color_) :
404 requested_enabled_color_; 379 requested_enabled_color_;
405 actual_disabled_color_ = auto_color_readability_ ? 380 actual_disabled_color_ = auto_color_readability_ ?
406 color_utils::GetReadableColor(requested_disabled_color_, 381 color_utils::GetReadableColor(requested_disabled_color_,
407 background_color_) : 382 background_color_) :
408 requested_disabled_color_; 383 requested_disabled_color_;
409 }
410 384
411 gfx::Rect Label::GetTextBounds() const { 385 SkColor color = enabled() ? actual_enabled_color_ : actual_disabled_color_;
412 gfx::Rect available(GetAvailableRect()); 386 bool background_is_transparent =
413 gfx::Size text_size(GetTextSize()); 387 SkColorGetA(background_color_) != 0xFF && !subpixel_rendering_enabled_;
414 text_size.set_width(std::min(available.width(), text_size.width())); 388 for (size_t i = 0; i < lines_.size(); ++i) {
415 gfx::Point origin(GetInsets().left(), GetInsets().top()); 389 lines_[i]->SetColor(color);
416 switch (GetHorizontalAlignment()) { 390 lines_[i]->set_background_is_transparent(background_is_transparent);
417 case gfx::ALIGN_LEFT:
418 break;
419 case gfx::ALIGN_CENTER:
420 // Put any extra margin pixel on the left to match the legacy behavior
421 // from the use of GetTextExtentPoint32() on Windows.
422 origin.Offset((available.width() + 1 - text_size.width()) / 2, 0);
423 break;
424 case gfx::ALIGN_RIGHT:
425 origin.set_x(available.right() - text_size.width());
426 break;
427 default:
428 NOTREACHED();
429 break;
430 } 391 }
431 origin.Offset(0, std::max(0, (available.height() - text_size.height())) / 2); 392 SchedulePaint();
432 return gfx::Rect(origin, text_size);
433 }
434
435 int Label::ComputeDrawStringFlags() const {
436 int flags = 0;
437
438 // We can't use subpixel rendering if the background is non-opaque.
439 if (SkColorGetA(background_color_) != 0xFF || !subpixel_rendering_enabled_)
440 flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING;
441
442 base::i18n::TextDirection direction =
443 base::i18n::GetFirstStrongCharacterDirection(layout_text_);
444 if (direction == base::i18n::RIGHT_TO_LEFT)
445 flags |= gfx::Canvas::FORCE_RTL_DIRECTIONALITY;
446 else
447 flags |= gfx::Canvas::FORCE_LTR_DIRECTIONALITY;
448
449 switch (GetHorizontalAlignment()) {
450 case gfx::ALIGN_LEFT:
451 flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
452 break;
453 case gfx::ALIGN_CENTER:
454 flags |= gfx::Canvas::TEXT_ALIGN_CENTER;
455 break;
456 case gfx::ALIGN_RIGHT:
457 flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
458 break;
459 default:
460 NOTREACHED();
461 break;
462 }
463
464 if (!multi_line_)
465 return flags;
466
467 flags |= gfx::Canvas::MULTI_LINE;
468 #if !defined(OS_WIN)
469 // Don't elide multiline labels on Linux.
470 // Todo(davemoore): Do we depend on eliding multiline text?
471 // Pango insists on limiting the number of lines to one if text is
472 // elided. You can get around this if you can pass a maximum height
473 // but we don't currently have that data when we call the pango code.
474 flags |= gfx::Canvas::NO_ELLIPSIS;
475 #endif
476 if (allow_character_break_)
477 flags |= gfx::Canvas::CHARACTER_BREAK;
478
479 return flags;
480 }
481
482 gfx::Rect Label::GetAvailableRect() const {
483 gfx::Rect bounds(size());
484 bounds.Inset(GetInsets());
485 return bounds;
486 }
487
488 void Label::CalculateDrawStringParams(base::string16* paint_text,
489 gfx::Rect* text_bounds,
490 int* flags) const {
491 DCHECK(paint_text && text_bounds && flags);
492
493 const bool forbid_ellipsis = elide_behavior_ == gfx::NO_ELIDE ||
494 elide_behavior_ == gfx::FADE_TAIL;
495 if (multi_line_ || forbid_ellipsis) {
496 *paint_text = layout_text_;
497 } else {
498 *paint_text = gfx::ElideText(layout_text_, font_list_,
499 GetAvailableRect().width(), elide_behavior_);
500 }
501
502 *text_bounds = GetTextBounds();
503 *flags = ComputeDrawStringFlags();
504 // TODO(msw): Elide multi-line text with ElideRectangleText instead.
505 if (!multi_line_ || forbid_ellipsis)
506 *flags |= gfx::Canvas::NO_ELLIPSIS;
507 } 393 }
508 394
509 void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) { 395 void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) {
510 if (!enabled_color_set_) { 396 if (!enabled_color_set_) {
511 requested_enabled_color_ = theme->GetSystemColor( 397 requested_enabled_color_ = theme->GetSystemColor(
512 ui::NativeTheme::kColorId_LabelEnabledColor); 398 ui::NativeTheme::kColorId_LabelEnabledColor);
513 } 399 }
514 if (!disabled_color_set_) { 400 if (!disabled_color_set_) {
515 requested_disabled_color_ = theme->GetSystemColor( 401 requested_disabled_color_ = theme->GetSystemColor(
516 ui::NativeTheme::kColorId_LabelDisabledColor); 402 ui::NativeTheme::kColorId_LabelDisabledColor);
517 } 403 }
518 if (!background_color_set_) { 404 if (!background_color_set_) {
519 background_color_ = theme->GetSystemColor( 405 background_color_ = theme->GetSystemColor(
520 ui::NativeTheme::kColorId_LabelBackgroundColor); 406 ui::NativeTheme::kColorId_LabelBackgroundColor);
521 } 407 }
522 RecalculateColors(); 408 RecalculateColors();
523 } 409 }
524 410
525 void Label::ResetCachedSize() {
526 text_size_valid_ = false;
527 cached_heights_cursor_ = 0;
528 for (int i = 0; i < kCachedSizeLimit; ++i)
529 cached_heights_[i] = gfx::Size();
530 }
531
532 bool Label::ShouldShowDefaultTooltip() const { 411 bool Label::ShouldShowDefaultTooltip() const {
533 const gfx::Size text_size = GetTextSize(); 412 const gfx::Size text_size = GetTextSize();
534 const gfx::Size size = GetContentsBounds().size(); 413 const gfx::Size size = GetContentsBounds().size();
535 return !obscured() && (text_size.width() > size.width() || 414 return !obscured() && (text_size.width() > size.width() ||
536 text_size.height() > size.height()); 415 text_size.height() > size.height());
537 } 416 }
538 417
539 } // namespace views 418 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/label.h ('k') | ui/views/controls/label_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698