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.
5 #include "ash/wm/caption_buttons/alternate_frame_caption_button.h"
7 #include "ui/gfx/animation/slide_animation.h"
8 #include "ui/gfx/canvas.h"
14 // The width and height of the region of the button which does not overlap
15 // with other buttons.
18 // A bubble is painted in the background when the mouse button is pressed.
19 // When the button is pressed:
20 // - The bubble is faded in from opacity 0 to |kShownBubbleOpacity|.
21 // - The bubble is expanded from |kInitialGrowBubbleRadius| to
22 // |kFullyGrownBubbleRadius|.
23 // When the button is unpressed (STATE_NORMAL)
24 // - The bubble is faded out from its current opacity back to 0.
25 // - The bubble is further expanded from its current radius to
26 // |kFinalBurstBubbleRadius|.
27 const int kInitialGrowBubbleRadius = 16;
28 const int kFullyGrownBubbleRadius = 22;
29 const int kFinalBurstBubbleRadius = 26;
30 const int kShownBubbleOpacity = 100;
32 // The duraton of the animations for hiding and showing the bubble.
33 const int kBubbleAnimationDuration = 100;
35 // TODO(pkotwicz): Replace these colors with colors from UX.
36 const SkColor kPathColor = SK_ColorDKGRAY;
37 const SkColor kPressedHoveredPathColor = SK_ColorBLACK;
38 const SkColor kBubbleColor = SK_ColorWHITE;
44 // Line segments for the button icons. The line segments are painted in a 12x12
46 const Line kMinimizeLineSegments[] = {
49 const Line kMaximizeRestoreLineSegments[] = {
55 const Line kCloseLineSegments[] = {
60 // The amount that the origin of the icon's 12x12 box should be offset from the
61 // center of the button.
62 const int kIconOffsetFromCenter = -6;
64 // Sets |line_segments| to the line segments for the icon for |action|.
65 void GetLineSegmentsForAction(AlternateFrameCaptionButton::Action action,
66 const Line** line_segments,
67 size_t* num_line_segments) {
69 case AlternateFrameCaptionButton::ACTION_MINIMIZE:
70 *line_segments = kMinimizeLineSegments;
71 *num_line_segments = arraysize(kMinimizeLineSegments);
73 case AlternateFrameCaptionButton::ACTION_MAXIMIZE_RESTORE:
74 *line_segments = kMaximizeRestoreLineSegments;
75 *num_line_segments = arraysize(kMaximizeRestoreLineSegments);
77 case AlternateFrameCaptionButton::ACTION_CLOSE:
78 *line_segments = kCloseLineSegments;
79 *num_line_segments = arraysize(kCloseLineSegments);
89 const char AlternateFrameCaptionButton::kViewClassName[] =
90 "AlternateFrameCaptionButton";
92 AlternateFrameCaptionButton::AlternateFrameCaptionButton(
93 views::ButtonListener* listener,
95 : views::CustomButton(listener),
97 hidden_bubble_radius_(0),
98 shown_bubble_radius_(0),
99 bubble_animation_(new gfx::SlideAnimation(this)) {
102 AlternateFrameCaptionButton::~AlternateFrameCaptionButton() {
106 int AlternateFrameCaptionButton::GetXOverlap() {
107 return kFinalBurstBubbleRadius - kSize / 2;
110 gfx::Size AlternateFrameCaptionButton::GetPreferredSize() {
111 gfx::Insets insets(GetInsets());
113 std::max(kFinalBurstBubbleRadius * 2, kSize + insets.width()),
114 kSize + insets.height());
117 const char* AlternateFrameCaptionButton::GetClassName() const {
118 return kViewClassName;
121 bool AlternateFrameCaptionButton::HitTestRect(const gfx::Rect& rect) const {
122 gfx::Rect bounds(GetLocalBounds());
123 if (state_ == STATE_PRESSED)
124 return bounds.Intersects(rect);
126 int x_overlap = GetXOverlap();
127 bounds.set_x(x_overlap);
128 bounds.set_width(width() - x_overlap * 2);
129 return bounds.Intersects(rect);
132 void AlternateFrameCaptionButton::OnPaint(gfx::Canvas* canvas) {
133 gfx::Point content_bounds_center(GetContentsBounds().CenterPoint());
135 int bubble_alpha = bubble_animation_->CurrentValueBetween(
136 0, kShownBubbleOpacity);
137 if (bubble_alpha != 0) {
138 int bubble_radius = bubble_animation_->CurrentValueBetween(
139 hidden_bubble_radius_, shown_bubble_radius_);
142 paint.setAntiAlias(true);
143 paint.setStyle(SkPaint::kFill_Style);
144 paint.setColor(SkColorSetA(kBubbleColor, bubble_alpha));
145 canvas->DrawCircle(content_bounds_center, bubble_radius, paint);
148 SkColor color = kPathColor;
149 if (state_ == STATE_HOVERED || state_ == STATE_PRESSED)
150 color = kPressedHoveredPathColor;
152 const Line* line_segments = NULL;
153 size_t num_line_segments = 0;
154 GetLineSegmentsForAction(action_, &line_segments, &num_line_segments);
156 gfx::Vector2d top_left_offset(
157 content_bounds_center.x() + kIconOffsetFromCenter,
158 content_bounds_center.y() + kIconOffsetFromCenter);
159 canvas->Translate(top_left_offset);
161 paint.setStyle(SkPaint::kStroke_Style);
162 paint.setStrokeWidth(SkIntToScalar(2));
163 paint.setStrokeCap(SkPaint::kSquare_Cap);
164 paint.setColor(color);
165 for (size_t i = 0; i < num_line_segments; ++i) {
166 canvas->DrawLine(gfx::Point(line_segments[i].x1, line_segments[i].y1),
167 gfx::Point(line_segments[i].x2, line_segments[i].y2),
170 canvas->Translate(-top_left_offset);
173 void AlternateFrameCaptionButton::MaybeStartNewBubbleAnimation() {
174 bool should_show = (state_ == STATE_PRESSED);
175 if (should_show == bubble_animation_->IsShowing())
178 if (!bubble_animation_->is_animating()) {
180 hidden_bubble_radius_ = kInitialGrowBubbleRadius;
182 hidden_bubble_radius_ = kFinalBurstBubbleRadius;
183 shown_bubble_radius_ = kFullyGrownBubbleRadius;
185 bubble_animation_->SetSlideDuration(kBubbleAnimationDuration);
187 bubble_animation_->Show();
189 bubble_animation_->Hide();
192 // The change in radius during a hide animation if there was no currently
193 // running animation.
194 int normal_radius_change =
195 kFinalBurstBubbleRadius - kFullyGrownBubbleRadius;
197 // Start a fade out animation from the bubble's current radius and
198 // opacity. Update the bubble radius and opacity at the same rate that it
199 // gets updated during a normal hide animation.
200 int current_bubble_radius = bubble_animation_->CurrentValueBetween(
201 kInitialGrowBubbleRadius, kFullyGrownBubbleRadius);
202 hidden_bubble_radius_ = current_bubble_radius +
203 bubble_animation_->GetCurrentValue() * normal_radius_change;
204 shown_bubble_radius_ = hidden_bubble_radius_ - normal_radius_change;
205 bubble_animation_->SetSlideDuration(
206 bubble_animation_->GetCurrentValue() * kBubbleAnimationDuration);
207 bubble_animation_->Hide();
209 // Else: The bubble is currently fading out. Wait till the hide animation
210 // completes before starting an animation to show a new bubble.
214 void AlternateFrameCaptionButton::StateChanged() {
215 MaybeStartNewBubbleAnimation();
218 void AlternateFrameCaptionButton::AnimationProgressed(
219 const gfx::Animation* animation) {
223 void AlternateFrameCaptionButton::AnimationEnded(
224 const gfx::Animation* animation) {
225 // The bubble animation was postponed if the button became pressed when the
226 // bubble was fading out. Do the animation now.
227 MaybeStartNewBubbleAnimation();