blob: 3fc11aebcd1ba6296d042612458a403c7f2a8723 [file] [log] [blame]
[email protected]70e983cc2012-10-16 21:08:221// 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/logging.h"
8#include "grit/ui_resources.h"
9#include "ui/base/animation/throb_animation.h"
10#include "ui/base/resource/resource_bundle.h"
[email protected]990e6222012-11-16 13:31:1811#include "ui/native_theme/native_theme.h"
[email protected]70e983cc2012-10-16 21:08:2212#include "ui/views/controls/button/label_button_border.h"
13#include "ui/views/focus_border.h"
14
15#if defined(OS_WIN)
16#include "ui/gfx/color_utils.h"
[email protected]990e6222012-11-16 13:31:1817#include "ui/native_theme/native_theme_win.h"
[email protected]70e983cc2012-10-16 21:08:2218#endif
19
[email protected]70e983cc2012-10-16 21:08:2220namespace {
21
22// The spacing between the icon and text.
[email protected]c5de33662012-10-27 22:37:3023const int kSpacing = 5;
[email protected]70e983cc2012-10-16 21:08:2224
25// The length of the hover fade animation.
[email protected]c5de33662012-10-27 22:37:3026const int kHoverAnimationDurationMs = 170;
[email protected]70e983cc2012-10-16 21:08:2227
28} // namespace
29
[email protected]c5de33662012-10-27 22:37:3030namespace views {
31
[email protected]fe22ba12013-02-21 13:43:4832// static
33const char LabelButton::kViewClassName[] = "views/LabelButton";
34
[email protected]70e983cc2012-10-16 21:08:2235LabelButton::LabelButton(ButtonListener* listener, const string16& text)
36 : CustomButton(listener),
37 image_(new ImageView()),
38 label_(new Label(text)),
[email protected]379bc982012-12-19 19:56:5339 button_state_images_(),
40 button_state_colors_(),
41 explicitly_set_colors_(),
[email protected]e59fc52a2013-02-20 20:24:1142 is_default_(false),
[email protected]fe22ba12013-02-21 13:43:4843 style_(STYLE_TEXTBUTTON) {
[email protected]70e983cc2012-10-16 21:08:2244 SetAnimationDuration(kHoverAnimationDurationMs);
45
46 AddChildView(image_);
47 image_->set_interactive(false);
48
49 AddChildView(label_);
50 label_->SetAutoColorReadabilityEnabled(false);
[email protected]cc563f02012-11-07 17:37:3651 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
[email protected]70e983cc2012-10-16 21:08:2252
[email protected]fe22ba12013-02-21 13:43:4853 // Initialize the colors, border, and layout.
54 SetStyle(style_);
[email protected]cd70b3e2013-04-03 20:01:3755
56 SetAccessibleName(text);
[email protected]70e983cc2012-10-16 21:08:2257}
58
59LabelButton::~LabelButton() {}
60
61const gfx::ImageSkia& LabelButton::GetImage(ButtonState for_state) {
[email protected]fca8dc02012-11-14 16:25:5662 if (for_state != STATE_NORMAL && button_state_images_[for_state].isNull())
63 return button_state_images_[STATE_NORMAL];
[email protected]70e983cc2012-10-16 21:08:2264 return button_state_images_[for_state];
65}
66
67void LabelButton::SetImage(ButtonState for_state, const gfx::ImageSkia& image) {
68 button_state_images_[for_state] = image;
69 image_->SetImage(GetImage(state()));
70}
71
72const string16& LabelButton::GetText() const {
73 return label_->text();
74}
75
76void LabelButton::SetText(const string16& text) {
77 label_->SetText(text);
[email protected]cd70b3e2013-04-03 20:01:3778 SetAccessibleName(text);
[email protected]70e983cc2012-10-16 21:08:2279}
80
81void LabelButton::SetTextColor(ButtonState for_state, SkColor color) {
82 button_state_colors_[for_state] = color;
[email protected]fca8dc02012-11-14 16:25:5683 if (for_state == STATE_DISABLED)
[email protected]70e983cc2012-10-16 21:08:2284 label_->SetDisabledColor(color);
85 else if (for_state == state())
86 label_->SetEnabledColor(color);
[email protected]204b2082012-11-07 17:53:0387 explicitly_set_colors_[for_state] = true;
[email protected]70e983cc2012-10-16 21:08:2288}
89
90bool LabelButton::GetTextMultiLine() const {
91 return label_->is_multi_line();
92}
93
94void LabelButton::SetTextMultiLine(bool text_multi_line) {
95 label_->SetMultiLine(text_multi_line);
96}
97
[email protected]cfc888a2012-11-05 18:20:5098const gfx::Font& LabelButton::GetFont() const {
99 return label_->font();
100}
101
102void LabelButton::SetFont(const gfx::Font& font) {
103 label_->SetFont(font);
104}
105
[email protected]cc563f02012-11-07 17:37:36106gfx::HorizontalAlignment LabelButton::GetHorizontalAlignment() const {
[email protected]70e983cc2012-10-16 21:08:22107 return label_->horizontal_alignment();
108}
109
[email protected]cc563f02012-11-07 17:37:36110void LabelButton::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
[email protected]70e983cc2012-10-16 21:08:22111 label_->SetHorizontalAlignment(alignment);
112 InvalidateLayout();
113}
114
[email protected]e59fc52a2013-02-20 20:24:11115void LabelButton::SetIsDefault(bool is_default) {
116 if (is_default == is_default_)
[email protected]70e983cc2012-10-16 21:08:22117 return;
[email protected]e59fc52a2013-02-20 20:24:11118 is_default_ = is_default;
[email protected]70e983cc2012-10-16 21:08:22119 ui::Accelerator accel(ui::VKEY_RETURN, ui::EF_NONE);
[email protected]e59fc52a2013-02-20 20:24:11120 is_default_ ? AddAccelerator(accel) : RemoveAccelerator(accel);
[email protected]70e983cc2012-10-16 21:08:22121}
122
[email protected]fe22ba12013-02-21 13:43:48123void LabelButton::SetStyle(ButtonStyle style) {
124 style_ = style;
125 set_border(new LabelButtonBorder(style));
126 // Inset the button focus rect from the actual border; roughly match Windows.
[email protected]644f4fb2013-03-01 21:14:34127 if (style == STYLE_TEXTBUTTON || style == STYLE_NATIVE_TEXTBUTTON)
128 set_focus_border(FocusBorder::CreateDashedFocusBorder(3, 3, 3, 3));
[email protected]df798cb2013-04-05 06:27:52129 if (style == STYLE_BUTTON || style == STYLE_NATIVE_TEXTBUTTON)
[email protected]d0abd282013-02-28 23:59:08130 label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
[email protected]df798cb2013-04-05 06:27:52131 if (style == STYLE_NATIVE_TEXTBUTTON)
132 set_focusable(true);
[email protected]644f4fb2013-03-01 21:14:34133 if (style == STYLE_BUTTON) {
134 set_min_size(gfx::Size(70, 31));
135 const SkColor color = GetNativeTheme()->GetSystemColor(
136 ui::NativeTheme::kColorId_WindowBackground);
137 label_->SetShadowColors(color, color);
138 label_->SetShadowOffset(0, 1);
139 }
[email protected]204b2082012-11-07 17:53:03140 // Invalidate the layout to pickup the new insets from the border.
141 InvalidateLayout();
[email protected]379bc982012-12-19 19:56:53142 ResetColorsFromNativeTheme();
[email protected]204b2082012-11-07 17:53:03143}
144
[email protected]7f39bb692013-01-07 23:08:16145gfx::Size LabelButton::GetPreferredSize() {
146 // Resize multi-line labels paired with images to use their available width.
147 const gfx::Size image_size(image_->GetPreferredSize());
148 if (GetTextMultiLine() && !image_size.IsEmpty() && !GetText().empty() &&
149 GetHorizontalAlignment() == gfx::ALIGN_CENTER) {
150 label_->SizeToFit(GetLocalBounds().width() - image_size.width() - kSpacing);
151 }
152
153 // Calculate the required size.
154 gfx::Size size(label_->GetPreferredSize());
155 if (image_size.width() > 0 && size.width() > 0)
156 size.Enlarge(kSpacing, 0);
157 size.set_height(std::max(size.height(), image_size.height()));
158 size.Enlarge(image_size.width() + GetInsets().width(), GetInsets().height());
159
160 // Increase the minimum size monotonically with the preferred size.
161 size.SetSize(std::max(min_size_.width(), size.width()),
162 std::max(min_size_.height(), size.height()));
163 min_size_ = size;
164
165 // Return the largest known size clamped to the maximum size (if valid).
166 if (max_size_.width() > 0)
167 size.set_width(std::min(max_size_.width(), size.width()));
168 if (max_size_.height() > 0)
169 size.set_height(std::min(max_size_.height(), size.height()));
170 return size;
171}
172
[email protected]fe22ba12013-02-21 13:43:48173std::string LabelButton::GetClassName() const {
174 return kViewClassName;
175}
176
[email protected]379bc982012-12-19 19:56:53177void LabelButton::ResetColorsFromNativeTheme() {
[email protected]204b2082012-11-07 17:53:03178 const ui::NativeTheme* theme = GetNativeTheme();
[email protected]fca8dc02012-11-14 16:25:56179 SkColor colors[STATE_COUNT] = {
[email protected]70e983cc2012-10-16 21:08:22180 theme->GetSystemColor(ui::NativeTheme::kColorId_TextButtonEnabledColor),
181 theme->GetSystemColor(ui::NativeTheme::kColorId_TextButtonHoverColor),
182 theme->GetSystemColor(ui::NativeTheme::kColorId_TextButtonHoverColor),
183 theme->GetSystemColor(ui::NativeTheme::kColorId_TextButtonDisabledColor),
184 };
[email protected]644f4fb2013-03-01 21:14:34185
186 // Certain styles do not change text color when hovered or pressed.
187 bool constant_text_color = style() == STYLE_BUTTON;
[email protected]70e983cc2012-10-16 21:08:22188#if defined(OS_WIN)
[email protected]644f4fb2013-03-01 21:14:34189 constant_text_color |= (style() == STYLE_NATIVE_TEXTBUTTON &&
190 theme == ui::NativeThemeWin::instance());
[email protected]70e983cc2012-10-16 21:08:22191#endif
[email protected]644f4fb2013-03-01 21:14:34192 if (constant_text_color)
193 colors[STATE_HOVERED] = colors[STATE_PRESSED] = colors[STATE_NORMAL];
194
[email protected]fca8dc02012-11-14 16:25:56195 for (size_t state = STATE_NORMAL; state < STATE_COUNT; ++state) {
[email protected]379bc982012-12-19 19:56:53196 if (!explicitly_set_colors_[state]) {
[email protected]204b2082012-11-07 17:53:03197 SetTextColor(static_cast<ButtonState>(state), colors[state]);
198 explicitly_set_colors_[state] = false;
199 }
200 }
[email protected]70e983cc2012-10-16 21:08:22201}
202
203void LabelButton::StateChanged() {
204 const gfx::Size previous_image_size(image_->GetPreferredSize());
205 image_->SetImage(GetImage(state()));
206 const SkColor color = button_state_colors_[state()];
[email protected]fca8dc02012-11-14 16:25:56207 if (state() != STATE_DISABLED && label_->enabled_color() != color)
[email protected]70e983cc2012-10-16 21:08:22208 label_->SetEnabledColor(color);
[email protected]4e88e352012-12-04 19:12:09209 label_->SetEnabled(state() != STATE_DISABLED);
[email protected]70e983cc2012-10-16 21:08:22210 if (image_->GetPreferredSize() != previous_image_size)
211 Layout();
212}
213
[email protected]70e983cc2012-10-16 21:08:22214void LabelButton::Layout() {
215 gfx::Rect child_area(GetLocalBounds());
216 child_area.Inset(GetInsets());
217
218 gfx::Size image_size(image_->GetPreferredSize());
219 image_size.set_width(std::min(image_size.width(), child_area.width()));
220 image_size.set_height(std::min(image_size.height(), child_area.height()));
221
[email protected]1c4f28f2012-10-18 23:36:06222 // The label takes any remaining width after sizing the image, unless both
223 // views are centered. In that case, using the tighter preferred label width
224 // avoids wasted space within the label that would look like awkward padding.
[email protected]70e983cc2012-10-16 21:08:22225 gfx::Size label_size(child_area.size());
226 if (!image_size.IsEmpty() && !label_size.IsEmpty()) {
[email protected]67a96b52013-01-11 05:08:56227 label_size.set_width(
228 std::max(child_area.width() - image_size.width() - kSpacing, 0));
[email protected]cc563f02012-11-07 17:37:36229 if (GetHorizontalAlignment() == gfx::ALIGN_CENTER) {
[email protected]379bc982012-12-19 19:56:53230 // Ensure multi-line labels paired with images use their available width.
231 if (GetTextMultiLine())
232 label_->SizeToFit(label_size.width());
233 label_size.set_width(
234 std::min(label_size.width(), label_->GetPreferredSize().width()));
[email protected]70e983cc2012-10-16 21:08:22235 }
236 }
237
238 gfx::Point image_origin(child_area.origin());
239 image_origin.Offset(0, (child_area.height() - image_size.height()) / 2);
[email protected]cc563f02012-11-07 17:37:36240 if (GetHorizontalAlignment() == gfx::ALIGN_CENTER) {
[email protected]70e983cc2012-10-16 21:08:22241 const int total_width = image_size.width() + label_size.width() +
242 ((image_size.width() > 0 && label_size.width() > 0) ? kSpacing : 0);
243 image_origin.Offset((child_area.width() - total_width) / 2, 0);
[email protected]cc563f02012-11-07 17:37:36244 } else if (GetHorizontalAlignment() == gfx::ALIGN_RIGHT) {
[email protected]70e983cc2012-10-16 21:08:22245 image_origin.Offset(child_area.width() - image_size.width(), 0);
246 }
247
248 gfx::Point label_origin(child_area.origin());
[email protected]cc563f02012-11-07 17:37:36249 if (!image_size.IsEmpty() && GetHorizontalAlignment() != gfx::ALIGN_RIGHT)
[email protected]70e983cc2012-10-16 21:08:22250 label_origin.set_x(image_origin.x() + image_size.width() + kSpacing);
251
252 image_->SetBoundsRect(gfx::Rect(image_origin, image_size));
253 label_->SetBoundsRect(gfx::Rect(label_origin, label_size));
254}
255
[email protected]70e983cc2012-10-16 21:08:22256void LabelButton::ChildPreferredSizeChanged(View* child) {
257 PreferredSizeChanged();
258}
259
[email protected]204b2082012-11-07 17:53:03260void LabelButton::OnNativeThemeChanged(const ui::NativeTheme* theme) {
[email protected]379bc982012-12-19 19:56:53261 ResetColorsFromNativeTheme();
[email protected]204b2082012-11-07 17:53:03262}
263
[email protected]70e983cc2012-10-16 21:08:22264ui::NativeTheme::Part LabelButton::GetThemePart() const {
265 return ui::NativeTheme::kPushButton;
266}
267
268gfx::Rect LabelButton::GetThemePaintRect() const {
269 return GetLocalBounds();
270}
271
272ui::NativeTheme::State LabelButton::GetThemeState(
273 ui::NativeTheme::ExtraParams* params) const {
274 GetExtraParams(params);
275 switch(state()) {
[email protected]fca8dc02012-11-14 16:25:56276 case STATE_NORMAL: return ui::NativeTheme::kNormal;
277 case STATE_HOVERED: return ui::NativeTheme::kHovered;
278 case STATE_PRESSED: return ui::NativeTheme::kPressed;
279 case STATE_DISABLED: return ui::NativeTheme::kDisabled;
280 case STATE_COUNT: NOTREACHED() << "Unknown state: " << state();
[email protected]70e983cc2012-10-16 21:08:22281 }
282 return ui::NativeTheme::kNormal;
283}
284
285const ui::Animation* LabelButton::GetThemeAnimation() const {
[email protected]204b2082012-11-07 17:53:03286#if defined(OS_WIN)
[email protected]fe22ba12013-02-21 13:43:48287 if (style() == STYLE_NATIVE_TEXTBUTTON &&
288 GetNativeTheme() == ui::NativeThemeWin::instance()) {
[email protected]204b2082012-11-07 17:53:03289 return ui::NativeThemeWin::instance()->IsThemingActive() ?
290 hover_animation_.get() : NULL;
291 }
[email protected]70e983cc2012-10-16 21:08:22292#endif
293 return hover_animation_.get();
294}
295
296ui::NativeTheme::State LabelButton::GetBackgroundThemeState(
297 ui::NativeTheme::ExtraParams* params) const {
298 GetExtraParams(params);
299 return ui::NativeTheme::kNormal;
300}
301
302ui::NativeTheme::State LabelButton::GetForegroundThemeState(
303 ui::NativeTheme::ExtraParams* params) const {
304 GetExtraParams(params);
305 return ui::NativeTheme::kHovered;
306}
307
308void LabelButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const {
309 params->button.checked = false;
310 params->button.indeterminate = false;
[email protected]e59fc52a2013-02-20 20:24:11311 params->button.is_default = is_default_;
[email protected]70e983cc2012-10-16 21:08:22312 params->button.is_focused = HasFocus() && IsAccessibilityFocusable();
[email protected]fe22ba12013-02-21 13:43:48313 params->button.has_border = style() == STYLE_NATIVE_TEXTBUTTON;
[email protected]70e983cc2012-10-16 21:08:22314 params->button.classic_state = 0;
[email protected]204b2082012-11-07 17:53:03315 params->button.background_color = GetNativeTheme()->GetSystemColor(
[email protected]70e983cc2012-10-16 21:08:22316 ui::NativeTheme::kColorId_TextButtonBackgroundColor);
317}
318
319} // namespace views