1 // Copyright 2014 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.
5 #include "chrome/browser/ui/libgtk2ui/gtk2_border.h"
9 #include "chrome/browser/ui/libgtk2ui/gtk2_ui.h"
10 #include "chrome/browser/ui/libgtk2ui/native_theme_gtk2.h"
11 #include "third_party/skia/include/effects/SkLerpXfermode.h"
12 #include "ui/base/theme_provider.h"
13 #include "ui/gfx/animation/animation.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/gfx/image/image_skia_source.h"
16 #include "ui/gfx/rect.h"
17 #include "ui/gfx/skia_util.h"
18 #include "ui/views/controls/button/label_button.h"
19 #include "ui/views/native_theme_delegate.h"
22 using views::NativeThemeDelegate
;
28 const int kNumberOfFocusedStates
= 2;
30 GtkStateType
GetGtkState(ui::NativeTheme::State state
) {
32 case ui::NativeTheme::kDisabled
: return GTK_STATE_INSENSITIVE
;
33 case ui::NativeTheme::kHovered
: return GTK_STATE_PRELIGHT
;
34 case ui::NativeTheme::kNormal
: return GTK_STATE_NORMAL
;
35 case ui::NativeTheme::kPressed
: return GTK_STATE_ACTIVE
;
36 case ui::NativeTheme::kMaxState
: NOTREACHED() << "Unknown state: " << state
;
38 return GTK_STATE_NORMAL
;
41 class ButtonImageSkiaSource
: public gfx::ImageSkiaSource
{
43 ButtonImageSkiaSource(const Gtk2UI
* gtk2_ui
,
44 const GtkStateType state
,
46 const gfx::Size
& size
)
53 virtual ~ButtonImageSkiaSource() {
56 virtual gfx::ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
57 int w
= size_
.width() * scale
;
58 int h
= size_
.height() * scale
;
59 return gfx::ImageSkiaRep(
60 gtk2_ui_
->DrawGtkButtonBorder(state_
, focused_
, w
, h
), scale
);
64 const Gtk2UI
* gtk2_ui_
;
65 const GtkStateType state_
;
67 const gfx::Size size_
;
69 DISALLOW_COPY_AND_ASSIGN(ButtonImageSkiaSource
);
74 Gtk2Border::Gtk2Border(Gtk2UI
* gtk2_ui
,
75 views::LabelButton
* owning_button
)
77 owning_button_(owning_button
),
78 observer_manager_(this) {
79 observer_manager_
.Add(NativeThemeGtk2::instance());
82 Gtk2Border::~Gtk2Border() {
85 void Gtk2Border::Paint(const views::View
& view
, gfx::Canvas
* canvas
) {
86 DCHECK_EQ(&view
, owning_button_
);
87 const NativeThemeDelegate
* native_theme_delegate
= owning_button_
;
88 gfx::Rect
rect(native_theme_delegate
->GetThemePaintRect());
89 ui::NativeTheme::ExtraParams extra
;
90 ui::NativeTheme::State state
= native_theme_delegate
->GetThemeState(&extra
);
92 const gfx::Animation
* animation
= native_theme_delegate
->GetThemeAnimation();
93 if (animation
&& animation
->is_animating()) {
94 // Linearly interpolate background and foreground painters during animation.
95 const SkRect sk_rect
= gfx::RectToSkRect(rect
);
96 canvas
->sk_canvas()->saveLayer(&sk_rect
, NULL
);
97 state
= native_theme_delegate
->GetBackgroundThemeState(&extra
);
98 PaintState(state
, extra
, rect
, canvas
);
101 skia::RefPtr
<SkXfermode
> sk_lerp_xfer
=
102 skia::AdoptRef(SkLerpXfermode::Create(animation
->GetCurrentValue()));
103 paint
.setXfermode(sk_lerp_xfer
.get());
104 canvas
->sk_canvas()->saveLayer(&sk_rect
, &paint
);
105 state
= native_theme_delegate
->GetForegroundThemeState(&extra
);
106 PaintState(state
, extra
, rect
, canvas
);
107 canvas
->sk_canvas()->restore();
109 canvas
->sk_canvas()->restore();
111 PaintState(state
, extra
, rect
, canvas
);
115 gfx::Insets
Gtk2Border::GetInsets() const {
116 // On STYLE_TEXTUBTTON, we want the smaller insets so we can fit the GTK icon
117 // in the toolbar without cutting off the edges of the GTK image.
118 return gtk2_ui_
->GetButtonInsets();
121 gfx::Size
Gtk2Border::GetMinimumSize() const {
122 gfx::Insets insets
= GetInsets();
123 return gfx::Size(insets
.width(), insets
.height());
126 void Gtk2Border::OnNativeThemeUpdated(ui::NativeTheme
* observed_theme
) {
127 DCHECK_EQ(observed_theme
, NativeThemeGtk2::instance());
128 for (int i
= 0; i
< kNumberOfFocusedStates
; ++i
) {
129 for (int j
= 0; j
< views::Button::STATE_COUNT
; ++j
) {
130 button_images_
[i
][j
] = gfx::ImageSkia();
134 // Our owning view must have its layout invalidated because the insets could
136 owning_button_
->InvalidateLayout();
139 void Gtk2Border::PaintState(const ui::NativeTheme::State state
,
140 const ui::NativeTheme::ExtraParams
& extra
,
141 const gfx::Rect
& rect
,
142 gfx::Canvas
* canvas
) {
143 bool focused
= extra
.button
.is_focused
;
144 Button::ButtonState views_state
= Button::GetButtonStateFrom(state
);
146 if (ShouldDrawBorder(focused
, views_state
)) {
147 gfx::ImageSkia
* image
= &button_images_
[focused
][views_state
];
149 if (image
->isNull() || image
->size() != rect
.size()) {
150 GtkStateType gtk_state
= GetGtkState(state
);
151 *image
= gfx::ImageSkia(
152 new ButtonImageSkiaSource(gtk2_ui_
, gtk_state
, focused
, rect
.size()),
155 canvas
->DrawImageInt(*image
, rect
.x(), rect
.y());
159 bool Gtk2Border::ShouldDrawBorder(bool focused
,
160 views::Button::ButtonState state
) {
161 // This logic should be kept in sync with the LabelButtonBorder constructor.
162 if (owning_button_
->style() == Button::STYLE_BUTTON
) {
164 } else if (owning_button_
->style() == Button::STYLE_TEXTBUTTON
) {
165 return focused
== false && (state
== Button::STATE_HOVERED
||
166 state
== Button::STATE_PRESSED
);
172 } // namespace libgtk2ui