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_view.h"
9 #include "ash/ash_constants.h"
10 #include "ash/ash_switches.h"
11 #include "ash/launcher/app_list_button.h"
12 #include "ash/launcher/launcher_button.h"
13 #include "ash/launcher/launcher_delegate.h"
14 #include "ash/launcher/launcher_icon_observer.h"
15 #include "ash/launcher/launcher_model.h"
16 #include "ash/launcher/launcher_tooltip_manager.h"
17 #include "ash/launcher/overflow_bubble.h"
18 #include "ash/launcher/overflow_button.h"
19 #include "ash/launcher/tabbed_launcher_button.h"
20 #include "ash/root_window_controller.h"
21 #include "ash/shelf/shelf_layout_manager.h"
22 #include "ash/shelf/shelf_widget.h"
23 #include "ash/shell_delegate.h"
24 #include "base/auto_reset.h"
25 #include "base/memory/scoped_ptr.h"
26 #include "grit/ash_resources.h"
27 #include "grit/ash_strings.h"
28 #include "ui/aura/window.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/models/simple_menu_model.h"
31 #include "ui/base/resource/resource_bundle.h"
32 #include "ui/compositor/layer.h"
33 #include "ui/compositor/layer_animator.h"
34 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
35 #include "ui/gfx/canvas.h"
36 #include "ui/views/animation/bounds_animator.h"
37 #include "ui/views/border.h"
38 #include "ui/views/controls/button/image_button.h"
39 #include "ui/views/controls/menu/menu_model_adapter.h"
40 #include "ui/views/controls/menu/menu_runner.h"
41 #include "ui/views/focus/focus_search.h"
42 #include "ui/views/focus_border.h"
43 #include "ui/views/view_model.h"
44 #include "ui/views/view_model_utils.h"
45 #include "ui/views/widget/widget.h"
53 // Default amount content is inset on the left edge.
54 const int kDefaultLeadingInset
= 8;
56 // Minimum distance before drag starts.
57 const int kMinimumDragDistance
= 8;
59 // Size between the buttons.
60 const int kButtonSpacing
= 4;
62 // Additional spacing for the left and right side of icons.
63 const int kHorizontalIconSpacing
= 2;
65 // Inset for items which do not have an icon.
66 const int kHorizontalNoIconInsetSpacing
=
67 kHorizontalIconSpacing
+ kDefaultLeadingInset
;
69 // The proportion of the launcher space reserved for non-panel icons. Panels
70 // may flow into this space but will be put into the overflow bubble if there
71 // is contention for the space.
72 const float kReservedNonPanelIconProportion
= 0.67f
;
74 // This is the command id of the menu item which contains the name of the menu.
75 const int kCommandIdOfMenuName
= 0;
77 // The background color of the active item in the list.
78 const SkColor kActiveListItemBackgroundColor
= SkColorSetRGB(203 , 219, 241);
80 // The background color of the active & hovered item in the list.
81 const SkColor kFocusedActiveListItemBackgroundColor
=
82 SkColorSetRGB(193, 211, 236);
84 // The text color of the caption item in a list.
85 const SkColor kCaptionItemForegroundColor
= SK_ColorBLACK
;
87 // The maximum allowable length of a menu line of an application menu in pixels.
88 const int kMaximumAppMenuItemLength
= 350;
92 // The MenuModelAdapter gets slightly changed to adapt the menu appearance to
94 class LauncherMenuModelAdapter
95 : public views::MenuModelAdapter
{
97 explicit LauncherMenuModelAdapter(ash::LauncherMenuModel
* menu_model
);
99 // Overriding MenuModelAdapter's MenuDelegate implementation.
100 virtual const gfx::Font
* GetLabelFont(int command_id
) const OVERRIDE
;
101 virtual bool IsCommandEnabled(int id
) const OVERRIDE
;
102 virtual void GetHorizontalIconMargins(int id
,
105 int* right_margin
) const OVERRIDE
;
106 virtual bool GetForegroundColor(int command_id
,
108 SkColor
* override_color
) const OVERRIDE
;
109 virtual bool GetBackgroundColor(int command_id
,
111 SkColor
* override_color
) const OVERRIDE
;
112 virtual int GetMaxWidthForMenu(views::MenuItemView
* menu
) OVERRIDE
;
113 virtual bool ShouldReserveSpaceForSubmenuIndicator() const OVERRIDE
;
116 ash::LauncherMenuModel
* launcher_menu_model_
;
118 DISALLOW_COPY_AND_ASSIGN(LauncherMenuModelAdapter
);
122 LauncherMenuModelAdapter::LauncherMenuModelAdapter(
123 ash::LauncherMenuModel
* menu_model
)
124 : MenuModelAdapter(menu_model
),
125 launcher_menu_model_(menu_model
) {}
127 const gfx::Font
* LauncherMenuModelAdapter::GetLabelFont(
128 int command_id
) const {
129 if (command_id
!= kCommandIdOfMenuName
)
130 return MenuModelAdapter::GetLabelFont(command_id
);
132 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
133 return &rb
.GetFont(ui::ResourceBundle::BoldFont
);
136 bool LauncherMenuModelAdapter::IsCommandEnabled(int id
) const {
137 return id
!= kCommandIdOfMenuName
;
140 bool LauncherMenuModelAdapter::GetForegroundColor(
143 SkColor
* override_color
) const {
144 if (command_id
!= kCommandIdOfMenuName
)
147 *override_color
= kCaptionItemForegroundColor
;
151 bool LauncherMenuModelAdapter::GetBackgroundColor(
154 SkColor
* override_color
) const {
155 if (!launcher_menu_model_
->IsCommandActive(command_id
))
158 *override_color
= is_hovered
? kFocusedActiveListItemBackgroundColor
:
159 kActiveListItemBackgroundColor
;
163 void LauncherMenuModelAdapter::GetHorizontalIconMargins(
167 int* right_margin
) const {
168 *left_margin
= kHorizontalIconSpacing
;
169 *right_margin
= (command_id
!= kCommandIdOfMenuName
) ?
170 kHorizontalIconSpacing
: -(icon_size
+ kHorizontalNoIconInsetSpacing
);
173 int LauncherMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView
* menu
) {
174 return kMaximumAppMenuItemLength
;
177 bool LauncherMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const {
181 // Custom FocusSearch used to navigate the launcher in the order items are in
183 class LauncherFocusSearch
: public views::FocusSearch
{
185 explicit LauncherFocusSearch(views::ViewModel
* view_model
)
186 : FocusSearch(NULL
, true, true),
187 view_model_(view_model
) {}
188 virtual ~LauncherFocusSearch() {}
190 // views::FocusSearch overrides:
191 virtual View
* FindNextFocusableView(
195 bool check_starting_view
,
196 views::FocusTraversable
** focus_traversable
,
197 View
** focus_traversable_view
) OVERRIDE
{
198 int index
= view_model_
->GetIndexOfView(starting_view
);
200 return view_model_
->view_at(0);
205 index
= view_model_
->view_size() - 1;
208 if (index
>= view_model_
->view_size())
211 return view_model_
->view_at(index
);
215 views::ViewModel
* view_model_
;
217 DISALLOW_COPY_AND_ASSIGN(LauncherFocusSearch
);
220 class LauncherButtonFocusBorder
: public views::FocusBorder
{
222 LauncherButtonFocusBorder() {}
223 virtual ~LauncherButtonFocusBorder() {}
226 // views::FocusBorder overrides:
227 virtual void Paint(const View
& view
, gfx::Canvas
* canvas
) const OVERRIDE
{
228 gfx::Rect
rect(view
.GetLocalBounds());
230 canvas
->DrawRect(rect
, kFocusBorderColor
);
233 DISALLOW_COPY_AND_ASSIGN(LauncherButtonFocusBorder
);
236 // AnimationDelegate that deletes a view when done. This is used when a launcher
237 // item is removed, which triggers a remove animation. When the animation is
238 // done we delete the view.
239 class DeleteViewAnimationDelegate
240 : public views::BoundsAnimator::OwnedAnimationDelegate
{
242 explicit DeleteViewAnimationDelegate(views::View
* view
) : view_(view
) {}
243 virtual ~DeleteViewAnimationDelegate() {}
246 scoped_ptr
<views::View
> view_
;
248 DISALLOW_COPY_AND_ASSIGN(DeleteViewAnimationDelegate
);
251 // AnimationDelegate used when inserting a new item. This steadily increases the
252 // opacity of the layer as the animation progress.
253 class FadeInAnimationDelegate
254 : public views::BoundsAnimator::OwnedAnimationDelegate
{
256 explicit FadeInAnimationDelegate(views::View
* view
) : view_(view
) {}
257 virtual ~FadeInAnimationDelegate() {}
259 // AnimationDelegate overrides:
260 virtual void AnimationProgressed(const Animation
* animation
) OVERRIDE
{
261 view_
->layer()->SetOpacity(animation
->GetCurrentValue());
262 view_
->layer()->ScheduleDraw();
264 virtual void AnimationEnded(const Animation
* animation
) OVERRIDE
{
265 view_
->layer()->SetOpacity(1.0f
);
266 view_
->layer()->ScheduleDraw();
268 virtual void AnimationCanceled(const Animation
* animation
) OVERRIDE
{
269 view_
->layer()->SetOpacity(1.0f
);
270 view_
->layer()->ScheduleDraw();
276 DISALLOW_COPY_AND_ASSIGN(FadeInAnimationDelegate
);
279 void ReflectItemStatus(const ash::LauncherItem
& item
,
280 LauncherButton
* button
) {
281 switch (item
.status
) {
283 button
->ClearState(LauncherButton::STATE_ACTIVE
);
284 button
->ClearState(LauncherButton::STATE_RUNNING
);
285 button
->ClearState(LauncherButton::STATE_ATTENTION
);
288 button
->ClearState(LauncherButton::STATE_ACTIVE
);
289 button
->AddState(LauncherButton::STATE_RUNNING
);
290 button
->ClearState(LauncherButton::STATE_ATTENTION
);
293 button
->AddState(LauncherButton::STATE_ACTIVE
);
294 button
->ClearState(LauncherButton::STATE_RUNNING
);
295 button
->ClearState(LauncherButton::STATE_ATTENTION
);
297 case STATUS_ATTENTION
:
298 button
->ClearState(LauncherButton::STATE_ACTIVE
);
299 button
->ClearState(LauncherButton::STATE_RUNNING
);
300 button
->AddState(LauncherButton::STATE_ATTENTION
);
307 // AnimationDelegate used when deleting an item. This steadily decreased the
308 // opacity of the layer as the animation progress.
309 class LauncherView::FadeOutAnimationDelegate
310 : public views::BoundsAnimator::OwnedAnimationDelegate
{
312 FadeOutAnimationDelegate(LauncherView
* host
, views::View
* view
)
313 : launcher_view_(host
),
315 virtual ~FadeOutAnimationDelegate() {}
317 // AnimationDelegate overrides:
318 virtual void AnimationProgressed(const Animation
* animation
) OVERRIDE
{
319 view_
->layer()->SetOpacity(1 - animation
->GetCurrentValue());
320 view_
->layer()->ScheduleDraw();
322 virtual void AnimationEnded(const Animation
* animation
) OVERRIDE
{
323 launcher_view_
->OnFadeOutAnimationEnded();
325 virtual void AnimationCanceled(const Animation
* animation
) OVERRIDE
{
329 LauncherView
* launcher_view_
;
330 scoped_ptr
<views::View
> view_
;
332 DISALLOW_COPY_AND_ASSIGN(FadeOutAnimationDelegate
);
335 // AnimationDelegate used to trigger fading an element in. When an item is
336 // inserted this delegate is attached to the animation that expands the size of
337 // the item. When done it kicks off another animation to fade the item in.
338 class LauncherView::StartFadeAnimationDelegate
339 : public views::BoundsAnimator::OwnedAnimationDelegate
{
341 StartFadeAnimationDelegate(LauncherView
* host
,
343 : launcher_view_(host
),
345 virtual ~StartFadeAnimationDelegate() {}
347 // AnimationDelegate overrides:
348 virtual void AnimationEnded(const Animation
* animation
) OVERRIDE
{
349 launcher_view_
->FadeIn(view_
);
351 virtual void AnimationCanceled(const Animation
* animation
) OVERRIDE
{
352 view_
->layer()->SetOpacity(1.0f
);
356 LauncherView
* launcher_view_
;
359 DISALLOW_COPY_AND_ASSIGN(StartFadeAnimationDelegate
);
362 LauncherView::LauncherView(LauncherModel
* model
,
363 LauncherDelegate
* delegate
,
364 ShelfLayoutManager
* shelf_layout_manager
)
367 view_model_(new views::ViewModel
),
368 first_visible_index_(0),
369 last_visible_index_(-1),
370 overflow_button_(NULL
),
374 start_drag_index_(-1),
376 leading_inset_(kDefaultLeadingInset
),
377 cancelling_drag_model_changed_(false),
378 last_hidden_index_(0),
379 closing_event_time_(base::TimeDelta()),
382 bounds_animator_
.reset(new views::BoundsAnimator(this));
383 bounds_animator_
->AddObserver(this);
384 set_context_menu_controller(this);
385 focus_search_
.reset(new LauncherFocusSearch(view_model_
.get()));
386 tooltip_
.reset(new LauncherTooltipManager(
387 shelf_layout_manager
, this));
390 LauncherView::~LauncherView() {
391 bounds_animator_
->RemoveObserver(this);
392 model_
->RemoveObserver(this);
393 // If we are inside the MenuRunner, we need to know if we were getting
394 // deleted while it was running.
396 *got_deleted_
= true;
399 void LauncherView::Init() {
400 model_
->AddObserver(this);
402 const LauncherItems
& items(model_
->items());
403 for (LauncherItems::const_iterator i
= items
.begin(); i
!= items
.end(); ++i
) {
404 views::View
* child
= CreateViewForItem(*i
);
405 child
->SetPaintToLayer(true);
406 view_model_
->Add(child
, static_cast<int>(i
- items
.begin()));
409 LauncherStatusChanged();
410 overflow_button_
= new OverflowButton(this);
411 overflow_button_
->set_context_menu_controller(this);
412 ConfigureChildView(overflow_button_
);
413 AddChildView(overflow_button_
);
414 UpdateFirstButtonPadding();
416 // We'll layout when our bounds change.
419 void LauncherView::OnShelfAlignmentChanged() {
420 UpdateFirstButtonPadding();
421 overflow_button_
->OnShelfAlignmentChanged();
422 LayoutToIdealBounds();
423 for (int i
=0; i
< view_model_
->view_size(); ++i
) {
424 // TODO: remove when AppIcon is a Launcher Button.
425 if (TYPE_APP_LIST
== model_
->items()[i
].type
) {
426 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
427 static_cast<AppListButton
*>(view_model_
->view_at(i
))->SetImageAlignment(
428 shelf
->SelectValueForShelfAlignment(
429 views::ImageButton::ALIGN_CENTER
,
430 views::ImageButton::ALIGN_LEFT
,
431 views::ImageButton::ALIGN_RIGHT
,
432 views::ImageButton::ALIGN_CENTER
),
433 shelf
->SelectValueForShelfAlignment(
434 views::ImageButton::ALIGN_TOP
,
435 views::ImageButton::ALIGN_MIDDLE
,
436 views::ImageButton::ALIGN_MIDDLE
,
437 views::ImageButton::ALIGN_BOTTOM
));
439 if (i
>= first_visible_index_
&& i
<= last_visible_index_
)
440 view_model_
->view_at(i
)->Layout();
442 tooltip_
->UpdateArrowLocation();
443 if (overflow_bubble_
.get())
444 overflow_bubble_
->Hide();
447 gfx::Rect
LauncherView::GetIdealBoundsOfItemIcon(LauncherID id
) {
448 int index
= model_
->ItemIndexByID(id
);
449 if (index
== -1 || (index
> last_visible_index_
&&
450 index
< model_
->FirstPanelIndex()))
452 const gfx::Rect
& ideal_bounds(view_model_
->ideal_bounds(index
));
453 DCHECK_NE(TYPE_APP_LIST
, model_
->items()[index
].type
);
454 LauncherButton
* button
=
455 static_cast<LauncherButton
*>(view_model_
->view_at(index
));
456 gfx::Rect icon_bounds
= button
->GetIconBounds();
457 return gfx::Rect(ideal_bounds
.x() + icon_bounds
.x(),
458 ideal_bounds
.y() + icon_bounds
.y(),
460 icon_bounds
.height());
463 void LauncherView::UpdatePanelIconPosition(LauncherID id
,
464 const gfx::Point
& midpoint
) {
465 int current_index
= model_
->ItemIndexByID(id
);
466 int first_panel_index
= model_
->FirstPanelIndex();
467 if (current_index
< first_panel_index
)
470 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
471 int target_index
= current_index
;
472 while (target_index
> first_panel_index
&&
473 shelf
->PrimaryAxisValue(view_model_
->ideal_bounds(target_index
).x(),
474 view_model_
->ideal_bounds(target_index
).y()) >
475 shelf
->PrimaryAxisValue(midpoint
.x(), midpoint
.y())) {
478 while (target_index
< view_model_
->view_size() - 1 &&
479 shelf
->PrimaryAxisValue(
480 view_model_
->ideal_bounds(target_index
).right(),
481 view_model_
->ideal_bounds(target_index
).bottom()) <
482 shelf
->PrimaryAxisValue(midpoint
.x(), midpoint
.y())) {
485 if (current_index
!= target_index
)
486 model_
->Move(current_index
, target_index
);
489 bool LauncherView::IsShowingMenu() const {
490 #if !defined(OS_MACOSX)
491 return (launcher_menu_runner_
.get() &&
492 launcher_menu_runner_
->IsRunning());
497 bool LauncherView::IsShowingOverflowBubble() const {
498 return overflow_bubble_
.get() && overflow_bubble_
->IsShowing();
501 views::View
* LauncherView::GetAppListButtonView() const {
502 for (int i
= 0; i
< model_
->item_count(); ++i
) {
503 if (model_
->items()[i
].type
== TYPE_APP_LIST
)
504 return view_model_
->view_at(i
);
507 NOTREACHED() << "Applist button not found";
511 ////////////////////////////////////////////////////////////////////////////////
512 // LauncherView, FocusTraversable implementation:
514 views::FocusSearch
* LauncherView::GetFocusSearch() {
515 return focus_search_
.get();
518 views::FocusTraversable
* LauncherView::GetFocusTraversableParent() {
519 return parent()->GetFocusTraversable();
522 View
* LauncherView::GetFocusTraversableParentView() {
526 void LauncherView::LayoutToIdealBounds() {
527 IdealBounds ideal_bounds
;
528 CalculateIdealBounds(&ideal_bounds
);
530 if (bounds_animator_
->IsAnimating())
531 AnimateToIdealBounds();
533 views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_
);
535 overflow_button_
->SetBoundsRect(ideal_bounds
.overflow_bounds
);
538 void LauncherView::CalculateIdealBounds(IdealBounds
* bounds
) {
539 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
541 int available_size
= shelf
->PrimaryAxisValue(width(), height());
542 DCHECK(model_
->item_count() == view_model_
->view_size());
546 int first_panel_index
= model_
->FirstPanelIndex();
547 int app_list_index
= first_panel_index
- 1;
549 // Initial x,y values account both leading_inset in primary
550 // coordinate and secondary coordinate based on the dynamic edge of the
551 // launcher (eg top edge on bottom-aligned launcher).
552 int x
= shelf
->SelectValueForShelfAlignment(
557 int y
= shelf
->SelectValueForShelfAlignment(
562 int w
= shelf
->PrimaryAxisValue(kLauncherPreferredSize
, width());
563 int h
= shelf
->PrimaryAxisValue(height(), kLauncherPreferredSize
);
564 for (int i
= 0; i
< view_model_
->view_size(); ++i
) {
565 if (i
< first_visible_index_
) {
566 view_model_
->set_ideal_bounds(i
, gfx::Rect(x
, y
, 0, 0));
570 view_model_
->set_ideal_bounds(i
, gfx::Rect(x
, y
, w
, h
));
571 if (i
!= app_list_index
) {
572 x
= shelf
->PrimaryAxisValue(x
+ w
+ kButtonSpacing
, x
);
573 y
= shelf
->PrimaryAxisValue(y
, y
+ h
+ kButtonSpacing
);
577 if (is_overflow_mode()) {
578 DCHECK_LT(last_visible_index_
, view_model_
->view_size());
579 for (int i
= 0; i
< view_model_
->view_size(); ++i
) {
580 view_model_
->view_at(i
)->SetVisible(
581 i
>= first_visible_index_
&&
582 i
!= app_list_index
&&
583 i
<= last_visible_index_
);
588 // To address Fitt's law, we make the first launcher button include the
589 // leading inset (if there is one).
590 if (view_model_
->view_size() > 0) {
591 view_model_
->set_ideal_bounds(0, gfx::Rect(gfx::Size(
592 shelf
->PrimaryAxisValue(leading_inset() + w
, w
),
593 shelf
->PrimaryAxisValue(h
, leading_inset() + h
))));
596 // Right aligned icons.
597 int end_position
= available_size
- kButtonSpacing
;
598 x
= shelf
->PrimaryAxisValue(end_position
, 0);
599 y
= shelf
->PrimaryAxisValue(0, end_position
);
600 for (int i
= view_model_
->view_size() - 1;
601 i
>= first_panel_index
; --i
) {
602 x
= shelf
->PrimaryAxisValue(x
- w
- kButtonSpacing
, x
);
603 y
= shelf
->PrimaryAxisValue(y
, y
- h
- kButtonSpacing
);
604 view_model_
->set_ideal_bounds(i
, gfx::Rect(x
, y
, w
, h
));
605 end_position
= shelf
->PrimaryAxisValue(x
, y
);
608 // Icons on the left / top are guaranteed up to kLeftIconProportion of
609 // the available space.
610 int last_icon_position
= shelf
->PrimaryAxisValue(
611 view_model_
->ideal_bounds(first_panel_index
- 1).right(),
612 view_model_
->ideal_bounds(first_panel_index
- 1).bottom()) +
613 2 * kLauncherPreferredSize
+ leading_inset();
614 int reserved_icon_space
= available_size
* kReservedNonPanelIconProportion
;
615 if (last_icon_position
< reserved_icon_space
)
616 end_position
= last_icon_position
;
618 end_position
= std::max(end_position
, reserved_icon_space
);
620 bounds
->overflow_bounds
.set_size(gfx::Size(
621 shelf
->PrimaryAxisValue(w
, width()),
622 shelf
->PrimaryAxisValue(height(), h
)));
623 last_visible_index_
= DetermineLastVisibleIndex(
624 end_position
- leading_inset() - 2 * kLauncherPreferredSize
);
625 last_hidden_index_
= DetermineFirstVisiblePanelIndex(end_position
) - 1;
626 bool show_overflow
= (last_visible_index_
+ 1 < app_list_index
||
627 last_hidden_index_
>= first_panel_index
);
629 for (int i
= 0; i
< view_model_
->view_size(); ++i
) {
630 view_model_
->view_at(i
)->SetVisible(
631 i
<= last_visible_index_
||
632 i
== app_list_index
||
633 i
> last_hidden_index_
);
636 overflow_button_
->SetVisible(show_overflow
);
638 DCHECK_NE(0, view_model_
->view_size());
639 if (last_visible_index_
== -1) {
640 x
= shelf
->SelectValueForShelfAlignment(
645 y
= shelf
->SelectValueForShelfAlignment(
650 } else if (last_visible_index_
== app_list_index
) {
651 x
= view_model_
->ideal_bounds(last_visible_index_
).x();
652 y
= view_model_
->ideal_bounds(last_visible_index_
).y();
654 x
= shelf
->PrimaryAxisValue(
655 view_model_
->ideal_bounds(last_visible_index_
).right(),
656 view_model_
->ideal_bounds(last_visible_index_
).x());
657 y
= shelf
->PrimaryAxisValue(
658 view_model_
->ideal_bounds(last_visible_index_
).y(),
659 view_model_
->ideal_bounds(last_visible_index_
).bottom());
661 gfx::Rect app_list_bounds
= view_model_
->ideal_bounds(app_list_index
);
662 bounds
->overflow_bounds
.set_x(x
);
663 bounds
->overflow_bounds
.set_y(y
);
665 // Set all hidden panel icon positions to be on the overflow button.
666 for (int i
= first_panel_index
; i
<= last_hidden_index_
; ++i
)
667 view_model_
->set_ideal_bounds(i
, gfx::Rect(x
, y
, w
, h
));
669 x
= shelf
->PrimaryAxisValue(x
+ w
+ kButtonSpacing
, x
);
670 y
= shelf
->PrimaryAxisValue(y
, y
+ h
+ kButtonSpacing
);
671 app_list_bounds
.set_x(x
);
672 app_list_bounds
.set_y(y
);
673 view_model_
->set_ideal_bounds(app_list_index
, app_list_bounds
);
675 if (overflow_bubble_
.get() && overflow_bubble_
->IsShowing())
676 UpdateOverflowRange(overflow_bubble_
->launcher_view());
678 if (overflow_bubble_
.get())
679 overflow_bubble_
->Hide();
683 int LauncherView::DetermineLastVisibleIndex(int max_value
) const {
684 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
686 int index
= model_
->FirstPanelIndex() - 1;
688 shelf
->PrimaryAxisValue(
689 view_model_
->ideal_bounds(index
).right(),
690 view_model_
->ideal_bounds(index
).bottom()) > max_value
) {
696 int LauncherView::DetermineFirstVisiblePanelIndex(int min_value
) const {
697 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
699 int index
= model_
->FirstPanelIndex();
700 while (index
< view_model_
->view_size() &&
701 shelf
->PrimaryAxisValue(
702 view_model_
->ideal_bounds(index
).right(),
703 view_model_
->ideal_bounds(index
).bottom()) < min_value
) {
709 void LauncherView::AddIconObserver(LauncherIconObserver
* observer
) {
710 observers_
.AddObserver(observer
);
713 void LauncherView::RemoveIconObserver(LauncherIconObserver
* observer
) {
714 observers_
.RemoveObserver(observer
);
717 void LauncherView::AnimateToIdealBounds() {
718 IdealBounds ideal_bounds
;
719 CalculateIdealBounds(&ideal_bounds
);
720 for (int i
= 0; i
< view_model_
->view_size(); ++i
) {
721 View
* view
= view_model_
->view_at(i
);
722 bounds_animator_
->AnimateViewTo(view
, view_model_
->ideal_bounds(i
));
723 // Now that the item animation starts, we have to make sure that the
724 // padding of the first gets properly transferred to the new first item.
725 if (i
&& view
->border())
726 view
->set_border(NULL
);
727 else if (!i
&& !view
->border())
728 UpdateFirstButtonPadding();
730 overflow_button_
->SetBoundsRect(ideal_bounds
.overflow_bounds
);
733 views::View
* LauncherView::CreateViewForItem(const LauncherItem
& item
) {
734 views::View
* view
= NULL
;
737 TabbedLauncherButton
* button
=
738 TabbedLauncherButton::Create(
741 tooltip_
->shelf_layout_manager(),
743 TabbedLauncherButton::STATE_INCOGNITO
:
744 TabbedLauncherButton::STATE_NOT_INCOGNITO
);
745 button
->SetTabImage(item
.image
);
746 ReflectItemStatus(item
, button
);
751 case TYPE_APP_SHORTCUT
:
752 case TYPE_WINDOWED_APP
:
753 case TYPE_PLATFORM_APP
:
754 case TYPE_APP_PANEL
: {
755 LauncherButton
* button
= LauncherButton::Create(
756 this, this, tooltip_
->shelf_layout_manager());
757 button
->SetImage(item
.image
);
758 ReflectItemStatus(item
, button
);
763 case TYPE_APP_LIST
: {
764 // TODO(dave): turn this into a LauncherButton too.
765 AppListButton
* button
= new AppListButton(this, this);
766 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
767 button
->SetImageAlignment(
768 shelf
->SelectValueForShelfAlignment(
769 views::ImageButton::ALIGN_CENTER
,
770 views::ImageButton::ALIGN_LEFT
,
771 views::ImageButton::ALIGN_RIGHT
,
772 views::ImageButton::ALIGN_CENTER
),
773 shelf
->SelectValueForShelfAlignment(
774 views::ImageButton::ALIGN_TOP
,
775 views::ImageButton::ALIGN_MIDDLE
,
776 views::ImageButton::ALIGN_MIDDLE
,
777 views::ImageButton::ALIGN_BOTTOM
));
782 case TYPE_BROWSER_SHORTCUT
: {
783 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
784 LauncherButton
* button
= LauncherButton::Create(
785 this, this, tooltip_
->shelf_layout_manager());
786 int image_id
= delegate_
?
787 delegate_
->GetBrowserShortcutResourceId() :
788 IDR_AURA_LAUNCHER_BROWSER_SHORTCUT
;
789 button
->SetImage(*rb
.GetImageNamed(image_id
).ToImageSkia());
797 view
->set_context_menu_controller(this);
798 view
->set_focus_border(new LauncherButtonFocusBorder
);
801 ConfigureChildView(view
);
805 void LauncherView::FadeIn(views::View
* view
) {
806 view
->SetVisible(true);
807 view
->layer()->SetOpacity(0);
808 AnimateToIdealBounds();
809 bounds_animator_
->SetAnimationDelegate(
810 view
, new FadeInAnimationDelegate(view
), true);
813 void LauncherView::PrepareForDrag(Pointer pointer
,
814 const ui::LocatedEvent
& event
) {
817 drag_pointer_
= pointer
;
818 start_drag_index_
= view_model_
->GetIndexOfView(drag_view_
);
820 // If the item is no longer draggable, bail out.
821 if (start_drag_index_
== -1 ||
822 !delegate_
->IsDraggable(model_
->items()[start_drag_index_
])) {
827 // Move the view to the front so that it appears on top of other views.
828 ReorderChildView(drag_view_
, -1);
829 bounds_animator_
->StopAnimatingView(drag_view_
);
832 void LauncherView::ContinueDrag(const ui::LocatedEvent
& event
) {
833 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
835 // TODO: I don't think this works correctly with RTL.
836 gfx::Point
drag_point(event
.location());
837 views::View::ConvertPointToTarget(drag_view_
, this, &drag_point
);
838 int current_index
= view_model_
->GetIndexOfView(drag_view_
);
839 DCHECK_NE(-1, current_index
);
841 // If the item is no longer draggable, bail out.
842 if (current_index
== -1 ||
843 !delegate_
->IsDraggable(model_
->items()[current_index
])) {
848 // Constrain the location to the range of valid indices for the type.
849 std::pair
<int, int> indices(GetDragRange(current_index
));
850 int first_drag_index
= indices
.first
;
851 int last_drag_index
= indices
.second
;
852 // If the last index isn't valid, we're overflowing. Constrain to the app list
853 // (which is the last visible item).
854 if (first_drag_index
< model_
->FirstPanelIndex() &&
855 last_drag_index
> last_visible_index_
)
856 last_drag_index
= last_visible_index_
;
858 if (shelf
->IsHorizontalAlignment()) {
859 x
= std::max(view_model_
->ideal_bounds(indices
.first
).x(),
860 drag_point
.x() - drag_offset_
);
861 x
= std::min(view_model_
->ideal_bounds(last_drag_index
).right() -
862 view_model_
->ideal_bounds(current_index
).width(),
864 if (drag_view_
->x() == x
)
868 y
= std::max(view_model_
->ideal_bounds(indices
.first
).y(),
869 drag_point
.y() - drag_offset_
);
870 y
= std::min(view_model_
->ideal_bounds(last_drag_index
).bottom() -
871 view_model_
->ideal_bounds(current_index
).height(),
873 if (drag_view_
->y() == y
)
879 views::ViewModelUtils::DetermineMoveIndex(
880 *view_model_
, drag_view_
,
881 shelf
->IsHorizontalAlignment() ?
882 views::ViewModelUtils::HORIZONTAL
:
883 views::ViewModelUtils::VERTICAL
,
886 std::min(indices
.second
, std::max(target_index
, indices
.first
));
887 if (target_index
== current_index
)
890 // Change the model, the LauncherItemMoved() callback will handle the
891 // |view_model_| update.
892 model_
->Move(current_index
, target_index
);
893 bounds_animator_
->StopAnimatingView(drag_view_
);
896 bool LauncherView::SameDragType(LauncherItemType typea
,
897 LauncherItemType typeb
) const {
900 case TYPE_PLATFORM_APP
:
901 return (typeb
== TYPE_TABBED
|| typeb
== TYPE_PLATFORM_APP
);
902 case TYPE_APP_SHORTCUT
:
903 case TYPE_WINDOWED_APP
:
906 case TYPE_BROWSER_SHORTCUT
:
907 return typeb
== typea
;
913 std::pair
<int, int> LauncherView::GetDragRange(int index
) {
916 LauncherItemType type
= model_
->items()[index
].type
;
917 for (int i
= 0; i
< model_
->item_count(); ++i
) {
918 if (SameDragType(model_
->items()[i
].type
, type
)) {
924 return std::pair
<int, int>(min_index
, max_index
);
927 void LauncherView::ConfigureChildView(views::View
* view
) {
928 view
->SetPaintToLayer(true);
929 view
->layer()->SetFillsBoundsOpaquely(false);
932 void LauncherView::ToggleOverflowBubble() {
933 if (IsShowingOverflowBubble()) {
934 overflow_bubble_
->Hide();
938 if (!overflow_bubble_
.get())
939 overflow_bubble_
.reset(new OverflowBubble());
941 LauncherView
* overflow_view
= new LauncherView(
942 model_
, delegate_
, tooltip_
->shelf_layout_manager());
943 overflow_view
->Init();
944 overflow_view
->OnShelfAlignmentChanged();
945 UpdateOverflowRange(overflow_view
);
947 overflow_bubble_
->Show(overflow_button_
, overflow_view
);
949 Shell::GetInstance()->UpdateShelfVisibility();
952 void LauncherView::UpdateFirstButtonPadding() {
953 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
955 // Creates an empty border for first launcher button to make included leading
956 // inset act as the button's padding. This is only needed on button creation
957 // and when shelf alignment changes.
958 if (view_model_
->view_size() > 0) {
959 view_model_
->view_at(0)->set_border(views::Border::CreateEmptyBorder(
960 shelf
->PrimaryAxisValue(0, leading_inset()),
961 shelf
->PrimaryAxisValue(leading_inset(), 0),
967 void LauncherView::OnFadeOutAnimationEnded() {
968 AnimateToIdealBounds();
970 // If overflow button is visible and there is a valid new last item, fading
971 // the new last item in after sliding animation is finished.
972 if (overflow_button_
->visible() && last_visible_index_
>= 0) {
973 views::View
* last_visible_view
= view_model_
->view_at(last_visible_index_
);
974 last_visible_view
->layer()->SetOpacity(0);
975 bounds_animator_
->SetAnimationDelegate(
977 new LauncherView::StartFadeAnimationDelegate(this, last_visible_view
),
982 void LauncherView::UpdateOverflowRange(LauncherView
* overflow_view
) {
983 const int first_overflow_index
= last_visible_index_
+ 1;
984 const int last_overflow_index
= last_hidden_index_
;
985 DCHECK_LE(first_overflow_index
, last_overflow_index
);
986 DCHECK_LT(last_overflow_index
, view_model_
->view_size());
988 overflow_view
->first_visible_index_
= first_overflow_index
;
989 overflow_view
->last_visible_index_
= last_overflow_index
;
992 bool LauncherView::ShouldHideTooltip(const gfx::Point
& cursor_location
) {
993 gfx::Rect active_bounds
;
995 for (int i
= 0; i
< child_count(); ++i
) {
996 views::View
* child
= child_at(i
);
997 if (child
== overflow_button_
)
999 if (!ShouldShowTooltipForView(child
))
1002 gfx::Rect child_bounds
= child
->GetMirroredBounds();
1003 active_bounds
.Union(child_bounds
);
1006 return !active_bounds
.Contains(cursor_location
);
1009 int LauncherView::CancelDrag(int modified_index
) {
1011 return modified_index
;
1012 bool was_dragging
= dragging();
1013 int drag_view_index
= view_model_
->GetIndexOfView(drag_view_
);
1014 drag_pointer_
= NONE
;
1016 if (drag_view_index
== modified_index
) {
1017 // The view that was being dragged is being modified. Don't do anything.
1018 return modified_index
;
1021 return modified_index
;
1023 // Restore previous position, tracking the position of the modified view.
1024 bool at_end
= modified_index
== view_model_
->view_size();
1025 views::View
* modified_view
=
1026 (modified_index
>= 0 && !at_end
) ?
1027 view_model_
->view_at(modified_index
) : NULL
;
1028 model_
->Move(drag_view_index
, start_drag_index_
);
1030 // If the modified view will be at the end of the list, return the new end of
1033 return view_model_
->view_size();
1034 return modified_view
? view_model_
->GetIndexOfView(modified_view
) : -1;
1037 gfx::Size
LauncherView::GetPreferredSize() {
1038 IdealBounds ideal_bounds
;
1039 CalculateIdealBounds(&ideal_bounds
);
1041 const int app_list_index
= view_model_
->view_size() - 1;
1042 const int last_button_index
= is_overflow_mode() ?
1043 last_visible_index_
: app_list_index
;
1044 const gfx::Rect last_button_bounds
=
1045 last_button_index
>= first_visible_index_
?
1046 view_model_
->view_at(last_button_index
)->bounds() :
1047 gfx::Rect(gfx::Size(kLauncherPreferredSize
,
1048 kLauncherPreferredSize
));
1050 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
1052 if (shelf
->IsHorizontalAlignment()) {
1053 return gfx::Size(last_button_bounds
.right() + leading_inset(),
1054 kLauncherPreferredSize
);
1057 return gfx::Size(kLauncherPreferredSize
,
1058 last_button_bounds
.bottom() + leading_inset());
1061 void LauncherView::OnBoundsChanged(const gfx::Rect
& previous_bounds
) {
1062 LayoutToIdealBounds();
1063 FOR_EACH_OBSERVER(LauncherIconObserver
, observers_
,
1064 OnLauncherIconPositionsChanged());
1066 if (IsShowingOverflowBubble())
1067 overflow_bubble_
->Hide();
1070 views::FocusTraversable
* LauncherView::GetPaneFocusTraversable() {
1074 void LauncherView::OnGestureEvent(ui::GestureEvent
* event
) {
1075 if (gesture_handler_
.ProcessGestureEvent(*event
))
1076 event
->StopPropagation();
1079 void LauncherView::LauncherItemAdded(int model_index
) {
1081 base::AutoReset
<bool> cancelling_drag(
1082 &cancelling_drag_model_changed_
, true);
1083 model_index
= CancelDrag(model_index
);
1085 views::View
* view
= CreateViewForItem(model_
->items()[model_index
]);
1087 // Hide the view, it'll be made visible when the animation is done. Using
1088 // opacity 0 here to avoid messing with CalculateIdealBounds which touches
1089 // the view's visibility.
1090 view
->layer()->SetOpacity(0);
1091 view_model_
->Add(view
, model_index
);
1093 // Give the button its ideal bounds. That way if we end up animating the
1094 // button before this animation completes it doesn't appear at some random
1095 // spot (because it was in the middle of animating from 0,0 0x0 to its
1097 IdealBounds ideal_bounds
;
1098 CalculateIdealBounds(&ideal_bounds
);
1099 view
->SetBoundsRect(view_model_
->ideal_bounds(model_index
));
1101 // The first animation moves all the views to their target position. |view|
1102 // is hidden, so it visually appears as though we are providing space for
1103 // it. When done we'll fade the view in.
1104 AnimateToIdealBounds();
1105 if (model_index
<= last_visible_index_
||
1106 model_index
>= model_
->FirstPanelIndex()) {
1107 bounds_animator_
->SetAnimationDelegate(
1108 view
, new StartFadeAnimationDelegate(this, view
), true);
1110 // Undo the hiding if animation does not run.
1111 view
->layer()->SetOpacity(1.0f
);
1115 void LauncherView::LauncherItemRemoved(int model_index
, LauncherID id
) {
1116 #if !defined(OS_MACOSX)
1117 if (id
== context_menu_id_
)
1118 launcher_menu_runner_
.reset();
1121 base::AutoReset
<bool> cancelling_drag(
1122 &cancelling_drag_model_changed_
, true);
1123 model_index
= CancelDrag(model_index
);
1125 views::View
* view
= view_model_
->view_at(model_index
);
1126 view_model_
->Remove(model_index
);
1127 // The first animation fades out the view. When done we'll animate the rest of
1128 // the views to their target location.
1129 bounds_animator_
->AnimateViewTo(view
, view
->bounds());
1130 bounds_animator_
->SetAnimationDelegate(
1131 view
, new FadeOutAnimationDelegate(this, view
), true);
1133 // If overflow bubble is visible, sanitize overflow range first and when the
1134 // above animation finishes, CalculateIdealBounds will be called to get
1135 // correct overflow range. CalculateIdealBounds could hide overflow bubble
1136 // and triggers LauncherItemChanged. And since we are still in the middle
1137 // of LauncherItemRemoved, LauncherView in overflow bubble is not synced
1138 // with LauncherModel and will crash.
1139 if (overflow_bubble_
&& overflow_bubble_
->IsShowing()) {
1140 last_hidden_index_
= std::min(last_hidden_index_
,
1141 view_model_
->view_size() - 1);
1142 UpdateOverflowRange(overflow_bubble_
->launcher_view());
1146 void LauncherView::LauncherItemChanged(int model_index
,
1147 const ash::LauncherItem
& old_item
) {
1148 const LauncherItem
& item(model_
->items()[model_index
]);
1149 if (old_item
.type
!= item
.type
) {
1150 // Type changed, swap the views.
1151 model_index
= CancelDrag(model_index
);
1152 scoped_ptr
<views::View
> old_view(view_model_
->view_at(model_index
));
1153 bounds_animator_
->StopAnimatingView(old_view
.get());
1154 view_model_
->Remove(model_index
);
1155 views::View
* new_view
= CreateViewForItem(item
);
1156 AddChildView(new_view
);
1157 view_model_
->Add(new_view
, model_index
);
1158 new_view
->SetBoundsRect(old_view
->bounds());
1162 views::View
* view
= view_model_
->view_at(model_index
);
1163 switch (item
.type
) {
1165 TabbedLauncherButton
* button
= static_cast<TabbedLauncherButton
*>(view
);
1166 gfx::Size pref
= button
->GetPreferredSize();
1167 button
->SetTabImage(item
.image
);
1168 if (pref
!= button
->GetPreferredSize())
1169 AnimateToIdealBounds();
1171 button
->SchedulePaint();
1172 ReflectItemStatus(item
, button
);
1175 case TYPE_BROWSER_SHORTCUT
:
1176 if (!Shell::IsLauncherPerDisplayEnabled())
1178 // Fallthrough for the new Launcher since it needs to show the activation
1180 case TYPE_APP_SHORTCUT
:
1181 case TYPE_WINDOWED_APP
:
1182 case TYPE_PLATFORM_APP
:
1183 case TYPE_APP_PANEL
: {
1184 LauncherButton
* button
= static_cast<LauncherButton
*>(view
);
1185 ReflectItemStatus(item
, button
);
1186 // The browser shortcut is currently not a "real" item and as such the
1187 // the image is bogous as well. We therefore keep the image as is for it.
1188 if (item
.type
!= TYPE_BROWSER_SHORTCUT
)
1189 button
->SetImage(item
.image
);
1190 button
->SchedulePaint();
1199 void LauncherView::LauncherItemMoved(int start_index
, int target_index
) {
1200 view_model_
->Move(start_index
, target_index
);
1201 // When cancelling a drag due to a launcher item being added, the currently
1202 // dragged item is moved back to its initial position. AnimateToIdealBounds
1203 // will be called again when the new item is added to the |view_model_| but
1204 // at this time the |view_model_| is inconsistent with the |model_|.
1205 if (!cancelling_drag_model_changed_
)
1206 AnimateToIdealBounds();
1209 void LauncherView::LauncherStatusChanged() {
1210 AppListButton
* app_list_button
=
1211 static_cast<AppListButton
*>(GetAppListButtonView());
1212 if (model_
->status() == LauncherModel::STATUS_LOADING
)
1213 app_list_button
->StartLoadingAnimation();
1215 app_list_button
->StopLoadingAnimation();
1218 void LauncherView::PointerPressedOnButton(views::View
* view
,
1220 const ui::LocatedEvent
& event
) {
1225 int index
= view_model_
->GetIndexOfView(view
);
1227 view_model_
->view_size() <= 1 ||
1228 !delegate_
->IsDraggable(model_
->items()[index
]))
1229 return; // View is being deleted or not draggable, ignore request.
1231 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
1234 drag_offset_
= shelf
->PrimaryAxisValue(event
.x(), event
.y());
1237 void LauncherView::PointerDraggedOnButton(views::View
* view
,
1239 const ui::LocatedEvent
& event
) {
1240 ShelfLayoutManager
* shelf
= tooltip_
->shelf_layout_manager();
1241 if (!dragging() && drag_view_
&&
1242 shelf
->PrimaryAxisValue(abs(event
.x() - drag_offset_
),
1243 abs(event
.y() - drag_offset_
)) >=
1244 kMinimumDragDistance
) {
1245 PrepareForDrag(pointer
, event
);
1247 if (drag_pointer_
== pointer
)
1248 ContinueDrag(event
);
1251 void LauncherView::PointerReleasedOnButton(views::View
* view
,
1256 } else if (drag_pointer_
== pointer
) {
1257 drag_pointer_
= NONE
;
1259 AnimateToIdealBounds();
1263 void LauncherView::MouseMovedOverButton(views::View
* view
) {
1264 if (!ShouldShowTooltipForView(view
))
1267 if (!tooltip_
->IsVisible())
1268 tooltip_
->ResetTimer();
1271 void LauncherView::MouseEnteredButton(views::View
* view
) {
1272 if (!ShouldShowTooltipForView(view
))
1275 if (tooltip_
->IsVisible()) {
1276 tooltip_
->ShowImmediately(view
, GetAccessibleName(view
));
1278 tooltip_
->ShowDelayed(view
, GetAccessibleName(view
));
1282 void LauncherView::MouseExitedButton(views::View
* view
) {
1283 if (!tooltip_
->IsVisible())
1284 tooltip_
->StopTimer();
1287 string16
LauncherView::GetAccessibleName(const views::View
* view
) {
1288 int view_index
= view_model_
->GetIndexOfView(view
);
1289 // May be -1 while in the process of animating closed.
1290 if (view_index
== -1)
1293 switch (model_
->items()[view_index
].type
) {
1295 case TYPE_APP_PANEL
:
1296 case TYPE_APP_SHORTCUT
:
1297 case TYPE_WINDOWED_APP
:
1298 case TYPE_PLATFORM_APP
:
1299 return delegate_
->GetTitle(model_
->items()[view_index
]);
1302 return model_
->status() == LauncherModel::STATUS_LOADING
?
1303 l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_SYNCING_TITLE
) :
1304 l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE
);
1306 case TYPE_BROWSER_SHORTCUT
:
1307 return Shell::GetInstance()->delegate()->GetProductName();
1312 void LauncherView::ButtonPressed(views::Button
* sender
,
1313 const ui::Event
& event
) {
1314 // Do not handle mouse release during drag.
1318 if (sender
== overflow_button_
) {
1319 ToggleOverflowBubble();
1323 int view_index
= view_model_
->GetIndexOfView(sender
);
1324 // May be -1 while in the process of animating closed.
1325 if (view_index
== -1)
1328 // If the previous menu was closed by the same event as this one, we ignore
1330 if (!IsUsableEvent(event
))
1336 // Slow down activation animations if shift key is pressed.
1337 scoped_ptr
<ui::ScopedAnimationDurationScaleMode
> slowing_animations
;
1338 if (event
.IsShiftDown()) {
1339 slowing_animations
.reset(new ui::ScopedAnimationDurationScaleMode(
1340 ui::ScopedAnimationDurationScaleMode::SLOW_DURATION
));
1343 // Collect usage statistics before we decide what to do with the click.
1344 switch (model_
->items()[view_index
].type
) {
1345 case TYPE_APP_SHORTCUT
:
1346 case TYPE_WINDOWED_APP
:
1347 case TYPE_PLATFORM_APP
:
1348 Shell::GetInstance()->delegate()->RecordUserMetricsAction(
1349 UMA_LAUNCHER_CLICK_ON_APP
);
1352 case TYPE_APP_PANEL
:
1353 delegate_
->ItemClicked(model_
->items()[view_index
], event
);
1357 Shell::GetInstance()->delegate()->RecordUserMetricsAction(
1358 UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON
);
1359 Shell::GetInstance()->ToggleAppList(GetWidget()->GetNativeView());
1362 case TYPE_BROWSER_SHORTCUT
:
1363 // Click on browser icon is counted in app clicks.
1364 Shell::GetInstance()->delegate()->RecordUserMetricsAction(
1365 UMA_LAUNCHER_CLICK_ON_APP
);
1366 delegate_
->OnBrowserShortcutClicked(event
.flags());
1371 if (model_
->items()[view_index
].type
!= TYPE_APP_LIST
)
1372 ShowListMenuForView(model_
->items()[view_index
], sender
, event
.flags());
1375 bool LauncherView::ShowListMenuForView(const LauncherItem
& item
,
1376 views::View
* source
,
1378 scoped_ptr
<ash::LauncherMenuModel
> menu_model
;
1379 menu_model
.reset(delegate_
->CreateApplicationMenu(item
, event_flags
));
1381 // Make sure we have a menu and it has at least two items in addition to the
1382 // application title and the 3 spacing separators.
1383 if (!menu_model
.get() || menu_model
->GetItemCount() <= 5)
1386 ShowMenu(scoped_ptr
<views::MenuModelAdapter
>(
1387 new LauncherMenuModelAdapter(menu_model
.get())),
1394 void LauncherView::ShowContextMenuForView(views::View
* source
,
1395 const gfx::Point
& point
) {
1396 int view_index
= view_model_
->GetIndexOfView(source
);
1397 if (view_index
!= -1 &&
1398 model_
->items()[view_index
].type
== TYPE_APP_LIST
) {
1402 if (view_index
== -1) {
1403 Shell::GetInstance()->ShowContextMenu(point
);
1406 scoped_ptr
<ui::MenuModel
> menu_model(delegate_
->CreateContextMenu(
1407 model_
->items()[view_index
],
1408 source
->GetWidget()->GetNativeView()->GetRootWindow()));
1409 if (!menu_model
.get())
1411 base::AutoReset
<LauncherID
> reseter(
1413 view_index
== -1 ? 0 : model_
->items()[view_index
].id
);
1415 ShowMenu(scoped_ptr
<views::MenuModelAdapter
>(
1416 new views::MenuModelAdapter(menu_model
.get())),
1422 void LauncherView::ShowMenu(
1423 scoped_ptr
<views::MenuModelAdapter
> menu_model_adapter
,
1424 views::View
* source
,
1425 const gfx::Point
& click_point
,
1426 bool context_menu
) {
1427 closing_event_time_
= base::TimeDelta();
1428 launcher_menu_runner_
.reset(
1429 new views::MenuRunner(menu_model_adapter
->CreateMenu()));
1431 // Determine the menu alignment dependent on the shelf.
1432 views::MenuItemView::AnchorPosition menu_alignment
=
1433 views::MenuItemView::TOPLEFT
;
1434 gfx::Rect anchor_point
= gfx::Rect(click_point
, gfx::Size());
1436 ShelfWidget
* shelf
= RootWindowController::ForLauncher(
1437 GetWidget()->GetNativeView())->shelf();
1438 if (!context_menu
) {
1439 // Application lists use a bubble.
1440 ash::ShelfAlignment align
= shelf
->GetAlignment();
1441 anchor_point
= source
->GetBoundsInScreen();
1443 // Launcher items can have an asymmetrical border for spacing reasons.
1444 // Adjust anchor location for this.
1445 if (source
->border())
1446 anchor_point
.Inset(source
->border()->GetInsets());
1449 case ash::SHELF_ALIGNMENT_BOTTOM
:
1450 menu_alignment
= views::MenuItemView::BUBBLE_ABOVE
;
1452 case ash::SHELF_ALIGNMENT_LEFT
:
1453 menu_alignment
= views::MenuItemView::BUBBLE_RIGHT
;
1455 case ash::SHELF_ALIGNMENT_RIGHT
:
1456 menu_alignment
= views::MenuItemView::BUBBLE_LEFT
;
1458 case ash::SHELF_ALIGNMENT_TOP
:
1459 menu_alignment
= views::MenuItemView::BUBBLE_BELOW
;
1463 // If this gets deleted while we are in the menu, the launcher will be gone
1465 bool got_deleted
= false;
1466 got_deleted_
= &got_deleted
;
1468 shelf
->ForceUndimming(true);
1469 // NOTE: if you convert to HAS_MNEMONICS be sure and update menu building
1471 if (launcher_menu_runner_
->RunMenuAt(
1472 source
->GetWidget(),
1476 views::MenuRunner::CONTEXT_MENU
) == views::MenuRunner::MENU_DELETED
) {
1478 got_deleted_
= NULL
;
1479 shelf
->ForceUndimming(false);
1483 got_deleted_
= NULL
;
1484 shelf
->ForceUndimming(false);
1486 // Unpinning an item will reset the |launcher_menu_runner_| before coming
1488 if (launcher_menu_runner_
.get())
1489 closing_event_time_
= launcher_menu_runner_
->closing_event_time();
1490 Shell::GetInstance()->UpdateShelfVisibility();
1493 void LauncherView::OnBoundsAnimatorProgressed(views::BoundsAnimator
* animator
) {
1494 FOR_EACH_OBSERVER(LauncherIconObserver
, observers_
,
1495 OnLauncherIconPositionsChanged());
1496 PreferredSizeChanged();
1499 void LauncherView::OnBoundsAnimatorDone(views::BoundsAnimator
* animator
) {
1502 bool LauncherView::IsUsableEvent(const ui::Event
& event
) {
1503 if (closing_event_time_
== base::TimeDelta())
1506 base::TimeDelta delta
=
1507 base::TimeDelta(event
.time_stamp() - closing_event_time_
);
1508 closing_event_time_
= base::TimeDelta();
1509 // TODO(skuhne): This time seems excessive, but it appears that the reposting
1510 // takes that long. Need to come up with a better way of doing this.
1511 return (delta
.InMilliseconds() < 0 || delta
.InMilliseconds() > 130);
1514 const LauncherItem
* LauncherView::LauncherItemForView(
1515 const views::View
* view
) const {
1516 int view_index
= view_model_
->GetIndexOfView(view
);
1517 if (view_index
== -1)
1519 return &(model_
->items()[view_index
]);
1522 bool LauncherView::ShouldShowTooltipForView(const views::View
* view
) const {
1523 if (view
== GetAppListButtonView() &&
1524 Shell::GetInstance()->GetAppListWindow())
1526 const LauncherItem
* item
= LauncherItemForView(view
);
1527 return (!item
|| delegate_
->ShouldShowTooltip(*item
));
1530 } // namespace internal