Enable account reconcilor when --new-profile-management is used.
[chromium-blink-merge.git] / ash / launcher / launcher_button.cc
blob469f4fca0996abbee78b86ae2c0c752b0b742b49
1 // Copyright (c) 2012 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/launcher/launcher_button.h"
7 #include <algorithm>
9 #include "ash/ash_switches.h"
10 #include "ash/shelf/shelf_button_host.h"
11 #include "ash/shelf/shelf_layout_manager.h"
12 #include "grit/ash_resources.h"
13 #include "skia/ext/image_operations.h"
14 #include "ui/base/accessibility/accessible_view_state.h"
15 #include "ui/base/resource/resource_bundle.h"
16 #include "ui/compositor/layer.h"
17 #include "ui/compositor/scoped_layer_animation_settings.h"
18 #include "ui/events/event_constants.h"
19 #include "ui/gfx/animation/animation_delegate.h"
20 #include "ui/gfx/animation/throb_animation.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/image/image.h"
23 #include "ui/gfx/image/image_skia_operations.h"
24 #include "ui/gfx/skbitmap_operations.h"
25 #include "ui/views/controls/image_view.h"
27 namespace {
29 // Size of the bar. This is along the opposite axis of the shelf. For example,
30 // if the shelf is aligned horizontally then this is the height of the bar.
31 const int kBarSize = 3;
32 const int kIconSize = 32;
33 const int kHopSpacing = 2;
34 const int kIconPad = 8;
35 const int kAlternateIconPad = 5;
36 const int kAlternateIconPadVertical = 6;
37 const int kHopUpMS = 0;
38 const int kHopDownMS = 200;
39 const int kAttentionThrobDurationMS = 800;
41 bool ShouldHop(int state) {
42 return state & ash::internal::LauncherButton::STATE_HOVERED ||
43 state & ash::internal::LauncherButton::STATE_ACTIVE ||
44 state & ash::internal::LauncherButton::STATE_FOCUSED;
47 // Simple AnimationDelegate that owns a single ThrobAnimation instance to
48 // keep all Draw Attention animations in sync.
49 class LauncherButtonAnimation : public gfx::AnimationDelegate {
50 public:
51 class Observer {
52 public:
53 virtual void AnimationProgressed() = 0;
55 protected:
56 virtual ~Observer() {}
59 static LauncherButtonAnimation* GetInstance() {
60 static LauncherButtonAnimation* s_instance = new LauncherButtonAnimation();
61 return s_instance;
64 void AddObserver(Observer* observer) {
65 observers_.AddObserver(observer);
68 void RemoveObserver(Observer* observer) {
69 observers_.RemoveObserver(observer);
70 if (!observers_.might_have_observers())
71 animation_.Stop();
74 int GetAlpha() {
75 return GetThrobAnimation().CurrentValueBetween(0, 255);
78 double GetAnimation() {
79 return GetThrobAnimation().GetCurrentValue();
82 private:
83 LauncherButtonAnimation()
84 : animation_(this) {
85 animation_.SetThrobDuration(kAttentionThrobDurationMS);
86 animation_.SetTweenType(gfx::Tween::SMOOTH_IN_OUT);
89 virtual ~LauncherButtonAnimation() {
92 gfx::ThrobAnimation& GetThrobAnimation() {
93 if (!animation_.is_animating()) {
94 animation_.Reset();
95 animation_.StartThrobbing(-1 /*throb indefinitely*/);
97 return animation_;
100 // gfx::AnimationDelegate
101 virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
102 if (animation != &animation_)
103 return;
104 if (!animation_.is_animating())
105 return;
106 FOR_EACH_OBSERVER(Observer, observers_, AnimationProgressed());
109 gfx::ThrobAnimation animation_;
110 ObserverList<Observer> observers_;
112 DISALLOW_COPY_AND_ASSIGN(LauncherButtonAnimation);
115 } // namespace
117 namespace ash {
118 namespace internal {
120 ////////////////////////////////////////////////////////////////////////////////
121 // LauncherButton::BarView
123 class LauncherButton::BarView : public views::ImageView,
124 public LauncherButtonAnimation::Observer {
125 public:
126 BarView(LauncherButton* host)
127 : host_(host),
128 show_attention_(false) {
131 virtual ~BarView() {
132 if (show_attention_)
133 LauncherButtonAnimation::GetInstance()->RemoveObserver(this);
136 // View
137 virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE {
138 // Allow Mouse...() messages to go to the parent view.
139 return false;
142 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
143 if (show_attention_) {
144 int alpha = LauncherButtonAnimation::GetInstance()->GetAlpha();
145 canvas->SaveLayerAlpha(alpha);
146 views::ImageView::OnPaint(canvas);
147 canvas->Restore();
148 } else {
149 views::ImageView::OnPaint(canvas);
153 // LauncherButtonAnimation::Observer
154 virtual void AnimationProgressed() OVERRIDE {
155 UpdateBounds();
156 SchedulePaint();
159 void SetBarBoundsRect(const gfx::Rect& bounds) {
160 base_bounds_ = bounds;
161 UpdateBounds();
164 void ShowAttention(bool show) {
165 if (show_attention_ != show) {
166 show_attention_ = show;
167 if (show_attention_)
168 LauncherButtonAnimation::GetInstance()->AddObserver(this);
169 else
170 LauncherButtonAnimation::GetInstance()->RemoveObserver(this);
172 UpdateBounds();
175 private:
176 void UpdateBounds() {
177 gfx::Rect bounds = base_bounds_;
178 if (show_attention_) {
179 // Scale from .35 to 1.0 of the total width (which is wider than the
180 // visible width of the image, so the animation "rests" briefly at full
181 // visible width.
182 double animation = LauncherButtonAnimation::GetInstance()->GetAnimation();
183 double scale = (.35 + .65 * animation);
184 if (host_->shelf_layout_manager()->GetAlignment() ==
185 SHELF_ALIGNMENT_BOTTOM) {
186 bounds.set_width(base_bounds_.width() * scale);
187 int x_offset = (base_bounds_.width() - bounds.width()) / 2;
188 bounds.set_x(base_bounds_.x() + x_offset);
189 } else {
190 bounds.set_height(base_bounds_.height() * scale);
191 int y_offset = (base_bounds_.height() - bounds.height()) / 2;
192 bounds.set_y(base_bounds_.y() + y_offset);
195 SetBoundsRect(bounds);
198 LauncherButton* host_;
199 bool show_attention_;
200 gfx::Rect base_bounds_;
202 DISALLOW_COPY_AND_ASSIGN(BarView);
205 ////////////////////////////////////////////////////////////////////////////////
206 // LauncherButton::IconView
208 LauncherButton::IconView::IconView() : icon_size_(kIconSize) {
211 LauncherButton::IconView::~IconView() {
214 bool LauncherButton::IconView::HitTestRect(const gfx::Rect& rect) const {
215 // Return false so that LauncherButton gets all the mouse events.
216 return false;
219 ////////////////////////////////////////////////////////////////////////////////
220 // LauncherButton
222 LauncherButton* LauncherButton::Create(
223 views::ButtonListener* listener,
224 ShelfButtonHost* host,
225 ShelfLayoutManager* shelf_layout_manager) {
226 LauncherButton* button =
227 new LauncherButton(listener, host, shelf_layout_manager);
228 button->Init();
229 return button;
232 LauncherButton::LauncherButton(views::ButtonListener* listener,
233 ShelfButtonHost* host,
234 ShelfLayoutManager* shelf_layout_manager)
235 : CustomButton(listener),
236 host_(host),
237 icon_view_(NULL),
238 bar_(new BarView(this)),
239 state_(STATE_NORMAL),
240 shelf_layout_manager_(shelf_layout_manager),
241 destroyed_flag_(NULL) {
242 set_accessibility_focusable(true);
244 const gfx::ShadowValue kShadows[] = {
245 gfx::ShadowValue(gfx::Point(0, 2), 0, SkColorSetARGB(0x1A, 0, 0, 0)),
246 gfx::ShadowValue(gfx::Point(0, 3), 1, SkColorSetARGB(0x1A, 0, 0, 0)),
247 gfx::ShadowValue(gfx::Point(0, 0), 1, SkColorSetARGB(0x54, 0, 0, 0)),
249 icon_shadows_.assign(kShadows, kShadows + arraysize(kShadows));
251 AddChildView(bar_);
254 LauncherButton::~LauncherButton() {
255 if (destroyed_flag_)
256 *destroyed_flag_ = true;
259 void LauncherButton::SetShadowedImage(const gfx::ImageSkia& image) {
260 icon_view_->SetImage(gfx::ImageSkiaOperations::CreateImageWithDropShadow(
261 image, icon_shadows_));
264 void LauncherButton::SetImage(const gfx::ImageSkia& image) {
265 if (image.isNull()) {
266 // TODO: need an empty image.
267 icon_view_->SetImage(image);
268 return;
271 if (icon_view_->icon_size() == 0) {
272 SetShadowedImage(image);
273 return;
276 // Resize the image maintaining our aspect ratio.
277 int pref = icon_view_->icon_size();
278 float aspect_ratio =
279 static_cast<float>(image.width()) / static_cast<float>(image.height());
280 int height = pref;
281 int width = static_cast<int>(aspect_ratio * height);
282 if (width > pref) {
283 width = pref;
284 height = static_cast<int>(width / aspect_ratio);
287 if (width == image.width() && height == image.height()) {
288 SetShadowedImage(image);
289 return;
292 SetShadowedImage(gfx::ImageSkiaOperations::CreateResizedImage(image,
293 skia::ImageOperations::RESIZE_BEST, gfx::Size(width, height)));
296 const gfx::ImageSkia& LauncherButton::GetImage() const {
297 return icon_view_->GetImage();
300 void LauncherButton::AddState(State state) {
301 if (!(state_ & state)) {
302 if (!ash::switches::UseAlternateShelfLayout() &&
303 (ShouldHop(state) || !ShouldHop(state_))) {
304 ui::ScopedLayerAnimationSettings scoped_setter(
305 icon_view_->layer()->GetAnimator());
306 scoped_setter.SetTransitionDuration(
307 base::TimeDelta::FromMilliseconds(kHopUpMS));
309 state_ |= state;
310 Layout();
311 if (state & STATE_ATTENTION)
312 bar_->ShowAttention(true);
316 void LauncherButton::ClearState(State state) {
317 if (state_ & state) {
318 if (!ash::switches::UseAlternateShelfLayout() &&
319 (!ShouldHop(state) || ShouldHop(state_))) {
320 ui::ScopedLayerAnimationSettings scoped_setter(
321 icon_view_->layer()->GetAnimator());
322 scoped_setter.SetTweenType(gfx::Tween::LINEAR);
323 scoped_setter.SetTransitionDuration(
324 base::TimeDelta::FromMilliseconds(kHopDownMS));
326 state_ &= ~state;
327 Layout();
328 if (state & STATE_ATTENTION)
329 bar_->ShowAttention(false);
333 gfx::Rect LauncherButton::GetIconBounds() const {
334 return icon_view_->bounds();
337 void LauncherButton::ShowContextMenu(const gfx::Point& p,
338 ui::MenuSourceType source_type) {
339 if (!context_menu_controller())
340 return;
342 bool destroyed = false;
343 destroyed_flag_ = &destroyed;
345 CustomButton::ShowContextMenu(p, source_type);
347 if (!destroyed) {
348 destroyed_flag_ = NULL;
349 // The menu will not propagate mouse events while its shown. To address,
350 // the hover state gets cleared once the menu was shown (and this was not
351 // destroyed).
352 ClearState(STATE_HOVERED);
356 bool LauncherButton::OnMousePressed(const ui::MouseEvent& event) {
357 CustomButton::OnMousePressed(event);
358 host_->PointerPressedOnButton(this, ShelfButtonHost::MOUSE, event);
359 return true;
362 void LauncherButton::OnMouseReleased(const ui::MouseEvent& event) {
363 CustomButton::OnMouseReleased(event);
364 host_->PointerReleasedOnButton(this, ShelfButtonHost::MOUSE, false);
367 void LauncherButton::OnMouseCaptureLost() {
368 ClearState(STATE_HOVERED);
369 host_->PointerReleasedOnButton(this, ShelfButtonHost::MOUSE, true);
370 CustomButton::OnMouseCaptureLost();
373 bool LauncherButton::OnMouseDragged(const ui::MouseEvent& event) {
374 CustomButton::OnMouseDragged(event);
375 host_->PointerDraggedOnButton(this, ShelfButtonHost::MOUSE, event);
376 return true;
379 void LauncherButton::OnMouseMoved(const ui::MouseEvent& event) {
380 CustomButton::OnMouseMoved(event);
381 host_->MouseMovedOverButton(this);
384 void LauncherButton::OnMouseEntered(const ui::MouseEvent& event) {
385 AddState(STATE_HOVERED);
386 CustomButton::OnMouseEntered(event);
387 host_->MouseEnteredButton(this);
390 void LauncherButton::OnMouseExited(const ui::MouseEvent& event) {
391 ClearState(STATE_HOVERED);
392 CustomButton::OnMouseExited(event);
393 host_->MouseExitedButton(this);
396 void LauncherButton::GetAccessibleState(ui::AccessibleViewState* state) {
397 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
398 state->name = host_->GetAccessibleName(this);
401 void LauncherButton::Layout() {
402 const gfx::Rect button_bounds(GetContentsBounds());
403 int icon_pad = kIconPad;
404 if (ash::switches::UseAlternateShelfLayout()) {
405 icon_pad =
406 shelf_layout_manager_->GetAlignment() != SHELF_ALIGNMENT_BOTTOM ?
407 kAlternateIconPadVertical : kAlternateIconPad;
409 int x_offset = shelf_layout_manager_->PrimaryAxisValue(0, icon_pad);
410 int y_offset = shelf_layout_manager_->PrimaryAxisValue(icon_pad, 0);
412 int icon_width = std::min(kIconSize,
413 button_bounds.width() - x_offset);
414 int icon_height = std::min(kIconSize,
415 button_bounds.height() - y_offset);
417 // If on the left or top 'invert' the inset so the constant gap is on
418 // the interior (towards the center of display) edge of the shelf.
419 if (SHELF_ALIGNMENT_LEFT == shelf_layout_manager_->GetAlignment())
420 x_offset = button_bounds.width() - (kIconSize + icon_pad);
422 if (SHELF_ALIGNMENT_TOP == shelf_layout_manager_->GetAlignment())
423 y_offset = button_bounds.height() - (kIconSize + icon_pad);
425 if (ShouldHop(state_) && !ash::switches::UseAlternateShelfLayout()) {
426 x_offset += shelf_layout_manager_->SelectValueForShelfAlignment(
427 0, kHopSpacing, -kHopSpacing, 0);
428 y_offset += shelf_layout_manager_->SelectValueForShelfAlignment(
429 -kHopSpacing, 0, 0, kHopSpacing);
432 // Center icon with respect to the secondary axis, and ensure
433 // that the icon doesn't occlude the bar highlight.
434 if (shelf_layout_manager_->IsHorizontalAlignment()) {
435 x_offset = std::max(0, button_bounds.width() - icon_width) / 2;
436 if (y_offset + icon_height + kBarSize > button_bounds.height())
437 icon_height = button_bounds.height() - (y_offset + kBarSize);
438 } else {
439 y_offset = std::max(0, button_bounds.height() - icon_height) / 2;
440 if (x_offset + icon_width + kBarSize > button_bounds.width())
441 icon_width = button_bounds.width() - (x_offset + kBarSize);
444 icon_view_->SetBoundsRect(gfx::Rect(
445 button_bounds.x() + x_offset,
446 button_bounds.y() + y_offset,
447 icon_width,
448 icon_height));
450 // Icon size has been incorrect when running
451 // PanelLayoutManagerTest.PanelAlignmentSecondDisplay on valgrind bot, see
452 // http://crbug.com/234854.
453 DCHECK_LE(icon_width, kIconSize);
454 DCHECK_LE(icon_height, kIconSize);
456 bar_->SetBarBoundsRect(button_bounds);
458 UpdateState();
461 void LauncherButton::ChildPreferredSizeChanged(views::View* child) {
462 Layout();
465 void LauncherButton::OnFocus() {
466 AddState(STATE_FOCUSED);
467 CustomButton::OnFocus();
470 void LauncherButton::OnBlur() {
471 ClearState(STATE_FOCUSED);
472 CustomButton::OnBlur();
475 void LauncherButton::OnGestureEvent(ui::GestureEvent* event) {
476 switch (event->type()) {
477 case ui::ET_GESTURE_TAP_DOWN:
478 AddState(STATE_HOVERED);
479 return CustomButton::OnGestureEvent(event);
480 case ui::ET_GESTURE_END:
481 ClearState(STATE_HOVERED);
482 return CustomButton::OnGestureEvent(event);
483 case ui::ET_GESTURE_SCROLL_BEGIN:
484 host_->PointerPressedOnButton(this, ShelfButtonHost::TOUCH, *event);
485 event->SetHandled();
486 return;
487 case ui::ET_GESTURE_SCROLL_UPDATE:
488 host_->PointerDraggedOnButton(this, ShelfButtonHost::TOUCH, *event);
489 event->SetHandled();
490 return;
491 case ui::ET_GESTURE_SCROLL_END:
492 case ui::ET_SCROLL_FLING_START:
493 host_->PointerReleasedOnButton(this, ShelfButtonHost::TOUCH, false);
494 event->SetHandled();
495 return;
496 default:
497 return CustomButton::OnGestureEvent(event);
501 void LauncherButton::Init() {
502 icon_view_ = CreateIconView();
504 // TODO: refactor the layers so each button doesn't require 2.
505 icon_view_->SetPaintToLayer(true);
506 icon_view_->SetFillsBoundsOpaquely(false);
507 icon_view_->SetHorizontalAlignment(views::ImageView::CENTER);
508 icon_view_->SetVerticalAlignment(views::ImageView::LEADING);
510 AddChildView(icon_view_);
513 LauncherButton::IconView* LauncherButton::CreateIconView() {
514 return new IconView;
517 bool LauncherButton::IsShelfHorizontal() const {
518 return shelf_layout_manager_->IsHorizontalAlignment();
521 void LauncherButton::UpdateState() {
522 UpdateBar();
524 icon_view_->SetHorizontalAlignment(
525 shelf_layout_manager_->PrimaryAxisValue(views::ImageView::CENTER,
526 views::ImageView::LEADING));
527 icon_view_->SetVerticalAlignment(
528 shelf_layout_manager_->PrimaryAxisValue(views::ImageView::LEADING,
529 views::ImageView::CENTER));
530 SchedulePaint();
533 void LauncherButton::UpdateBar() {
534 if (state_ & STATE_HIDDEN) {
535 bar_->SetVisible(false);
536 return;
539 int bar_id = 0;
540 if (ash::switches::UseAlternateShelfLayout()) {
541 if (state_ & STATE_ACTIVE)
542 bar_id = IDR_AURA_LAUNCHER_UNDERLINE_ACTIVE_ALTERNATE;
543 else if (state_ & STATE_RUNNING)
544 bar_id = IDR_AURA_LAUNCHER_UNDERLINE_RUNNING_ALTERNATE;
545 } else {
546 if (state_ & (STATE_ACTIVE | STATE_ATTENTION))
547 bar_id = IDR_AURA_LAUNCHER_UNDERLINE_ACTIVE;
548 else if (state_ & (STATE_HOVERED | STATE_FOCUSED))
549 bar_id = IDR_AURA_LAUNCHER_UNDERLINE_HOVER;
550 else
551 bar_id = IDR_AURA_LAUNCHER_UNDERLINE_RUNNING;
554 if (bar_id != 0) {
555 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
556 const gfx::ImageSkia* image = rb.GetImageNamed(bar_id).ToImageSkia();
557 if (shelf_layout_manager_->GetAlignment() == SHELF_ALIGNMENT_BOTTOM) {
558 bar_->SetImage(*image);
559 } else {
560 bar_->SetImage(gfx::ImageSkiaOperations::CreateRotatedImage(*image,
561 shelf_layout_manager_->SelectValueForShelfAlignment(
562 SkBitmapOperations::ROTATION_90_CW,
563 SkBitmapOperations::ROTATION_90_CW,
564 SkBitmapOperations::ROTATION_270_CW,
565 SkBitmapOperations::ROTATION_180_CW)));
567 bar_->SetHorizontalAlignment(
568 shelf_layout_manager_->SelectValueForShelfAlignment(
569 views::ImageView::CENTER,
570 views::ImageView::LEADING,
571 views::ImageView::TRAILING,
572 views::ImageView::CENTER));
573 bar_->SetVerticalAlignment(
574 shelf_layout_manager_->SelectValueForShelfAlignment(
575 views::ImageView::TRAILING,
576 views::ImageView::CENTER,
577 views::ImageView::CENTER,
578 views::ImageView::LEADING));
579 bar_->SchedulePaint();
582 bar_->SetVisible(bar_id != 0 && state_ != STATE_NORMAL);
585 } // namespace internal
586 } // namespace ash