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/wm/caption_buttons/frame_caption_button.h"
6
7#include "grit/ash_resources.h"
8#include "ui/base/resource/resource_bundle.h"
9#include "ui/gfx/animation/slide_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 icon.
17const int kCrossfadeDurationMs = 200;
18
19}  // namespace
20
21// static
22const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton";
23
24FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener,
25                                       CaptionButtonIcon icon)
26    : ImageButton(listener),
27      icon_(icon),
28      style_(STYLE_SHORT_RESTORED),
29      last_paint_scale_(1.0f),
30      animation_(new gfx::SlideAnimation(this)) {
31  animation_->Reset(1);
32  UpdateImages();
33}
34
35FrameCaptionButton::~FrameCaptionButton() {
36}
37
38void FrameCaptionButton::SetIcon(CaptionButtonIcon icon, Animate animate) {
39  if (icon_ == icon)
40    return;
41
42  if (animate == ANIMATE_YES) {
43    gfx::Canvas canvas(size(), last_paint_scale_, false);
44    OnPaint(&canvas);
45    crossfade_image_ = gfx::ImageSkia(canvas.ExtractImageRep());
46
47    icon_ = icon;
48    UpdateImages();
49
50    animation_->Reset(0);
51    animation_->SetSlideDuration(kCrossfadeDurationMs);
52    animation_->Show();
53  } else {
54    animation_->Reset(1);
55    icon_ = icon;
56    UpdateImages();
57  }
58}
59
60void FrameCaptionButton::SetStyle(Style style) {
61  if (style_ == style)
62    return;
63  animation_->Reset(1);
64  style_ = style;
65  UpdateImages();
66}
67
68const char* FrameCaptionButton::GetClassName() const {
69  return kViewClassName;
70}
71
72void FrameCaptionButton::OnPaint(gfx::Canvas* canvas) {
73  last_paint_scale_ = canvas->image_scale();
74  int alpha = static_cast<int>(animation_->GetCurrentValue() * 255);
75  int crossfade_alpha = 255 - alpha;
76  if (crossfade_alpha > 0 && !crossfade_image_.isNull()) {
77    gfx::Canvas composed_canvas(size(), last_paint_scale_, false);
78    SkPaint paint;
79    paint.setAlpha(crossfade_alpha);
80    paint.setXfermodeMode(SkXfermode::kPlus_Mode);
81    composed_canvas.DrawImageInt(crossfade_image_, 0, 0, paint);
82    paint.setAlpha(alpha);
83    ImageButton::OnPaint(&composed_canvas);
84
85    canvas->DrawImageInt(
86        gfx::ImageSkia(composed_canvas.ExtractImageRep()), 0, 0);
87  } else {
88    ImageButton::OnPaint(canvas);
89  }
90}
91
92void FrameCaptionButton::OnGestureEvent(ui::GestureEvent* event) {
93  // ImageButton does not become pressed when the user drags off and then back
94  // onto the button. Make FrameCaptionButton pressed in this case because this
95  // behavior is more consistent with AlternateFrameSizeButton.
96  if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
97      event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
98    if (HitTestPoint(event->location())) {
99      SetState(STATE_PRESSED);
100      RequestFocus();
101      event->StopPropagation();
102    } else {
103      SetState(STATE_NORMAL);
104    }
105  } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
106    if (HitTestPoint(event->location())) {
107      SetState(STATE_HOVERED);
108      NotifyClick(*event);
109      event->StopPropagation();
110    }
111  }
112  ImageButton::OnGestureEvent(event);
113}
114
115void FrameCaptionButton::StateChanged() {
116  if (state_ == STATE_HOVERED || state_ == STATE_PRESSED)
117    animation_->Reset(1);
118}
119
120void FrameCaptionButton::UpdateImages() {
121  switch (icon_) {
122    case CAPTION_BUTTON_ICON_MINIMIZE:
123      SetImages(IDR_AURA_WINDOW_MINIMIZE_SHORT,
124                IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
125                IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
126      break;
127   case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
128   case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
129   case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
130     if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) {
131       SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
132                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
133                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
134     } else if (style_ == STYLE_SHORT_RESTORED) {
135       SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
136                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
137                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
138     } else {
139       SetImages(IDR_AURA_WINDOW_MAXIMIZE,
140                 IDR_AURA_WINDOW_MAXIMIZE_H,
141                 IDR_AURA_WINDOW_MAXIMIZE_P);
142     }
143     break;
144   case CAPTION_BUTTON_ICON_CLOSE:
145     if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) {
146       SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
147                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
148                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
149     } else if (style_ == STYLE_SHORT_RESTORED) {
150       SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
151                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
152                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
153     } else {
154       SetImages(IDR_AURA_WINDOW_CLOSE,
155                 IDR_AURA_WINDOW_CLOSE_H,
156                 IDR_AURA_WINDOW_CLOSE_P);
157     }
158     break;
159  }
160
161  SchedulePaint();
162}
163
164void FrameCaptionButton::SetImages(int normal_image_id,
165                                   int hot_image_id,
166                                   int pushed_image_id) {
167  ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
168  SetImage(STATE_NORMAL, resource_bundle.GetImageSkiaNamed(normal_image_id));
169  SetImage(STATE_HOVERED, resource_bundle.GetImageSkiaNamed(hot_image_id));
170  SetImage(STATE_PRESSED, resource_bundle.GetImageSkiaNamed(pushed_image_id));
171}
172
173void FrameCaptionButton::AnimationProgressed(const gfx::Animation* animation) {
174  SchedulePaint();
175}
176
177}  // namespace ash
178