- add sources.
[platform/framework/web/crosswalk.git] / src / ash / wm / caption_buttons / alternate_frame_caption_button.cc
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/alternate_frame_caption_button.h"
6
7 #include "ui/gfx/animation/slide_animation.h"
8 #include "ui/gfx/canvas.h"
9
10 namespace ash {
11
12 namespace {
13
14 // The width and height of the region of the button which does not overlap
15 // with other buttons.
16 const int kSize = 32;
17
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;
31
32 // The duraton of the animations for hiding and showing the bubble.
33 const int kBubbleAnimationDuration = 100;
34
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;
39
40 struct Line {
41   int x1, y1, x2, y2;
42 };
43
44 // Line segments for the button icons. The line segments are painted in a 12x12
45 // centered box.
46 const Line kMinimizeLineSegments[] = {
47   {1, 11, 11, 11}
48 };
49 const Line kMaximizeRestoreLineSegments[] = {
50   {1, 1, 11, 1},
51   {11, 1, 11, 11},
52   {11, 11, 1, 11},
53   {1, 11, 1, 1}
54 };
55 const Line kCloseLineSegments[] = {
56   {1, 1, 11, 11},
57   {1, 11, 11, 1}
58 };
59
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;
63
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) {
68   switch(action) {
69     case AlternateFrameCaptionButton::ACTION_MINIMIZE:
70       *line_segments = kMinimizeLineSegments;
71       *num_line_segments = arraysize(kMinimizeLineSegments);
72       break;
73     case AlternateFrameCaptionButton::ACTION_MAXIMIZE_RESTORE:
74       *line_segments = kMaximizeRestoreLineSegments;
75       *num_line_segments = arraysize(kMaximizeRestoreLineSegments);
76       break;
77     case AlternateFrameCaptionButton::ACTION_CLOSE:
78       *line_segments = kCloseLineSegments;
79       *num_line_segments = arraysize(kCloseLineSegments);
80       break;
81     default:
82       NOTREACHED();
83       break;
84   }
85 }
86
87 }  // namespace
88
89 const char AlternateFrameCaptionButton::kViewClassName[] =
90     "AlternateFrameCaptionButton";
91
92 AlternateFrameCaptionButton::AlternateFrameCaptionButton(
93     views::ButtonListener* listener,
94     Action action)
95     : views::CustomButton(listener),
96       action_(action),
97       hidden_bubble_radius_(0),
98       shown_bubble_radius_(0),
99       bubble_animation_(new gfx::SlideAnimation(this)) {
100 }
101
102 AlternateFrameCaptionButton::~AlternateFrameCaptionButton() {
103 }
104
105 // static
106 int AlternateFrameCaptionButton::GetXOverlap() {
107   return kFinalBurstBubbleRadius - kSize / 2;
108 }
109
110 gfx::Size AlternateFrameCaptionButton::GetPreferredSize() {
111   gfx::Insets insets(GetInsets());
112   return gfx::Size(
113       std::max(kFinalBurstBubbleRadius * 2, kSize + insets.width()),
114       kSize + insets.height());
115 }
116
117 const char* AlternateFrameCaptionButton::GetClassName() const {
118   return kViewClassName;
119 }
120
121 bool AlternateFrameCaptionButton::HitTestRect(const gfx::Rect& rect) const {
122   gfx::Rect bounds(GetLocalBounds());
123   if (state_ == STATE_PRESSED)
124     return bounds.Intersects(rect);
125
126   int x_overlap = GetXOverlap();
127   bounds.set_x(x_overlap);
128   bounds.set_width(width() - x_overlap * 2);
129   return bounds.Intersects(rect);
130 }
131
132 void AlternateFrameCaptionButton::OnPaint(gfx::Canvas* canvas) {
133   gfx::Point content_bounds_center(GetContentsBounds().CenterPoint());
134
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_);
140
141     SkPaint paint;
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);
146   }
147
148   SkColor color = kPathColor;
149   if (state_ == STATE_HOVERED || state_ == STATE_PRESSED)
150     color = kPressedHoveredPathColor;
151
152   const Line* line_segments = NULL;
153   size_t num_line_segments = 0;
154   GetLineSegmentsForAction(action_, &line_segments, &num_line_segments);
155
156   gfx::Vector2d top_left_offset(
157       content_bounds_center.x() + kIconOffsetFromCenter,
158       content_bounds_center.y() + kIconOffsetFromCenter);
159   canvas->Translate(top_left_offset);
160   SkPaint paint;
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),
168                      paint);
169   }
170   canvas->Translate(-top_left_offset);
171 }
172
173 void AlternateFrameCaptionButton::MaybeStartNewBubbleAnimation() {
174   bool should_show = (state_ == STATE_PRESSED);
175   if (should_show == bubble_animation_->IsShowing())
176     return;
177
178   if (!bubble_animation_->is_animating()) {
179     if (should_show)
180       hidden_bubble_radius_ = kInitialGrowBubbleRadius;
181     else
182       hidden_bubble_radius_ = kFinalBurstBubbleRadius;
183     shown_bubble_radius_ = kFullyGrownBubbleRadius;
184
185     bubble_animation_->SetSlideDuration(kBubbleAnimationDuration);
186     if (should_show)
187       bubble_animation_->Show();
188     else
189       bubble_animation_->Hide();
190   } else {
191     if (!should_show) {
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;
196
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();
208     }
209     // Else: The bubble is currently fading out. Wait till the hide animation
210     // completes before starting an animation to show a new bubble.
211   }
212 }
213
214 void AlternateFrameCaptionButton::StateChanged() {
215   MaybeStartNewBubbleAnimation();
216 }
217
218 void AlternateFrameCaptionButton::AnimationProgressed(
219     const gfx::Animation* animation) {
220   SchedulePaint();
221 }
222
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();
228 }
229
230 }  // namespace ash