// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/libgtk2ui/gtk2_border.h" #include #include "chrome/browser/ui/libgtk2ui/gtk2_ui.h" #include "chrome/browser/ui/libgtk2ui/gtk2_util.h" #include "chrome/browser/ui/libgtk2ui/native_theme_gtk2.h" #include "third_party/skia/include/effects/SkLerpXfermode.h" #include "ui/base/theme_provider.h" #include "ui/gfx/animation/animation.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/skia_util.h" #include "ui/views/controls/button/blue_button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/label_button_border.h" #include "ui/views/native_theme_delegate.h" using views::Button; using views::NativeThemeDelegate; namespace libgtk2ui { namespace { const int kNumberOfFocusedStates = 2; class ButtonImageSkiaSource : public gfx::ImageSkiaSource { public: ButtonImageSkiaSource(const Gtk2UI* gtk2_ui, const GtkStateType state, const bool focused, const bool call_to_action, const gfx::Size& size) : gtk2_ui_(gtk2_ui), state_(state), focused_(focused), call_to_action_(call_to_action), size_(size) { } ~ButtonImageSkiaSource() override {} gfx::ImageSkiaRep GetImageForScale(float scale) override { int w = size_.width() * scale; int h = size_.height() * scale; return gfx::ImageSkiaRep( gtk2_ui_->DrawGtkButtonBorder(state_, focused_, call_to_action_, w, h), scale); } private: const Gtk2UI* gtk2_ui_; const GtkStateType state_; const bool focused_; const bool call_to_action_; const gfx::Size size_; DISALLOW_COPY_AND_ASSIGN(ButtonImageSkiaSource); }; } // namespace Gtk2Border::Gtk2Border(Gtk2UI* gtk2_ui, views::LabelButton* owning_button, scoped_ptr border) : gtk2_ui_(gtk2_ui), owning_button_(owning_button), border_(border.Pass()), observer_manager_(this) { observer_manager_.Add(NativeThemeGtk2::instance()); } Gtk2Border::~Gtk2Border() { } void Gtk2Border::Paint(const views::View& view, gfx::Canvas* canvas) { DCHECK_EQ(&view, owning_button_); const NativeThemeDelegate* native_theme_delegate = owning_button_; gfx::Rect rect(native_theme_delegate->GetThemePaintRect()); ui::NativeTheme::ExtraParams extra; ui::NativeTheme::State state = native_theme_delegate->GetThemeState(&extra); const gfx::Animation* animation = native_theme_delegate->GetThemeAnimation(); if (animation && animation->is_animating()) { // Linearly interpolate background and foreground painters during animation. const SkRect sk_rect = gfx::RectToSkRect(rect); canvas->sk_canvas()->saveLayer(&sk_rect, NULL); state = native_theme_delegate->GetBackgroundThemeState(&extra); PaintState(state, extra, rect, canvas); SkPaint paint; skia::RefPtr sk_lerp_xfer = skia::AdoptRef(SkLerpXfermode::Create(animation->GetCurrentValue())); paint.setXfermode(sk_lerp_xfer.get()); canvas->sk_canvas()->saveLayer(&sk_rect, &paint); state = native_theme_delegate->GetForegroundThemeState(&extra); PaintState(state, extra, rect, canvas); canvas->sk_canvas()->restore(); canvas->sk_canvas()->restore(); } else { PaintState(state, extra, rect, canvas); } } gfx::Insets Gtk2Border::GetInsets() const { return border_->GetInsets(); } gfx::Size Gtk2Border::GetMinimumSize() const { return border_->GetMinimumSize(); } void Gtk2Border::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { DCHECK_EQ(observed_theme, NativeThemeGtk2::instance()); for (int i = 0; i < kNumberOfFocusedStates; ++i) { for (int j = 0; j < views::Button::STATE_COUNT; ++j) { button_images_[i][j] = gfx::ImageSkia(); } } // Our owning view must have its layout invalidated because the insets could // have changed. owning_button_->InvalidateLayout(); } void Gtk2Border::PaintState(const ui::NativeTheme::State state, const ui::NativeTheme::ExtraParams& extra, const gfx::Rect& rect, gfx::Canvas* canvas) { bool focused = extra.button.is_focused; Button::ButtonState views_state = Button::GetButtonStateFrom(state); if (border_->GetPainter(focused, views_state) || (focused && border_->GetPainter(false, views_state))) { gfx::ImageSkia* image = &button_images_[focused][views_state]; if (image->isNull() || image->size() != rect.size()) { bool call_to_action = owning_button_->GetClassName() == views::BlueButton::kViewClassName; GtkStateType gtk_state = GetGtkState(state); *image = gfx::ImageSkia( new ButtonImageSkiaSource(gtk2_ui_, gtk_state, focused, call_to_action, rect.size()), rect.size()); } canvas->DrawImageInt(*image, rect.x(), rect.y()); } } } // namespace libgtk2ui