31a239690e7c9f6c04fe7136c186d124c1f6f77a
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / libgtk2ui / gtk2_border.cc
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.
4
5 #include "chrome/browser/ui/libgtk2ui/gtk2_border.h"
6
7 #include <gtk/gtk.h>
8
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"
20
21 using views::Button;
22 using views::NativeThemeDelegate;
23
24 namespace libgtk2ui {
25
26 namespace {
27
28 const int kNumberOfFocusedStates = 2;
29
30 GtkStateType GetGtkState(ui::NativeTheme::State state) {
31   switch (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;
37   }
38   return GTK_STATE_NORMAL;
39 }
40
41 class ButtonImageSkiaSource : public gfx::ImageSkiaSource {
42  public:
43   ButtonImageSkiaSource(const Gtk2UI* gtk2_ui,
44                         const GtkStateType state,
45                         const bool focused,
46                         const gfx::Size& size)
47       : gtk2_ui_(gtk2_ui),
48         state_(state),
49         focused_(focused),
50         size_(size) {
51   }
52
53   virtual ~ButtonImageSkiaSource() {
54   }
55
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);
61   }
62
63  private:
64   const Gtk2UI* gtk2_ui_;
65   const GtkStateType state_;
66   const bool focused_;
67   const gfx::Size size_;
68
69   DISALLOW_COPY_AND_ASSIGN(ButtonImageSkiaSource);
70 };
71
72 }  // namespace
73
74 Gtk2Border::Gtk2Border(Gtk2UI* gtk2_ui,
75                        views::LabelButton* owning_button)
76     : gtk2_ui_(gtk2_ui),
77       owning_button_(owning_button),
78       observer_manager_(this) {
79   observer_manager_.Add(NativeThemeGtk2::instance());
80 }
81
82 Gtk2Border::~Gtk2Border() {
83 }
84
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);
91
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);
99
100     SkPaint paint;
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();
108
109     canvas->sk_canvas()->restore();
110   } else {
111     PaintState(state, extra, rect, canvas);
112   }
113 }
114
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();
119 }
120
121 gfx::Size Gtk2Border::GetMinimumSize() const {
122   gfx::Insets insets = GetInsets();
123   return gfx::Size(insets.width(), insets.height());
124 }
125
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();
131     }
132   }
133
134   // Our owning view must have its layout invalidated because the insets could
135   // have changed.
136   owning_button_->InvalidateLayout();
137 }
138
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);
145
146   if (ShouldDrawBorder(focused, views_state)) {
147     gfx::ImageSkia* image = &button_images_[focused][views_state];
148
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()),
153           rect.size());
154     }
155     canvas->DrawImageInt(*image, rect.x(), rect.y());
156   }
157 }
158
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) {
163     return true;
164   } else if (owning_button_->style() == Button::STYLE_TEXTBUTTON) {
165     return focused == false && (state == Button::STATE_HOVERED ||
166                                 state == Button::STATE_PRESSED);
167   }
168
169   return false;
170 }
171
172 }  // namespace libgtk2ui