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 12namespace ash { 13 14namespace { 15 16// The duration of the crossfade animation when swapping the button's images. 17const 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|. 21const float kFadeOutRatio = 0.5f; 22 23} // namespace 24 25// static 26const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton"; 27 28FrameCaptionButton::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 45FrameCaptionButton::~FrameCaptionButton() { 46} 47 48void 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 93bool FrameCaptionButton::IsAnimatingImageSwap() const { 94 return swap_images_animation_->is_animating(); 95} 96 97gfx::Size FrameCaptionButton::GetPreferredSize() const { 98 return hovered_background_image_.isNull() ? 99 gfx::Size() : hovered_background_image_.size(); 100} 101 102const char* FrameCaptionButton::GetClassName() const { 103 return kViewClassName; 104} 105 106void 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 139void 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 162const gfx::ImageSkia& FrameCaptionButton::GetIconImageToPaint() const { 163 return paint_as_active_ ? icon_image_ : inactive_icon_image_; 164} 165 166void 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