Home | History | Annotate | Download | only in caption_buttons
      1 // Copyright 2013 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 "ash/frame/caption_buttons/frame_caption_button.h"
      6 
      7 #include "ui/base/resource/resource_bundle.h"
      8 #include "ui/gfx/animation/slide_animation.h"
      9 #include "ui/gfx/animation/throb_animation.h"
     10 #include "ui/gfx/canvas.h"
     11 
     12 namespace ash {
     13 
     14 namespace {
     15 
     16 // The duration of the crossfade animation when swapping the button's images.
     17 const int kSwapImagesAnimationDurationMs = 200;
     18 
     19 // The duration of the fade out animation of the old icon during a crossfade
     20 // animation as a ratio of |kSwapImagesAnimationDurationMs|.
     21 const float kFadeOutRatio = 0.5f;
     22 
     23 }  // namespace
     24 
     25 // static
     26 const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton";
     27 
     28 FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener,
     29                                        CaptionButtonIcon icon)
     30     : CustomButton(listener),
     31       icon_(icon),
     32       paint_as_active_(false),
     33       icon_image_id_(-1),
     34       inactive_icon_image_id_(-1),
     35       hovered_background_image_id_(-1),
     36       pressed_background_image_id_(-1),
     37       swap_images_animation_(new gfx::SlideAnimation(this)) {
     38   swap_images_animation_->Reset(1);
     39 
     40   // Do not flip the gfx::Canvas passed to the OnPaint() method. The snap left
     41   // and snap right button icons should not be flipped. The other icons are
     42   // horizontally symmetrical.
     43 }
     44 
     45 FrameCaptionButton::~FrameCaptionButton() {
     46 }
     47 
     48 void FrameCaptionButton::SetImages(CaptionButtonIcon icon,
     49                                    Animate animate,
     50                                    int icon_image_id,
     51                                    int inactive_icon_image_id,
     52                                    int hovered_background_image_id,
     53                                    int pressed_background_image_id) {
     54   // The early return is dependant on |animate| because callers use SetImages()
     55   // with ANIMATE_NO to progress the crossfade animation to the end.
     56   if (icon == icon_ &&
     57       (animate == ANIMATE_YES || !swap_images_animation_->is_animating()) &&
     58       icon_image_id == icon_image_id_ &&
     59       inactive_icon_image_id == inactive_icon_image_id_ &&
     60       hovered_background_image_id == hovered_background_image_id_ &&
     61       pressed_background_image_id == pressed_background_image_id_) {
     62     return;
     63   }
     64 
     65   if (animate == ANIMATE_YES)
     66     crossfade_icon_image_ = GetIconImageToPaint();
     67 
     68   icon_ = icon;
     69   icon_image_id_ = icon_image_id;
     70   inactive_icon_image_id_ = inactive_icon_image_id;
     71   hovered_background_image_id_ = hovered_background_image_id;
     72   pressed_background_image_id_ = pressed_background_image_id;
     73 
     74   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
     75   icon_image_ = *rb.GetImageSkiaNamed(icon_image_id);
     76   inactive_icon_image_ = *rb.GetImageSkiaNamed(inactive_icon_image_id);
     77   hovered_background_image_ = *rb.GetImageSkiaNamed(
     78       hovered_background_image_id);
     79   pressed_background_image_ = *rb.GetImageSkiaNamed(
     80       pressed_background_image_id);
     81 
     82   if (animate == ANIMATE_YES) {
     83     swap_images_animation_->Reset(0);
     84     swap_images_animation_->SetSlideDuration(kSwapImagesAnimationDurationMs);
     85     swap_images_animation_->Show();
     86   } else {
     87     swap_images_animation_->Reset(1);
     88   }
     89   PreferredSizeChanged();
     90   SchedulePaint();
     91 }
     92 
     93 bool FrameCaptionButton::IsAnimatingImageSwap() const {
     94   return swap_images_animation_->is_animating();
     95 }
     96 
     97 gfx::Size FrameCaptionButton::GetPreferredSize() const {
     98   return hovered_background_image_.isNull() ?
     99       gfx::Size() : hovered_background_image_.size();
    100 }
    101 
    102 const char* FrameCaptionButton::GetClassName() const {
    103   return kViewClassName;
    104 }
    105 
    106 void FrameCaptionButton::OnPaint(gfx::Canvas* canvas) {
    107   if (hover_animation_->is_animating() || state() == STATE_HOVERED) {
    108     int hovered_background_alpha = hover_animation_->is_animating() ?
    109         hover_animation_->CurrentValueBetween(0, 255) : 255;
    110     SkPaint paint;
    111     paint.setAlpha(hovered_background_alpha);
    112     canvas->DrawImageInt(hovered_background_image_, 0, 0, paint);
    113   } else if (state() == STATE_PRESSED) {
    114     canvas->DrawImageInt(pressed_background_image_, 0, 0);
    115   }
    116 
    117   int icon_alpha = swap_images_animation_->CurrentValueBetween(0, 255);
    118   int crossfade_icon_alpha = 0;
    119   if (icon_alpha < static_cast<int>(kFadeOutRatio * 255))
    120      crossfade_icon_alpha = static_cast<int>(255 - icon_alpha / kFadeOutRatio);
    121 
    122   gfx::ImageSkia icon_image = GetIconImageToPaint();
    123   if (crossfade_icon_alpha > 0 && !crossfade_icon_image_.isNull()) {
    124     gfx::Canvas icon_canvas(icon_image.size(), canvas->image_scale(), false);
    125     SkPaint paint;
    126     paint.setAlpha(icon_alpha);
    127     icon_canvas.DrawImageInt(icon_image, 0, 0, paint);
    128 
    129     paint.setAlpha(crossfade_icon_alpha);
    130     paint.setXfermodeMode(SkXfermode::kPlus_Mode);
    131     icon_canvas.DrawImageInt(crossfade_icon_image_, 0, 0, paint);
    132 
    133     PaintCentered(canvas, gfx::ImageSkia(icon_canvas.ExtractImageRep()), 255);
    134   } else {
    135     PaintCentered(canvas, icon_image, icon_alpha);
    136   }
    137 }
    138 
    139 void FrameCaptionButton::OnGestureEvent(ui::GestureEvent* event) {
    140   // CustomButton does not become pressed when the user drags off and then back
    141   // onto the button. Make FrameCaptionButton pressed in this case because this
    142   // behavior is more consistent with AlternateFrameSizeButton.
    143   if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
    144       event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
    145     if (HitTestPoint(event->location())) {
    146       SetState(STATE_PRESSED);
    147       RequestFocus();
    148       event->StopPropagation();
    149     } else {
    150       SetState(STATE_NORMAL);
    151     }
    152   } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
    153     if (HitTestPoint(event->location())) {
    154       SetState(STATE_HOVERED);
    155       NotifyClick(*event);
    156       event->StopPropagation();
    157     }
    158   }
    159   CustomButton::OnGestureEvent(event);
    160 }
    161 
    162 const gfx::ImageSkia& FrameCaptionButton::GetIconImageToPaint() const {
    163   return paint_as_active_ ? icon_image_ : inactive_icon_image_;
    164 }
    165 
    166 void FrameCaptionButton::PaintCentered(gfx::Canvas* canvas,
    167                                        const gfx::ImageSkia& to_center,
    168                                        int alpha) {
    169   SkPaint paint;
    170   paint.setAlpha(alpha);
    171   canvas->DrawImageInt(to_center,
    172                        (width() - to_center.width()) / 2,
    173                        (height() - to_center.height()) / 2,
    174                        paint);
    175 }
    176 
    177 }  // namespace ash
    178