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

Side by Side Diff: ui/views/controls/button/label_button.cc

Issue 851853002: It is time. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Trying to reup because the last upload failed. Created 5 years, 11 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
« no previous file with comments | « ui/views/controls/button/label_button.h ('k') | ui/views/controls/button/label_button_border.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 (c) 2012 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/button/label_button.h"
6
7 #include "base/lazy_instance.h"
8 #include "base/logging.h"
9 #include "ui/gfx/animation/throb_animation.h"
10 #include "ui/gfx/canvas.h"
11 #include "ui/gfx/font_list.h"
12 #include "ui/gfx/sys_color_change_listener.h"
13 #include "ui/native_theme/native_theme.h"
14 #include "ui/views/background.h"
15 #include "ui/views/controls/button/label_button_border.h"
16 #include "ui/views/painter.h"
17 #include "ui/views/window/dialog_delegate.h"
18
19 namespace {
20
21 // The default spacing between the icon and text.
22 const int kSpacing = 5;
23
24 #if !(defined(OS_LINUX) && !defined(OS_CHROMEOS))
25 // Default text and shadow colors for STYLE_BUTTON.
26 const SkColor kStyleButtonTextColor = SK_ColorBLACK;
27 const SkColor kStyleButtonShadowColor = SK_ColorWHITE;
28 #endif
29
30 const gfx::FontList& GetDefaultNormalFontList() {
31 static base::LazyInstance<gfx::FontList>::Leaky font_list =
32 LAZY_INSTANCE_INITIALIZER;
33 return font_list.Get();
34 }
35
36 const gfx::FontList& GetDefaultBoldFontList() {
37 static base::LazyInstance<gfx::FontList>::Leaky font_list =
38 LAZY_INSTANCE_INITIALIZER;
39 if ((font_list.Get().GetFontStyle() & gfx::Font::BOLD) == 0) {
40 font_list.Get() = font_list.Get().
41 DeriveWithStyle(font_list.Get().GetFontStyle() | gfx::Font::BOLD);
42 DCHECK_NE(font_list.Get().GetFontStyle() & gfx::Font::BOLD, 0);
43 }
44 return font_list.Get();
45 }
46
47 } // namespace
48
49 namespace views {
50
51 // static
52 const int LabelButton::kHoverAnimationDurationMs = 170;
53
54 // static
55 const char LabelButton::kViewClassName[] = "LabelButton";
56
57 LabelButton::LabelButton(ButtonListener* listener, const base::string16& text)
58 : CustomButton(listener),
59 image_(new ImageView()),
60 label_(new Label()),
61 cached_normal_font_list_(GetDefaultNormalFontList()),
62 cached_bold_font_list_(GetDefaultBoldFontList()),
63 button_state_images_(),
64 button_state_colors_(),
65 explicitly_set_colors_(),
66 is_default_(false),
67 style_(STYLE_TEXTBUTTON),
68 border_is_themed_border_(true),
69 image_label_spacing_(kSpacing) {
70 SetAnimationDuration(kHoverAnimationDurationMs);
71 SetText(text);
72
73 AddChildView(image_);
74 image_->set_interactive(false);
75
76 AddChildView(label_);
77 label_->SetFontList(cached_normal_font_list_);
78 label_->SetAutoColorReadabilityEnabled(false);
79 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
80
81 // Initialize the colors, border, and layout.
82 SetStyle(style_);
83
84 SetAccessibleName(text);
85 }
86
87 LabelButton::~LabelButton() {}
88
89 const gfx::ImageSkia& LabelButton::GetImage(ButtonState for_state) {
90 if (for_state != STATE_NORMAL && button_state_images_[for_state].isNull())
91 return button_state_images_[STATE_NORMAL];
92 return button_state_images_[for_state];
93 }
94
95 void LabelButton::SetImage(ButtonState for_state, const gfx::ImageSkia& image) {
96 button_state_images_[for_state] = image;
97 UpdateImage();
98 }
99
100 const base::string16& LabelButton::GetText() const {
101 return label_->text();
102 }
103
104 void LabelButton::SetText(const base::string16& text) {
105 SetAccessibleName(text);
106 label_->SetText(text);
107 }
108
109 void LabelButton::SetTextColor(ButtonState for_state, SkColor color) {
110 button_state_colors_[for_state] = color;
111 if (for_state == STATE_DISABLED)
112 label_->SetDisabledColor(color);
113 else if (for_state == state())
114 label_->SetEnabledColor(color);
115 explicitly_set_colors_[for_state] = true;
116 }
117
118 void LabelButton::SetTextShadows(const gfx::ShadowValues& shadows) {
119 label_->SetShadows(shadows);
120 }
121
122 void LabelButton::SetTextSubpixelRenderingEnabled(bool enabled) {
123 label_->SetSubpixelRenderingEnabled(enabled);
124 }
125
126 bool LabelButton::GetTextMultiLine() const {
127 return label_->multi_line();
128 }
129
130 void LabelButton::SetTextMultiLine(bool text_multi_line) {
131 label_->SetMultiLine(text_multi_line);
132 }
133
134 const gfx::FontList& LabelButton::GetFontList() const {
135 return label_->font_list();
136 }
137
138 void LabelButton::SetFontList(const gfx::FontList& font_list) {
139 cached_normal_font_list_ = font_list;
140 cached_bold_font_list_ = font_list.DeriveWithStyle(
141 font_list.GetFontStyle() | gfx::Font::BOLD);
142
143 // STYLE_BUTTON uses bold text to indicate default buttons.
144 label_->SetFontList(
145 style_ == STYLE_BUTTON && is_default_ ?
146 cached_bold_font_list_ : cached_normal_font_list_);
147 }
148
149 void LabelButton::SetElideBehavior(gfx::ElideBehavior elide_behavior) {
150 label_->SetElideBehavior(elide_behavior);
151 }
152
153 gfx::HorizontalAlignment LabelButton::GetHorizontalAlignment() const {
154 return label_->GetHorizontalAlignment();
155 }
156
157 void LabelButton::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
158 label_->SetHorizontalAlignment(alignment);
159 InvalidateLayout();
160 }
161
162 void LabelButton::SetMinSize(const gfx::Size& min_size) {
163 min_size_ = min_size;
164 ResetCachedPreferredSize();
165 }
166
167 void LabelButton::SetMaxSize(const gfx::Size& max_size) {
168 max_size_ = max_size;
169 ResetCachedPreferredSize();
170 }
171
172 void LabelButton::SetIsDefault(bool is_default) {
173 if (is_default == is_default_)
174 return;
175 is_default_ = is_default;
176 ui::Accelerator accel(ui::VKEY_RETURN, ui::EF_NONE);
177 is_default_ ? AddAccelerator(accel) : RemoveAccelerator(accel);
178
179 // STYLE_BUTTON uses bold text to indicate default buttons.
180 if (style_ == STYLE_BUTTON) {
181 label_->SetFontList(
182 is_default ? cached_bold_font_list_ : cached_normal_font_list_);
183 }
184 }
185
186 void LabelButton::SetStyle(ButtonStyle style) {
187 style_ = style;
188 // Inset the button focus rect from the actual border; roughly match Windows.
189 if (style == STYLE_BUTTON) {
190 SetFocusPainter(scoped_ptr<Painter>());
191 } else {
192 SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets(
193 gfx::Insets(3, 3, 3, 3)));
194 }
195 if (style == STYLE_BUTTON) {
196 label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
197 SetFocusable(true);
198 }
199 if (style == STYLE_BUTTON)
200 SetMinSize(gfx::Size(70, 33));
201 OnNativeThemeChanged(GetNativeTheme());
202 ResetCachedPreferredSize();
203 }
204
205 void LabelButton::SetImageLabelSpacing(int spacing) {
206 if (spacing == image_label_spacing_)
207 return;
208 image_label_spacing_ = spacing;
209 ResetCachedPreferredSize();
210 InvalidateLayout();
211 }
212
213 void LabelButton::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
214 focus_painter_ = focus_painter.Pass();
215 }
216
217 gfx::Size LabelButton::GetPreferredSize() const {
218 if (cached_preferred_size_valid_)
219 return cached_preferred_size_;
220
221 // Use a temporary label copy for sizing to avoid calculation side-effects.
222 Label label(GetText(), cached_normal_font_list_);
223 label.SetShadows(label_->shadows());
224 label.SetMultiLine(GetTextMultiLine());
225
226 if (style() == STYLE_BUTTON) {
227 // Some text appears wider when rendered normally than when rendered bold.
228 // Accommodate the widest, as buttons may show bold and shouldn't resize.
229 const int current_width = label.GetPreferredSize().width();
230 label.SetFontList(cached_bold_font_list_);
231 if (label.GetPreferredSize().width() < current_width)
232 label.SetFontList(cached_normal_font_list_);
233 }
234
235 // Calculate the required size.
236 const gfx::Size image_size(image_->GetPreferredSize());
237 gfx::Size size(label.GetPreferredSize());
238 if (image_size.width() > 0 && size.width() > 0)
239 size.Enlarge(image_label_spacing_, 0);
240 size.SetToMax(gfx::Size(0, image_size.height()));
241 const gfx::Insets insets(GetInsets());
242 size.Enlarge(image_size.width() + insets.width(), insets.height());
243
244 // Make the size at least as large as the minimum size needed by the border.
245 size.SetToMax(border() ? border()->GetMinimumSize() : gfx::Size());
246
247 // Increase the minimum size monotonically with the preferred size.
248 size.SetToMax(min_size_);
249 min_size_ = size;
250
251 // Return the largest known size clamped to the maximum size (if valid).
252 if (max_size_.width() > 0)
253 size.set_width(std::min(max_size_.width(), size.width()));
254 if (max_size_.height() > 0)
255 size.set_height(std::min(max_size_.height(), size.height()));
256
257 // Cache this computed size, as recomputing it is an expensive operation.
258 cached_preferred_size_valid_ = true;
259 cached_preferred_size_ = size;
260 return cached_preferred_size_;
261 }
262
263 int LabelButton::GetHeightForWidth(int w) const {
264 w -= GetInsets().width();
265 const gfx::Size image_size(image_->GetPreferredSize());
266 w -= image_size.width();
267 if (image_size.width() > 0 && !GetText().empty())
268 w -= image_label_spacing_;
269
270 int height = std::max(image_size.height(), label_->GetHeightForWidth(w));
271 if (border())
272 height = std::max(height, border()->GetMinimumSize().height());
273
274 height = std::max(height, min_size_.height());
275 if (max_size_.height() > 0)
276 height = std::min(height, max_size_.height());
277 return height;
278 }
279
280 void LabelButton::Layout() {
281 gfx::HorizontalAlignment adjusted_alignment = GetHorizontalAlignment();
282 if (base::i18n::IsRTL() && adjusted_alignment != gfx::ALIGN_CENTER)
283 adjusted_alignment = (adjusted_alignment == gfx::ALIGN_LEFT) ?
284 gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT;
285
286 gfx::Rect child_area(GetChildAreaBounds());
287 child_area.Inset(GetInsets());
288
289 gfx::Size image_size(image_->GetPreferredSize());
290 image_size.SetToMin(child_area.size());
291
292 // The label takes any remaining width after sizing the image, unless both
293 // views are centered. In that case, using the tighter preferred label width
294 // avoids wasted space within the label that would look like awkward padding.
295 // Labels can paint over the full button height, including the border height.
296 gfx::Size label_size(child_area.width(), height());
297 if (!image_size.IsEmpty() && !label_size.IsEmpty()) {
298 label_size.set_width(std::max(child_area.width() -
299 image_size.width() - image_label_spacing_, 0));
300 if (adjusted_alignment == gfx::ALIGN_CENTER) {
301 // Ensure multi-line labels paired with images use their available width.
302 label_size.set_width(
303 std::min(label_size.width(), label_->GetPreferredSize().width()));
304 }
305 }
306
307 gfx::Point image_origin(child_area.origin());
308 image_origin.Offset(0, (child_area.height() - image_size.height()) / 2);
309 if (adjusted_alignment == gfx::ALIGN_CENTER) {
310 const int spacing = (image_size.width() > 0 && label_size.width() > 0) ?
311 image_label_spacing_ : 0;
312 const int total_width = image_size.width() + label_size.width() +
313 spacing;
314 image_origin.Offset((child_area.width() - total_width) / 2, 0);
315 } else if (adjusted_alignment == gfx::ALIGN_RIGHT) {
316 image_origin.Offset(child_area.width() - image_size.width(), 0);
317 }
318
319 gfx::Point label_origin(child_area.x(), 0);
320 if (!image_size.IsEmpty() && adjusted_alignment != gfx::ALIGN_RIGHT) {
321 label_origin.set_x(image_origin.x() + image_size.width() +
322 image_label_spacing_);
323 }
324
325 image_->SetBoundsRect(gfx::Rect(image_origin, image_size));
326 label_->SetBoundsRect(gfx::Rect(label_origin, label_size));
327 }
328
329 const char* LabelButton::GetClassName() const {
330 return kViewClassName;
331 }
332
333 scoped_ptr<LabelButtonBorder> LabelButton::CreateDefaultBorder() const {
334 return scoped_ptr<LabelButtonBorder>(new LabelButtonBorder(style_));
335 }
336
337 void LabelButton::SetBorder(scoped_ptr<Border> border) {
338 border_is_themed_border_ = false;
339 View::SetBorder(border.Pass());
340 ResetCachedPreferredSize();
341 }
342
343 gfx::Rect LabelButton::GetChildAreaBounds() {
344 return GetLocalBounds();
345 }
346
347 void LabelButton::OnPaint(gfx::Canvas* canvas) {
348 View::OnPaint(canvas);
349 Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
350 }
351
352 void LabelButton::OnFocus() {
353 View::OnFocus();
354 // Typically the border renders differently when focused.
355 SchedulePaint();
356 }
357
358 void LabelButton::OnBlur() {
359 View::OnBlur();
360 // Typically the border renders differently when focused.
361 SchedulePaint();
362 }
363
364 void LabelButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const {
365 params->button.checked = false;
366 params->button.indeterminate = false;
367 params->button.is_default = is_default_;
368 params->button.is_focused = HasFocus() && IsAccessibilityFocusable();
369 params->button.has_border = false;
370 params->button.classic_state = 0;
371 params->button.background_color = label_->background_color();
372 }
373
374 void LabelButton::ResetColorsFromNativeTheme() {
375 const ui::NativeTheme* theme = GetNativeTheme();
376 SkColor colors[STATE_COUNT] = {
377 theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonEnabledColor),
378 theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonHoverColor),
379 theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonHoverColor),
380 theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonDisabledColor),
381 };
382
383 // Certain styles do not change text color when hovered or pressed.
384 bool constant_text_color = false;
385 // Use hardcoded colors for inverted color scheme support and STYLE_BUTTON.
386 if (gfx::IsInvertedColorScheme()) {
387 constant_text_color = true;
388 colors[STATE_NORMAL] = SK_ColorWHITE;
389 label_->SetBackgroundColor(SK_ColorBLACK);
390 label_->set_background(Background::CreateSolidBackground(SK_ColorBLACK));
391 label_->SetAutoColorReadabilityEnabled(true);
392 label_->SetShadows(gfx::ShadowValues());
393 } else if (style() == STYLE_BUTTON) {
394 // TODO(erg): This is disabled on desktop linux because of the binary asset
395 // confusion. These details should either be pushed into ui::NativeThemeWin
396 // or should be obsoleted by rendering buttons with paint calls instead of
397 // with static assets. http://crbug.com/350498
398 #if !(defined(OS_LINUX) && !defined(OS_CHROMEOS))
399 constant_text_color = true;
400 colors[STATE_NORMAL] = kStyleButtonTextColor;
401 label_->SetBackgroundColor(theme->GetSystemColor(
402 ui::NativeTheme::kColorId_ButtonBackgroundColor));
403 label_->SetAutoColorReadabilityEnabled(false);
404 label_->SetShadows(gfx::ShadowValues(
405 1, gfx::ShadowValue(gfx::Point(0, 1), 0, kStyleButtonShadowColor)));
406 #endif
407 label_->set_background(NULL);
408 } else {
409 label_->set_background(NULL);
410 }
411
412 if (constant_text_color)
413 colors[STATE_HOVERED] = colors[STATE_PRESSED] = colors[STATE_NORMAL];
414
415 for (size_t state = STATE_NORMAL; state < STATE_COUNT; ++state) {
416 if (!explicitly_set_colors_[state]) {
417 SetTextColor(static_cast<ButtonState>(state), colors[state]);
418 explicitly_set_colors_[state] = false;
419 }
420 }
421 }
422
423 void LabelButton::UpdateImage() {
424 image_->SetImage(GetImage(state()));
425 ResetCachedPreferredSize();
426 }
427
428 void LabelButton::UpdateThemedBorder() {
429 // Don't override borders set by others.
430 if (!border_is_themed_border_)
431 return;
432
433 scoped_ptr<LabelButtonBorder> label_button_border = CreateDefaultBorder();
434 SetBorder(label_button_border.Pass());
435
436 border_is_themed_border_ = true;
437 }
438
439 void LabelButton::StateChanged() {
440 const gfx::Size previous_image_size(image_->GetPreferredSize());
441 UpdateImage();
442 const SkColor color = button_state_colors_[state()];
443 if (state() != STATE_DISABLED && label_->enabled_color() != color)
444 label_->SetEnabledColor(color);
445 label_->SetEnabled(state() != STATE_DISABLED);
446 if (image_->GetPreferredSize() != previous_image_size)
447 Layout();
448 }
449
450 void LabelButton::ChildPreferredSizeChanged(View* child) {
451 ResetCachedPreferredSize();
452 PreferredSizeChanged();
453 }
454
455 void LabelButton::OnNativeThemeChanged(const ui::NativeTheme* theme) {
456 ResetColorsFromNativeTheme();
457 UpdateThemedBorder();
458 // Invalidate the layout to pickup the new insets from the border.
459 InvalidateLayout();
460 }
461
462 ui::NativeTheme::Part LabelButton::GetThemePart() const {
463 return ui::NativeTheme::kPushButton;
464 }
465
466 gfx::Rect LabelButton::GetThemePaintRect() const {
467 return GetLocalBounds();
468 }
469
470 ui::NativeTheme::State LabelButton::GetThemeState(
471 ui::NativeTheme::ExtraParams* params) const {
472 GetExtraParams(params);
473 switch (state()) {
474 case STATE_NORMAL: return ui::NativeTheme::kNormal;
475 case STATE_HOVERED: return ui::NativeTheme::kHovered;
476 case STATE_PRESSED: return ui::NativeTheme::kPressed;
477 case STATE_DISABLED: return ui::NativeTheme::kDisabled;
478 case STATE_COUNT: NOTREACHED() << "Unknown state: " << state();
479 }
480 return ui::NativeTheme::kNormal;
481 }
482
483 const gfx::Animation* LabelButton::GetThemeAnimation() const {
484 return hover_animation_.get();
485 }
486
487 ui::NativeTheme::State LabelButton::GetBackgroundThemeState(
488 ui::NativeTheme::ExtraParams* params) const {
489 GetExtraParams(params);
490 return ui::NativeTheme::kNormal;
491 }
492
493 ui::NativeTheme::State LabelButton::GetForegroundThemeState(
494 ui::NativeTheme::ExtraParams* params) const {
495 GetExtraParams(params);
496 return ui::NativeTheme::kHovered;
497 }
498
499 void LabelButton::ResetCachedPreferredSize() {
500 cached_preferred_size_valid_ = false;
501 cached_preferred_size_= gfx::Size();
502 }
503
504 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/button/label_button.h ('k') | ui/views/controls/button/label_button_border.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698