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_border.h" 6 7#include "base/logging.h" 8#include "grit/ui_resources.h" 9#include "third_party/skia/include/core/SkPaint.h" 10#include "third_party/skia/include/effects/SkLerpXfermode.h" 11#include "ui/base/resource/resource_bundle.h" 12#include "ui/gfx/animation/animation.h" 13#include "ui/gfx/canvas.h" 14#include "ui/gfx/rect.h" 15#include "ui/gfx/skia_util.h" 16#include "ui/gfx/sys_color_change_listener.h" 17#include "ui/native_theme/native_theme.h" 18#include "ui/views/border.h" 19#include "ui/views/controls/button/label_button.h" 20#include "ui/views/native_theme_delegate.h" 21 22namespace views { 23 24namespace { 25 26// Insets for the unified button images. This assumes that the images 27// are of a 9 grid, of 5x5 size each. 28const int kButtonInsets = 5; 29 30// The text-button hot and pushed image IDs; normal is unadorned by default. 31const int kTextHoveredImages[] = IMAGE_GRID(IDR_TEXTBUTTON_HOVER); 32const int kTextPressedImages[] = IMAGE_GRID(IDR_TEXTBUTTON_PRESSED); 33 34Button::ButtonState GetButtonState(ui::NativeTheme::State state) { 35 switch (state) { 36 case ui::NativeTheme::kDisabled: return Button::STATE_DISABLED; 37 case ui::NativeTheme::kHovered: return Button::STATE_HOVERED; 38 case ui::NativeTheme::kNormal: return Button::STATE_NORMAL; 39 case ui::NativeTheme::kPressed: return Button::STATE_PRESSED; 40 case ui::NativeTheme::kMaxState: NOTREACHED() << "Unknown state: " << state; 41 } 42 return Button::STATE_NORMAL; 43} 44 45// A helper function to paint the native theme or images as appropriate. 46void PaintHelper(LabelButtonBorder* border, 47 gfx::Canvas* canvas, 48 const ui::NativeTheme* theme, 49 ui::NativeTheme::Part part, 50 ui::NativeTheme::State state, 51 const gfx::Rect& rect, 52 const ui::NativeTheme::ExtraParams& extra) { 53 if (border->style() == Button::STYLE_NATIVE_TEXTBUTTON) { 54 theme->Paint(canvas->sk_canvas(), part, state, rect, extra); 55 } else { 56 Painter* painter = 57 border->GetPainter(extra.button.is_focused, GetButtonState(state)); 58 // Paint any corresponding unfocused painter if there is no focused painter. 59 if (!painter && extra.button.is_focused) 60 painter = border->GetPainter(false, GetButtonState(state)); 61 if (painter) 62 Painter::PaintPainterAt(canvas, painter, rect); 63 } 64} 65 66} // namespace 67 68LabelButtonBorder::LabelButtonBorder(Button::ButtonStyle style) 69 : style_(style) { 70 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 71 const gfx::Insets insets(kButtonInsets, 72 kButtonInsets, 73 kButtonInsets, 74 kButtonInsets); 75 76 if (style == Button::STYLE_BUTTON) { 77 set_insets(gfx::Insets(8, 13, 8, 13)); 78 SetPainter(false, Button::STATE_NORMAL, 79 Painter::CreateImagePainter( 80 *rb.GetImageSkiaNamed(IDR_BUTTON_NORMAL), insets)); 81 SetPainter(false, Button::STATE_HOVERED, 82 Painter::CreateImagePainter( 83 *rb.GetImageSkiaNamed(IDR_BUTTON_HOVER), insets)); 84 SetPainter(false, Button::STATE_PRESSED, 85 Painter::CreateImagePainter( 86 *rb.GetImageSkiaNamed(IDR_BUTTON_PRESSED), insets)); 87 SetPainter(false, Button::STATE_DISABLED, 88 Painter::CreateImagePainter( 89 *rb.GetImageSkiaNamed(IDR_BUTTON_DISABLED), insets)); 90 SetPainter(true, Button::STATE_NORMAL, 91 Painter::CreateImagePainter( 92 *rb.GetImageSkiaNamed(IDR_BUTTON_FOCUSED_NORMAL), insets)); 93 SetPainter(true, Button::STATE_HOVERED, 94 Painter::CreateImagePainter( 95 *rb.GetImageSkiaNamed(IDR_BUTTON_FOCUSED_HOVER), insets)); 96 SetPainter(true, Button::STATE_PRESSED, 97 Painter::CreateImagePainter( 98 *rb.GetImageSkiaNamed(IDR_BUTTON_FOCUSED_PRESSED), insets)); 99 SetPainter(true, Button::STATE_DISABLED, 100 Painter::CreateImagePainter( 101 *rb.GetImageSkiaNamed(IDR_BUTTON_DISABLED), insets)); 102 } else if (style == Button::STYLE_TEXTBUTTON) { 103 set_insets(gfx::Insets(5, 6, 5, 6)); 104 SetPainter(false, Button::STATE_HOVERED, 105 Painter::CreateImageGridPainter(kTextHoveredImages)); 106 SetPainter(false, Button::STATE_PRESSED, 107 Painter::CreateImageGridPainter(kTextPressedImages)); 108 } else if (style == Button::STYLE_NATIVE_TEXTBUTTON) { 109 set_insets(gfx::Insets(5, 12, 5, 12)); 110 } 111} 112 113LabelButtonBorder::~LabelButtonBorder() {} 114 115void LabelButtonBorder::Paint(const View& view, gfx::Canvas* canvas) { 116 const NativeThemeDelegate* native_theme_delegate = 117 static_cast<const LabelButton*>(&view); 118 ui::NativeTheme::Part part = native_theme_delegate->GetThemePart(); 119 gfx::Rect rect(native_theme_delegate->GetThemePaintRect()); 120 ui::NativeTheme::ExtraParams extra; 121 const ui::NativeTheme* theme = view.GetNativeTheme(); 122 const gfx::Animation* animation = native_theme_delegate->GetThemeAnimation(); 123 ui::NativeTheme::State state = native_theme_delegate->GetThemeState(&extra); 124 125 if (animation && animation->is_animating()) { 126 // Linearly interpolate background and foreground painters during animation. 127 const SkRect sk_rect = gfx::RectToSkRect(rect); 128 canvas->sk_canvas()->saveLayer(&sk_rect, NULL); 129 state = native_theme_delegate->GetBackgroundThemeState(&extra); 130 PaintHelper(this, canvas, theme, part, state, rect, extra); 131 132 SkPaint paint; 133 skia::RefPtr<SkXfermode> sk_lerp_xfer = 134 skia::AdoptRef(SkLerpXfermode::Create(animation->GetCurrentValue())); 135 paint.setXfermode(sk_lerp_xfer.get()); 136 canvas->sk_canvas()->saveLayer(&sk_rect, &paint); 137 state = native_theme_delegate->GetForegroundThemeState(&extra); 138 PaintHelper(this, canvas, theme, part, state, rect, extra); 139 canvas->sk_canvas()->restore(); 140 141 canvas->sk_canvas()->restore(); 142 } else { 143 PaintHelper(this, canvas, theme, part, state, rect, extra); 144 } 145 146 // For inverted color schemes, draw a solid fill with the button color. 147 if (gfx::IsInvertedColorScheme()) { 148 rect.Inset(insets_); 149 canvas->FillRect(rect, extra.button.background_color); 150 } 151} 152 153gfx::Insets LabelButtonBorder::GetInsets() const { 154 return insets_; 155} 156 157gfx::Size LabelButtonBorder::GetMinimumSize() const { 158 gfx::Size minimum_size; 159 for (int i = 0; i < 2; ++i) { 160 for (int j = 0; j < Button::STATE_COUNT; ++j) { 161 if (painters_[i][j]) 162 minimum_size.SetToMax(painters_[i][j]->GetMinimumSize()); 163 } 164 } 165 return minimum_size; 166} 167 168Painter* LabelButtonBorder::GetPainter(bool focused, 169 Button::ButtonState state) { 170 return painters_[focused ? 1 : 0][state].get(); 171} 172 173void LabelButtonBorder::SetPainter(bool focused, 174 Button::ButtonState state, 175 Painter* painter) { 176 painters_[focused ? 1 : 0][state].reset(painter); 177} 178 179} // namespace views 180