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/gtk2_util.h" 11 #include "chrome/browser/ui/libgtk2ui/native_theme_gtk2.h" 12 #include "third_party/skia/include/effects/SkLerpXfermode.h" 13 #include "ui/base/theme_provider.h" 14 #include "ui/gfx/animation/animation.h" 15 #include "ui/gfx/canvas.h" 16 #include "ui/gfx/image/image_skia_source.h" 17 #include "ui/gfx/rect.h" 18 #include "ui/gfx/skia_util.h" 19 #include "ui/views/controls/button/blue_button.h" 20 #include "ui/views/controls/button/label_button.h" 21 #include "ui/views/controls/button/label_button_border.h" 22 #include "ui/views/native_theme_delegate.h" 23 24 using views::Button; 25 using views::NativeThemeDelegate; 26 27 namespace libgtk2ui { 28 29 namespace { 30 31 const int kNumberOfFocusedStates = 2; 32 33 class ButtonImageSkiaSource : public gfx::ImageSkiaSource { 34 public: 35 ButtonImageSkiaSource(const Gtk2UI* gtk2_ui, 36 const GtkStateType state, 37 const bool focused, 38 const bool call_to_action, 39 const gfx::Size& size) 40 : gtk2_ui_(gtk2_ui), 41 state_(state), 42 focused_(focused), 43 call_to_action_(call_to_action), 44 size_(size) { 45 } 46 47 virtual ~ButtonImageSkiaSource() { 48 } 49 50 virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 51 int w = size_.width() * scale; 52 int h = size_.height() * scale; 53 return gfx::ImageSkiaRep( 54 gtk2_ui_->DrawGtkButtonBorder(state_, focused_, call_to_action_, w, h), 55 scale); 56 } 57 58 private: 59 const Gtk2UI* gtk2_ui_; 60 const GtkStateType state_; 61 const bool focused_; 62 const bool call_to_action_; 63 const gfx::Size size_; 64 65 DISALLOW_COPY_AND_ASSIGN(ButtonImageSkiaSource); 66 }; 67 68 } // namespace 69 70 Gtk2Border::Gtk2Border(Gtk2UI* gtk2_ui, 71 views::LabelButton* owning_button, 72 scoped_ptr<views::LabelButtonBorder> border) 73 : gtk2_ui_(gtk2_ui), 74 owning_button_(owning_button), 75 border_(border.Pass()), 76 observer_manager_(this) { 77 observer_manager_.Add(NativeThemeGtk2::instance()); 78 } 79 80 Gtk2Border::~Gtk2Border() { 81 } 82 83 void Gtk2Border::Paint(const views::View& view, gfx::Canvas* canvas) { 84 DCHECK_EQ(&view, owning_button_); 85 const NativeThemeDelegate* native_theme_delegate = owning_button_; 86 gfx::Rect rect(native_theme_delegate->GetThemePaintRect()); 87 ui::NativeTheme::ExtraParams extra; 88 ui::NativeTheme::State state = native_theme_delegate->GetThemeState(&extra); 89 90 const gfx::Animation* animation = native_theme_delegate->GetThemeAnimation(); 91 if (animation && animation->is_animating()) { 92 // Linearly interpolate background and foreground painters during animation. 93 const SkRect sk_rect = gfx::RectToSkRect(rect); 94 canvas->sk_canvas()->saveLayer(&sk_rect, NULL); 95 state = native_theme_delegate->GetBackgroundThemeState(&extra); 96 PaintState(state, extra, rect, canvas); 97 98 SkPaint paint; 99 skia::RefPtr<SkXfermode> sk_lerp_xfer = 100 skia::AdoptRef(SkLerpXfermode::Create(animation->GetCurrentValue())); 101 paint.setXfermode(sk_lerp_xfer.get()); 102 canvas->sk_canvas()->saveLayer(&sk_rect, &paint); 103 state = native_theme_delegate->GetForegroundThemeState(&extra); 104 PaintState(state, extra, rect, canvas); 105 canvas->sk_canvas()->restore(); 106 107 canvas->sk_canvas()->restore(); 108 } else { 109 PaintState(state, extra, rect, canvas); 110 } 111 } 112 113 gfx::Insets Gtk2Border::GetInsets() const { 114 return border_->GetInsets(); 115 } 116 117 gfx::Size Gtk2Border::GetMinimumSize() const { 118 return border_->GetMinimumSize(); 119 } 120 121 void Gtk2Border::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { 122 DCHECK_EQ(observed_theme, NativeThemeGtk2::instance()); 123 for (int i = 0; i < kNumberOfFocusedStates; ++i) { 124 for (int j = 0; j < views::Button::STATE_COUNT; ++j) { 125 button_images_[i][j] = gfx::ImageSkia(); 126 } 127 } 128 129 // Our owning view must have its layout invalidated because the insets could 130 // have changed. 131 owning_button_->InvalidateLayout(); 132 } 133 134 void Gtk2Border::PaintState(const ui::NativeTheme::State state, 135 const ui::NativeTheme::ExtraParams& extra, 136 const gfx::Rect& rect, 137 gfx::Canvas* canvas) { 138 bool focused = extra.button.is_focused; 139 Button::ButtonState views_state = Button::GetButtonStateFrom(state); 140 141 if (border_->GetPainter(focused, views_state) || 142 (focused && border_->GetPainter(false, views_state))) { 143 gfx::ImageSkia* image = &button_images_[focused][views_state]; 144 145 if (image->isNull() || image->size() != rect.size()) { 146 bool call_to_action = owning_button_->GetClassName() == 147 views::BlueButton::kViewClassName; 148 GtkStateType gtk_state = GetGtkState(state); 149 *image = gfx::ImageSkia( 150 new ButtonImageSkiaSource(gtk2_ui_, 151 gtk_state, 152 focused, 153 call_to_action, 154 rect.size()), 155 rect.size()); 156 } 157 canvas->DrawImageInt(*image, rect.x(), rect.y()); 158 } 159 } 160 161 } // namespace libgtk2ui 162