Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1472)

Unified Diff: ash/common/shelf/shelf_view.cc

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ash/common/shelf/shelf_view.h ('k') | ash/common/shelf/shelf_widget.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ash/common/shelf/shelf_view.cc
diff --git a/ash/common/shelf/shelf_view.cc b/ash/common/shelf/shelf_view.cc
deleted file mode 100644
index c46b168b96fe3cc818aa5e87dc511376a85e2528..0000000000000000000000000000000000000000
--- a/ash/common/shelf/shelf_view.cc
+++ /dev/null
@@ -1,1779 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/common/shelf/shelf_view.h"
-
-#include <algorithm>
-#include <memory>
-
-#include "ash/common/ash_constants.h"
-#include "ash/common/drag_drop/drag_image_view.h"
-#include "ash/common/scoped_root_window_for_new_windows.h"
-#include "ash/common/shelf/app_list_button.h"
-#include "ash/common/shelf/overflow_bubble.h"
-#include "ash/common/shelf/overflow_bubble_view.h"
-#include "ash/common/shelf/overflow_button.h"
-#include "ash/common/shelf/shelf_application_menu_model.h"
-#include "ash/common/shelf/shelf_button.h"
-#include "ash/common/shelf/shelf_constants.h"
-#include "ash/common/shelf/shelf_delegate.h"
-#include "ash/common/shelf/shelf_model.h"
-#include "ash/common/shelf/shelf_widget.h"
-#include "ash/common/shelf/wm_shelf.h"
-#include "ash/common/shell_delegate.h"
-#include "ash/common/wm/root_window_finder.h"
-#include "ash/common/wm_shell.h"
-#include "ash/common/wm_window.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "base/auto_reset.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "ui/accessibility/ax_node_data.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/models/simple_menu_model.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/layer_animator.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
-#include "ui/events/event_utils.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/views/animation/bounds_animator.h"
-#include "ui/views/border.h"
-#include "ui/views/controls/button/image_button.h"
-#include "ui/views/controls/menu/menu_model_adapter.h"
-#include "ui/views/controls/menu/menu_runner.h"
-#include "ui/views/focus/focus_search.h"
-#include "ui/views/view_model.h"
-#include "ui/views/view_model_utils.h"
-#include "ui/views/widget/widget.h"
-#include "ui/wm/core/coordinate_conversion.h"
-
-using gfx::Animation;
-using views::View;
-
-namespace ash {
-
-const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_BOTTOM = 0;
-const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_LEFT = 1;
-const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_RIGHT = 2;
-const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_COUNT = 3;
-
-// Default amount content is inset on the left edge.
-const int kDefaultLeadingInset = 8;
-
-// The proportion of the shelf space reserved for non-panel icons. Panels
-// may flow into this space but will be put into the overflow bubble if there
-// is contention for the space.
-const float kReservedNonPanelIconProportion = 0.67f;
-
-// The distance of the cursor from the outer rim of the shelf before it
-// separates.
-const int kRipOffDistance = 48;
-
-// The rip off drag and drop proxy image should get scaled by this factor.
-const float kDragAndDropProxyScale = 1.5f;
-
-// The opacity represents that this partially disappeared item will get removed.
-const float kDraggedImageOpacity = 0.5f;
-
-namespace {
-
-// A class to temporarily disable a given bounds animator.
-class BoundsAnimatorDisabler {
- public:
- explicit BoundsAnimatorDisabler(views::BoundsAnimator* bounds_animator)
- : old_duration_(bounds_animator->GetAnimationDuration()),
- bounds_animator_(bounds_animator) {
- bounds_animator_->SetAnimationDuration(1);
- }
-
- ~BoundsAnimatorDisabler() {
- bounds_animator_->SetAnimationDuration(old_duration_);
- }
-
- private:
- // The previous animation duration.
- int old_duration_;
- // The bounds animator which gets used.
- views::BoundsAnimator* bounds_animator_;
-
- DISALLOW_COPY_AND_ASSIGN(BoundsAnimatorDisabler);
-};
-
-// Custom FocusSearch used to navigate the shelf in the order items are in
-// the ViewModel.
-class ShelfFocusSearch : public views::FocusSearch {
- public:
- explicit ShelfFocusSearch(views::ViewModel* view_model)
- : FocusSearch(nullptr, true, true), view_model_(view_model) {}
- ~ShelfFocusSearch() override {}
-
- // views::FocusSearch overrides:
- View* FindNextFocusableView(View* starting_view,
- bool reverse,
- Direction direction,
- bool check_starting_view,
- views::FocusTraversable** focus_traversable,
- View** focus_traversable_view) override {
- int index = view_model_->GetIndexOfView(starting_view);
- if (index == -1)
- return view_model_->view_at(0);
-
- if (reverse) {
- --index;
- if (index < 0)
- index = view_model_->view_size() - 1;
- } else {
- ++index;
- if (index >= view_model_->view_size())
- index = 0;
- }
- return view_model_->view_at(index);
- }
-
- private:
- views::ViewModel* view_model_;
-
- DISALLOW_COPY_AND_ASSIGN(ShelfFocusSearch);
-};
-
-// AnimationDelegate used when inserting a new item. This steadily increases the
-// opacity of the layer as the animation progress.
-class FadeInAnimationDelegate : public gfx::AnimationDelegate {
- public:
- explicit FadeInAnimationDelegate(views::View* view) : view_(view) {}
- ~FadeInAnimationDelegate() override {}
-
- // AnimationDelegate overrides:
- void AnimationProgressed(const Animation* animation) override {
- view_->layer()->SetOpacity(animation->GetCurrentValue());
- view_->layer()->ScheduleDraw();
- }
- void AnimationEnded(const Animation* animation) override {
- view_->layer()->SetOpacity(1.0f);
- view_->layer()->ScheduleDraw();
- }
- void AnimationCanceled(const Animation* animation) override {
- view_->layer()->SetOpacity(1.0f);
- view_->layer()->ScheduleDraw();
- }
-
- private:
- views::View* view_;
-
- DISALLOW_COPY_AND_ASSIGN(FadeInAnimationDelegate);
-};
-
-void ReflectItemStatus(const ShelfItem& item, ShelfButton* button) {
- switch (item.status) {
- case STATUS_CLOSED:
- button->ClearState(ShelfButton::STATE_ACTIVE);
- button->ClearState(ShelfButton::STATE_RUNNING);
- button->ClearState(ShelfButton::STATE_ATTENTION);
- break;
- case STATUS_RUNNING:
- button->ClearState(ShelfButton::STATE_ACTIVE);
- button->AddState(ShelfButton::STATE_RUNNING);
- button->ClearState(ShelfButton::STATE_ATTENTION);
- break;
- case STATUS_ACTIVE:
- button->AddState(ShelfButton::STATE_ACTIVE);
- button->ClearState(ShelfButton::STATE_RUNNING);
- button->ClearState(ShelfButton::STATE_ATTENTION);
- break;
- case STATUS_ATTENTION:
- button->ClearState(ShelfButton::STATE_ACTIVE);
- button->ClearState(ShelfButton::STATE_RUNNING);
- button->AddState(ShelfButton::STATE_ATTENTION);
- break;
- }
-}
-
-} // namespace
-
-// AnimationDelegate used when deleting an item. This steadily decreased the
-// opacity of the layer as the animation progress.
-class ShelfView::FadeOutAnimationDelegate : public gfx::AnimationDelegate {
- public:
- FadeOutAnimationDelegate(ShelfView* host, views::View* view)
- : shelf_view_(host), view_(view) {}
- ~FadeOutAnimationDelegate() override {}
-
- // AnimationDelegate overrides:
- void AnimationProgressed(const Animation* animation) override {
- view_->layer()->SetOpacity(1 - animation->GetCurrentValue());
- view_->layer()->ScheduleDraw();
- }
- void AnimationEnded(const Animation* animation) override {
- shelf_view_->OnFadeOutAnimationEnded();
- }
- void AnimationCanceled(const Animation* animation) override {}
-
- private:
- ShelfView* shelf_view_;
- std::unique_ptr<views::View> view_;
-
- DISALLOW_COPY_AND_ASSIGN(FadeOutAnimationDelegate);
-};
-
-// AnimationDelegate used to trigger fading an element in. When an item is
-// inserted this delegate is attached to the animation that expands the size of
-// the item. When done it kicks off another animation to fade the item in.
-class ShelfView::StartFadeAnimationDelegate : public gfx::AnimationDelegate {
- public:
- StartFadeAnimationDelegate(ShelfView* host, views::View* view)
- : shelf_view_(host), view_(view) {}
- ~StartFadeAnimationDelegate() override {}
-
- // AnimationDelegate overrides:
- void AnimationEnded(const Animation* animation) override {
- shelf_view_->FadeIn(view_);
- }
- void AnimationCanceled(const Animation* animation) override {
- view_->layer()->SetOpacity(1.0f);
- }
-
- private:
- ShelfView* shelf_view_;
- views::View* view_;
-
- DISALLOW_COPY_AND_ASSIGN(StartFadeAnimationDelegate);
-};
-
-// static
-const int ShelfView::kMinimumDragDistance = 8;
-
-ShelfView::ShelfView(ShelfModel* model,
- ShelfDelegate* delegate,
- WmShelf* wm_shelf,
- ShelfWidget* shelf_widget)
- : model_(model),
- delegate_(delegate),
- wm_shelf_(wm_shelf),
- shelf_widget_(shelf_widget),
- view_model_(new views::ViewModel),
- first_visible_index_(0),
- last_visible_index_(-1),
- overflow_button_(nullptr),
- owner_overflow_bubble_(nullptr),
- tooltip_(this),
- drag_pointer_(NONE),
- drag_view_(nullptr),
- start_drag_index_(-1),
- context_menu_id_(0),
- leading_inset_(kDefaultLeadingInset),
- cancelling_drag_model_changed_(false),
- last_hidden_index_(0),
- closing_event_time_(base::TimeTicks()),
- drag_and_drop_item_pinned_(false),
- drag_and_drop_shelf_id_(0),
- drag_replaced_view_(nullptr),
- dragged_off_shelf_(false),
- snap_back_from_rip_off_view_(nullptr),
- overflow_mode_(false),
- main_shelf_(nullptr),
- dragged_off_from_overflow_to_shelf_(false),
- is_repost_event_on_same_item_(false),
- last_pressed_index_(-1) {
- DCHECK(model_);
- DCHECK(delegate_);
- DCHECK(wm_shelf_);
- DCHECK(shelf_widget_);
- bounds_animator_.reset(new views::BoundsAnimator(this));
- bounds_animator_->AddObserver(this);
- set_context_menu_controller(this);
- focus_search_.reset(new ShelfFocusSearch(view_model_.get()));
-}
-
-ShelfView::~ShelfView() {
- bounds_animator_->RemoveObserver(this);
- model_->RemoveObserver(this);
-}
-
-void ShelfView::Init() {
- model_->AddObserver(this);
-
- const ShelfItems& items(model_->items());
- for (ShelfItems::const_iterator i = items.begin(); i != items.end(); ++i) {
- views::View* child = CreateViewForItem(*i);
- child->SetPaintToLayer();
- view_model_->Add(child, static_cast<int>(i - items.begin()));
- AddChildView(child);
- }
- overflow_button_ = new OverflowButton(this, wm_shelf_);
- overflow_button_->set_context_menu_controller(this);
- ConfigureChildView(overflow_button_);
- AddChildView(overflow_button_);
-
- // We'll layout when our bounds change.
-}
-
-void ShelfView::OnShelfAlignmentChanged() {
- overflow_button_->OnShelfAlignmentChanged();
- LayoutToIdealBounds();
- for (int i = 0; i < view_model_->view_size(); ++i) {
- if (i >= first_visible_index_ && i <= last_visible_index_)
- view_model_->view_at(i)->Layout();
- }
- tooltip_.Close();
- if (overflow_bubble_)
- overflow_bubble_->Hide();
- // For crbug.com/587931, because AppListButton layout logic is in OnPaint.
- AppListButton* app_list_button = GetAppListButton();
- if (app_list_button)
- app_list_button->SchedulePaint();
-}
-
-gfx::Rect ShelfView::GetIdealBoundsOfItemIcon(ShelfID id) {
- int index = model_->ItemIndexByID(id);
- if (index == -1)
- return gfx::Rect();
- // Map all items from overflow area to the overflow button. Note that the
- // section between last_index_hidden_ and model_->FirstPanelIndex() is the
- // list of invisible panel items. However, these items are currently nowhere
- // represented and get dropped instead - see (crbug.com/378907). As such there
- // is no way to address them or place them. We therefore move them over the
- // overflow button.
- if (index > last_visible_index_ && index < model_->FirstPanelIndex())
- index = last_visible_index_ + 1;
- const gfx::Rect& ideal_bounds(view_model_->ideal_bounds(index));
- DCHECK_NE(TYPE_APP_LIST, model_->items()[index].type);
- views::View* view = view_model_->view_at(index);
- CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
- ShelfButton* button = static_cast<ShelfButton*>(view);
- gfx::Rect icon_bounds = button->GetIconBounds();
- return gfx::Rect(GetMirroredXWithWidthInView(
- ideal_bounds.x() + icon_bounds.x(), icon_bounds.width()),
- ideal_bounds.y() + icon_bounds.y(), icon_bounds.width(),
- icon_bounds.height());
-}
-
-void ShelfView::UpdatePanelIconPosition(ShelfID id,
- const gfx::Point& midpoint) {
- int current_index = model_->ItemIndexByID(id);
- int first_panel_index = model_->FirstPanelIndex();
- if (current_index < first_panel_index)
- return;
-
- gfx::Point midpoint_in_view(GetMirroredXInView(midpoint.x()), midpoint.y());
- int target_index = current_index;
- while (
- target_index > first_panel_index &&
- wm_shelf_->PrimaryAxisValue(view_model_->ideal_bounds(target_index).x(),
- view_model_->ideal_bounds(target_index).y()) >
- wm_shelf_->PrimaryAxisValue(midpoint_in_view.x(),
- midpoint_in_view.y())) {
- --target_index;
- }
- while (target_index < view_model_->view_size() - 1 &&
- wm_shelf_->PrimaryAxisValue(
- view_model_->ideal_bounds(target_index).right(),
- view_model_->ideal_bounds(target_index).bottom()) <
- wm_shelf_->PrimaryAxisValue(midpoint_in_view.x(),
- midpoint_in_view.y())) {
- ++target_index;
- }
- if (current_index != target_index)
- model_->Move(current_index, target_index);
-}
-
-bool ShelfView::IsShowingMenu() const {
- return launcher_menu_runner_.get() && launcher_menu_runner_->IsRunning();
-}
-
-bool ShelfView::IsShowingOverflowBubble() const {
- return overflow_bubble_.get() && overflow_bubble_->IsShowing();
-}
-
-AppListButton* ShelfView::GetAppListButton() const {
- for (int i = 0; i < model_->item_count(); ++i) {
- if (model_->items()[i].type == TYPE_APP_LIST) {
- views::View* view = view_model_->view_at(i);
- CHECK_EQ(AppListButton::kViewClassName, view->GetClassName());
- return static_cast<AppListButton*>(view);
- }
- }
-
- NOTREACHED() << "Applist button not found";
- return nullptr;
-}
-
-bool ShelfView::ShouldHideTooltip(const gfx::Point& cursor_location) const {
- gfx::Rect tooltip_bounds;
- for (int i = 0; i < child_count(); ++i) {
- const views::View* child = child_at(i);
- if (child != overflow_button_ && ShouldShowTooltipForView(child))
- tooltip_bounds.Union(child->GetMirroredBounds());
- }
- return !tooltip_bounds.Contains(cursor_location);
-}
-
-bool ShelfView::ShouldShowTooltipForView(const views::View* view) const {
- // TODO(msw): Push this app list state into ShelfItem::shows_tooltip.
- if (view == GetAppListButton() && GetAppListButton()->is_showing_app_list())
- return false;
- const ShelfItem* item = ShelfItemForView(view);
- return item && item->shows_tooltip;
-}
-
-base::string16 ShelfView::GetTitleForView(const views::View* view) const {
- const ShelfItem* item = ShelfItemForView(view);
- return item ? item->title : base::string16();
-}
-
-gfx::Rect ShelfView::GetVisibleItemsBoundsInScreen() {
- gfx::Size preferred_size = GetPreferredSize();
- gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
- ConvertPointToScreen(this, &origin);
- return gfx::Rect(origin, preferred_size);
-}
-
-void ShelfView::ButtonPressed(views::Button* sender,
- const ui::Event& event,
- views::InkDrop* ink_drop) {
- if (sender == overflow_button_) {
- ToggleOverflowBubble();
- shelf_button_pressed_metric_tracker_.ButtonPressed(event, sender,
- SHELF_ACTION_NONE);
- return;
- }
-
- // None of the checks in ShouldEventActivateButton() affects overflow button.
- // So, it is safe to be checked after handling overflow button.
- if (!ShouldEventActivateButton(sender, event))
- return;
-
- // Record the index for the last pressed shelf item.
- last_pressed_index_ = view_model_->GetIndexOfView(sender);
- DCHECK_LT(-1, last_pressed_index_);
-
- // Place new windows on the same display as the button.
- WmWindow* window = WmWindow::Get(sender->GetWidget()->GetNativeWindow());
- scoped_root_window_for_new_windows_.reset(
- new ScopedRootWindowForNewWindows(window->GetRootWindow()));
-
- // Slow down activation animations if shift key is pressed.
- std::unique_ptr<ui::ScopedAnimationDurationScaleMode> slowing_animations;
- if (event.IsShiftDown()) {
- slowing_animations.reset(new ui::ScopedAnimationDurationScaleMode(
- ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
- }
-
- // Collect usage statistics before we decide what to do with the click.
- switch (model_->items()[last_pressed_index_].type) {
- case TYPE_APP_SHORTCUT:
- case TYPE_BROWSER_SHORTCUT:
- case TYPE_APP:
- WmShell::Get()->RecordUserMetricsAction(UMA_LAUNCHER_CLICK_ON_APP);
- break;
-
- case TYPE_APP_LIST:
- WmShell::Get()->RecordUserMetricsAction(
- UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);
- break;
-
- case TYPE_APP_PANEL:
- case TYPE_DIALOG:
- break;
-
- case TYPE_UNDEFINED:
- NOTREACHED() << "ShelfItemType must be set.";
- break;
- }
-
- const int64_t display_id = window->GetDisplayNearestWindow().id();
- ShelfAction performed_action =
- model_->GetShelfItemDelegate(model_->items()[last_pressed_index_].id)
- ->ItemSelected(event.type(), event.flags(), display_id,
- LAUNCH_FROM_UNKNOWN);
-
- shelf_button_pressed_metric_tracker_.ButtonPressed(event, sender,
- performed_action);
-
- // For the app list menu no TRIGGERED ink drop effect is needed and it
- // handles its own ACTIVATED/DEACTIVATED states.
- if (performed_action == SHELF_ACTION_NEW_WINDOW_CREATED ||
- (performed_action != SHELF_ACTION_APP_LIST_SHOWN &&
- !ShowListMenuForView(model_->items()[last_pressed_index_], sender, event,
- ink_drop))) {
- ink_drop->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
- }
- // Allow the menu to clear |scoped_root_window_for_new_windows_| during
- // OnMenuClosed.
- if (!IsShowingMenu())
- scoped_root_window_for_new_windows_.reset();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ShelfView, FocusTraversable implementation:
-
-views::FocusSearch* ShelfView::GetFocusSearch() {
- return focus_search_.get();
-}
-
-views::FocusTraversable* ShelfView::GetFocusTraversableParent() {
- return parent()->GetFocusTraversable();
-}
-
-View* ShelfView::GetFocusTraversableParentView() {
- return this;
-}
-
-void ShelfView::CreateDragIconProxy(
- const gfx::Point& location_in_screen_coordinates,
- const gfx::ImageSkia& icon,
- views::View* replaced_view,
- const gfx::Vector2d& cursor_offset_from_center,
- float scale_factor) {
- drag_replaced_view_ = replaced_view;
- WmWindow* root_window =
- WmWindow::Get(drag_replaced_view_->GetWidget()->GetNativeWindow())
- ->GetRootWindow();
- drag_image_.reset(new DragImageView(
- root_window, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE));
- drag_image_->SetImage(icon);
- gfx::Size size = drag_image_->GetPreferredSize();
- size.set_width(size.width() * scale_factor);
- size.set_height(size.height() * scale_factor);
- drag_image_offset_ = gfx::Vector2d(size.width() / 2, size.height() / 2) +
- cursor_offset_from_center;
- gfx::Rect drag_image_bounds(
- location_in_screen_coordinates - drag_image_offset_, size);
- drag_image_->SetBoundsInScreen(drag_image_bounds);
- drag_image_->SetWidgetVisible(true);
-}
-
-void ShelfView::UpdateDragIconProxy(
- const gfx::Point& location_in_screen_coordinates) {
- // TODO(jennyz): Investigate why drag_image_ becomes null at this point per
- // crbug.com/34722, while the app list item is still being dragged around.
- if (drag_image_) {
- drag_image_->SetScreenPosition(location_in_screen_coordinates -
- drag_image_offset_);
- }
-}
-
-void ShelfView::DestroyDragIconProxy() {
- drag_image_.reset();
- drag_image_offset_ = gfx::Vector2d(0, 0);
-}
-
-bool ShelfView::StartDrag(const std::string& app_id,
- const gfx::Point& location_in_screen_coordinates) {
- // Bail if an operation is already going on - or the cursor is not inside.
- // This could happen if mouse / touch operations overlap.
- if (drag_and_drop_shelf_id_ ||
- !GetBoundsInScreen().Contains(location_in_screen_coordinates))
- return false;
-
- // If the AppsGridView (which was dispatching this event) was opened by our
- // button, ShelfView dragging operations are locked and we have to unlock.
- CancelDrag(-1);
- drag_and_drop_item_pinned_ = false;
- drag_and_drop_app_id_ = app_id;
- drag_and_drop_shelf_id_ =
- delegate_->GetShelfIDForAppID(drag_and_drop_app_id_);
- // Check if the application is known and pinned - if not, we have to pin it so
- // that we can re-arrange the shelf order accordingly. Note that items have
- // to be pinned to give them the same (order) possibilities as a shortcut.
- // When an item is dragged from overflow to shelf, IsShowingOverflowBubble()
- // returns true. At this time, we don't need to pin the item.
- if (!IsShowingOverflowBubble() &&
- (!drag_and_drop_shelf_id_ || !delegate_->IsAppPinned(app_id))) {
- delegate_->PinAppWithID(app_id);
- drag_and_drop_shelf_id_ =
- delegate_->GetShelfIDForAppID(drag_and_drop_app_id_);
- if (!drag_and_drop_shelf_id_)
- return false;
- drag_and_drop_item_pinned_ = true;
- }
- views::View* drag_and_drop_view =
- view_model_->view_at(model_->ItemIndexByID(drag_and_drop_shelf_id_));
- DCHECK(drag_and_drop_view);
-
- // Since there is already an icon presented by the caller, we hide this item
- // for now. That has to be done by reducing the size since the visibility will
- // change once a regrouping animation is performed.
- pre_drag_and_drop_size_ = drag_and_drop_view->size();
- drag_and_drop_view->SetSize(gfx::Size());
-
- // First we have to center the mouse cursor over the item.
- gfx::Point pt = drag_and_drop_view->GetBoundsInScreen().CenterPoint();
- views::View::ConvertPointFromScreen(drag_and_drop_view, &pt);
- gfx::Point point_in_root =
- wm::GetRootWindowAt(location_in_screen_coordinates)
- ->ConvertPointFromScreen(location_in_screen_coordinates);
- ui::MouseEvent event(ui::ET_MOUSE_PRESSED, pt, point_in_root,
- ui::EventTimeForNow(), 0, 0);
- PointerPressedOnButton(drag_and_drop_view, DRAG_AND_DROP, event);
-
- // Drag the item where it really belongs.
- Drag(location_in_screen_coordinates);
- return true;
-}
-
-bool ShelfView::Drag(const gfx::Point& location_in_screen_coordinates) {
- if (!drag_and_drop_shelf_id_ ||
- !GetBoundsInScreen().Contains(location_in_screen_coordinates))
- return false;
-
- gfx::Point pt = location_in_screen_coordinates;
- views::View* drag_and_drop_view =
- view_model_->view_at(model_->ItemIndexByID(drag_and_drop_shelf_id_));
- ConvertPointFromScreen(drag_and_drop_view, &pt);
- gfx::Point point_in_root =
- wm::GetRootWindowAt(location_in_screen_coordinates)
- ->ConvertPointFromScreen(location_in_screen_coordinates);
- ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, point_in_root,
- ui::EventTimeForNow(), 0, 0);
- PointerDraggedOnButton(drag_and_drop_view, DRAG_AND_DROP, event);
- return true;
-}
-
-void ShelfView::EndDrag(bool cancel) {
- if (!drag_and_drop_shelf_id_)
- return;
-
- views::View* drag_and_drop_view =
- view_model_->view_at(model_->ItemIndexByID(drag_and_drop_shelf_id_));
- PointerReleasedOnButton(drag_and_drop_view, DRAG_AND_DROP, cancel);
-
- // Either destroy the temporarily created item - or - make the item visible.
- if (drag_and_drop_item_pinned_ && cancel) {
- delegate_->UnpinAppWithID(drag_and_drop_app_id_);
- } else if (drag_and_drop_view) {
- if (cancel) {
- // When a hosted drag gets canceled, the item can remain in the same slot
- // and it might have moved within the bounds. In that case the item need
- // to animate back to its correct location.
- AnimateToIdealBounds();
- } else {
- drag_and_drop_view->SetSize(pre_drag_and_drop_size_);
- }
- }
-
- drag_and_drop_shelf_id_ = 0;
-}
-
-bool ShelfView::ShouldEventActivateButton(View* view, const ui::Event& event) {
- if (dragging())
- return false;
-
- // Ignore if we are already in a pointer event sequence started with a repost
- // event on the same shelf item. See crbug.com/343005 for more detail.
- if (is_repost_event_on_same_item_)
- return false;
-
- // Don't activate the item twice on double-click. Otherwise the window starts
- // animating open due to the first click, then immediately minimizes due to
- // the second click. The user most likely intended to open or minimize the
- // item once, not do both.
- if (event.flags() & ui::EF_IS_DOUBLE_CLICK)
- return false;
-
- // Ignore if this is a repost event on the last pressed shelf item.
- int index = view_model_->GetIndexOfView(view);
- if (index == -1)
- return false;
- return !IsRepostEvent(event) || last_pressed_index_ != index;
-}
-
-void ShelfView::PointerPressedOnButton(views::View* view,
- Pointer pointer,
- const ui::LocatedEvent& event) {
- if (drag_view_)
- return;
-
- int index = view_model_->GetIndexOfView(view);
- if (index == -1 || view_model_->view_size() <= 1)
- return; // View is being deleted, ignore request.
-
- if (view == GetAppListButton())
- return; // View is not draggable, ignore request.
-
- // Only when the repost event occurs on the same shelf item, we should ignore
- // the call in ShelfView::ButtonPressed(...).
- is_repost_event_on_same_item_ =
- IsRepostEvent(event) && (last_pressed_index_ == index);
-
- CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
- drag_view_ = static_cast<ShelfButton*>(view);
- drag_origin_ = gfx::Point(event.x(), event.y());
- UMA_HISTOGRAM_ENUMERATION("Ash.ShelfAlignmentUsage",
- wm_shelf_->SelectValueForShelfAlignment(
- SHELF_ALIGNMENT_UMA_ENUM_VALUE_BOTTOM,
- SHELF_ALIGNMENT_UMA_ENUM_VALUE_LEFT,
- SHELF_ALIGNMENT_UMA_ENUM_VALUE_RIGHT),
- SHELF_ALIGNMENT_UMA_ENUM_VALUE_COUNT);
-}
-
-void ShelfView::PointerDraggedOnButton(views::View* view,
- Pointer pointer,
- const ui::LocatedEvent& event) {
- // To prepare all drag types (moving an item in the shelf and dragging off),
- // we should check the x-axis and y-axis offset.
- if (!dragging() && drag_view_ &&
- ((std::abs(event.x() - drag_origin_.x()) >= kMinimumDragDistance) ||
- (std::abs(event.y() - drag_origin_.y()) >= kMinimumDragDistance))) {
- PrepareForDrag(pointer, event);
- }
- if (drag_pointer_ == pointer)
- ContinueDrag(event);
-}
-
-void ShelfView::PointerReleasedOnButton(views::View* view,
- Pointer pointer,
- bool canceled) {
- is_repost_event_on_same_item_ = false;
-
- if (canceled) {
- CancelDrag(-1);
- } else if (drag_pointer_ == pointer) {
- FinalizeRipOffDrag(false);
- drag_pointer_ = NONE;
- AnimateToIdealBounds();
- }
- // If the drag pointer is NONE, no drag operation is going on and the
- // drag_view can be released.
- if (drag_pointer_ == NONE)
- drag_view_ = nullptr;
-}
-
-void ShelfView::LayoutToIdealBounds() {
- if (bounds_animator_->IsAnimating()) {
- AnimateToIdealBounds();
- return;
- }
-
- IdealBounds ideal_bounds;
- CalculateIdealBounds(&ideal_bounds);
- views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
- overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
-}
-
-void ShelfView::UpdateShelfItemBackground(SkColor color) {
- GetAppListButton()->UpdateShelfItemBackground(color);
- overflow_button_->UpdateShelfItemBackground(color);
-}
-
-void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
- // The overflow button is not shown in overflow mode.
- overflow_button_->SetVisible(false);
- DCHECK_LT(last_visible_index_, view_model_->view_size());
- for (int i = 0; i < view_model_->view_size(); ++i) {
- bool visible = i >= first_visible_index_ && i <= last_visible_index_;
- // To track the dragging of |drag_view_| continuously, its visibility
- // should be always true regardless of its position.
- if (dragged_off_from_overflow_to_shelf_ &&
- view_model_->view_at(i) == drag_view_)
- view_model_->view_at(i)->SetVisible(true);
- else
- view_model_->view_at(i)->SetVisible(visible);
- }
-}
-
-void ShelfView::CalculateIdealBounds(IdealBounds* bounds) const {
- int available_size = wm_shelf_->PrimaryAxisValue(width(), height());
- DCHECK(model_->item_count() == view_model_->view_size());
- if (!available_size)
- return;
-
- int first_panel_index = model_->FirstPanelIndex();
- int last_button_index = first_panel_index - 1;
-
- int x = 0;
- int y = 0;
-
- int w = wm_shelf_->PrimaryAxisValue(kShelfButtonSize, width());
- int h = wm_shelf_->PrimaryAxisValue(height(), kShelfButtonSize);
- for (int i = 0; i < view_model_->view_size(); ++i) {
- if (i < first_visible_index_) {
- view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
- continue;
- }
-
- view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
- x = wm_shelf_->PrimaryAxisValue(x + w + kShelfButtonSpacing, x);
- y = wm_shelf_->PrimaryAxisValue(y, y + h + kShelfButtonSpacing);
- }
-
- if (is_overflow_mode()) {
- const_cast<ShelfView*>(this)->UpdateAllButtonsVisibilityInOverflowMode();
- return;
- }
-
- // Right aligned icons.
- int end_position = available_size;
- x = wm_shelf_->PrimaryAxisValue(end_position, 0);
- y = wm_shelf_->PrimaryAxisValue(0, end_position);
- for (int i = view_model_->view_size() - 1; i >= first_panel_index; --i) {
- x = wm_shelf_->PrimaryAxisValue(x - w - kShelfButtonSpacing, x);
- y = wm_shelf_->PrimaryAxisValue(y, y - h - kShelfButtonSpacing);
- view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
- end_position = wm_shelf_->PrimaryAxisValue(x, y);
- }
-
- // Icons on the left / top are guaranteed up to kLeftIconProportion of
- // the available space.
- int last_icon_position =
- wm_shelf_->PrimaryAxisValue(
- view_model_->ideal_bounds(last_button_index).right(),
- view_model_->ideal_bounds(last_button_index).bottom()) +
- kShelfButtonSpacing;
- int reserved_icon_space = available_size * kReservedNonPanelIconProportion;
- if (last_icon_position < reserved_icon_space)
- end_position = last_icon_position;
- else
- end_position = std::max(end_position, reserved_icon_space);
-
- bounds->overflow_bounds.set_size(
- gfx::Size(wm_shelf_->PrimaryAxisValue(w, width()),
- wm_shelf_->PrimaryAxisValue(height(), h)));
-
- last_visible_index_ =
- DetermineLastVisibleIndex(end_position - kShelfButtonSpacing);
- last_hidden_index_ = DetermineFirstVisiblePanelIndex(end_position) - 1;
- bool show_overflow = last_visible_index_ < last_button_index ||
- last_hidden_index_ >= first_panel_index;
-
- // Create Space for the overflow button
- if (show_overflow) {
- // The following code makes sure that platform apps icons (aligned to left /
- // top) are favored over panel apps icons (aligned to right / bottom).
- if (last_visible_index_ > 0 && last_visible_index_ < last_button_index) {
- // This condition means that we will take one platform app and replace it
- // with the overflow button and put the app in the overflow bubble.
- // This happens when the space needed for platform apps exceeds the
- // reserved area for non-panel icons,
- // (i.e. |last_icon_position| > |reserved_icon_space|).
- --last_visible_index_;
- } else if (last_hidden_index_ >= first_panel_index &&
- last_hidden_index_ < view_model_->view_size() - 1) {
- // This condition means that we will take a panel app icon and replace it
- // with the overflow button.
- // This happens when there is still room for platform apps in the reserved
- // area for non-panel icons,
- // (i.e. |last_icon_position| < |reserved_icon_space|).
- ++last_hidden_index_;
- }
- }
-
- for (int i = 0; i < view_model_->view_size(); ++i) {
- bool visible = i <= last_visible_index_ || i > last_hidden_index_;
- // To receive drag event continuously from |drag_view_| during the dragging
- // off from the shelf, don't make |drag_view_| invisible. It will be
- // eventually invisible and removed from the |view_model_| by
- // FinalizeRipOffDrag().
- if (dragged_off_shelf_ && view_model_->view_at(i) == drag_view_)
- continue;
- view_model_->view_at(i)->SetVisible(visible);
- }
-
- overflow_button_->SetVisible(show_overflow);
- if (show_overflow) {
- DCHECK_NE(0, view_model_->view_size());
- if (last_visible_index_ == -1) {
- x = 0;
- y = 0;
- } else {
- x = wm_shelf_->PrimaryAxisValue(
- view_model_->ideal_bounds(last_visible_index_).right(),
- view_model_->ideal_bounds(last_visible_index_).x());
- y = wm_shelf_->PrimaryAxisValue(
- view_model_->ideal_bounds(last_visible_index_).y(),
- view_model_->ideal_bounds(last_visible_index_).bottom());
- }
-
- if (last_visible_index_ >= 0) {
- // Add more space between last visible item and overflow button.
- // Without this, two buttons look too close compared with other items.
- x = wm_shelf_->PrimaryAxisValue(x + kShelfButtonSpacing, x);
- y = wm_shelf_->PrimaryAxisValue(y, y + kShelfButtonSpacing);
- }
-
- // Set all hidden panel icon positions to be on the overflow button.
- for (int i = first_panel_index; i <= last_hidden_index_; ++i)
- view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
-
- bounds->overflow_bounds.set_x(x);
- bounds->overflow_bounds.set_y(y);
- if (overflow_bubble_.get() && overflow_bubble_->IsShowing())
- UpdateOverflowRange(overflow_bubble_->shelf_view());
- } else {
- if (overflow_bubble_)
- overflow_bubble_->Hide();
- }
-}
-
-int ShelfView::DetermineLastVisibleIndex(int max_value) const {
- int index = model_->FirstPanelIndex() - 1;
- while (index >= 0 &&
- wm_shelf_->PrimaryAxisValue(
- view_model_->ideal_bounds(index).right(),
- view_model_->ideal_bounds(index).bottom()) > max_value) {
- index--;
- }
- return index;
-}
-
-int ShelfView::DetermineFirstVisiblePanelIndex(int min_value) const {
- int index = model_->FirstPanelIndex();
- while (index < view_model_->view_size() &&
- wm_shelf_->PrimaryAxisValue(view_model_->ideal_bounds(index).x(),
- view_model_->ideal_bounds(index).y()) <
- min_value) {
- ++index;
- }
- return index;
-}
-
-void ShelfView::AnimateToIdealBounds() {
- IdealBounds ideal_bounds;
- CalculateIdealBounds(&ideal_bounds);
- for (int i = 0; i < view_model_->view_size(); ++i) {
- View* view = view_model_->view_at(i);
- bounds_animator_->AnimateViewTo(view, view_model_->ideal_bounds(i));
- // Now that the item animation starts, we have to make sure that the
- // padding of the first gets properly transferred to the new first item.
- if (i && view->border())
- view->SetBorder(views::NullBorder());
- }
- overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
-}
-
-views::View* ShelfView::CreateViewForItem(const ShelfItem& item) {
- views::View* view = nullptr;
- switch (item.type) {
- case TYPE_APP_PANEL:
- case TYPE_APP_SHORTCUT:
- case TYPE_BROWSER_SHORTCUT:
- case TYPE_APP:
- case TYPE_DIALOG: {
- ShelfButton* button = new ShelfButton(this, this);
- button->SetImage(item.image);
- ReflectItemStatus(item, button);
- view = button;
- break;
- }
-
- case TYPE_APP_LIST: {
- view = new AppListButton(this, this, wm_shelf_);
- break;
- }
-
- case TYPE_UNDEFINED:
- return nullptr;
- }
-
- view->set_context_menu_controller(this);
- ConfigureChildView(view);
- return view;
-}
-
-void ShelfView::FadeIn(views::View* view) {
- view->SetVisible(true);
- view->layer()->SetOpacity(0);
- AnimateToIdealBounds();
- bounds_animator_->SetAnimationDelegate(
- view, std::unique_ptr<gfx::AnimationDelegate>(
- new FadeInAnimationDelegate(view)));
-}
-
-void ShelfView::PrepareForDrag(Pointer pointer, const ui::LocatedEvent& event) {
- DCHECK(!dragging());
- DCHECK(drag_view_);
- drag_pointer_ = pointer;
- start_drag_index_ = view_model_->GetIndexOfView(drag_view_);
-
- if (start_drag_index_ == -1) {
- CancelDrag(-1);
- return;
- }
-
- // Move the view to the front so that it appears on top of other views.
- ReorderChildView(drag_view_, -1);
- bounds_animator_->StopAnimatingView(drag_view_);
-
- drag_view_->OnDragStarted(&event);
-}
-
-void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
- DCHECK(dragging());
- DCHECK(drag_view_);
- // Due to a syncing operation the application might have been removed.
- // Bail if it is gone.
- int current_index = view_model_->GetIndexOfView(drag_view_);
- DCHECK_NE(-1, current_index);
-
- // If this is not a drag and drop host operation and not the app list item,
- // check if the item got ripped off the shelf - if it did we are done.
- if (!drag_and_drop_shelf_id_ &&
- RemovableByRipOff(current_index) != NOT_REMOVABLE) {
- if (HandleRipOffDrag(event))
- return;
- // The rip off handler could have changed the location of the item.
- current_index = view_model_->GetIndexOfView(drag_view_);
- }
-
- // TODO: I don't think this works correctly with RTL.
- gfx::Point drag_point(event.location());
- ConvertPointToTarget(drag_view_, this, &drag_point);
-
- // Constrain the location to the range of valid indices for the type.
- std::pair<int, int> indices(GetDragRange(current_index));
- int first_drag_index = indices.first;
- int last_drag_index = indices.second;
- // If the last index isn't valid, we're overflowing. Constrain to the app list
- // (which is the last visible item).
- if (first_drag_index < model_->FirstPanelIndex() &&
- last_drag_index > last_visible_index_)
- last_drag_index = last_visible_index_;
- int x = 0, y = 0;
- if (wm_shelf_->IsHorizontalAlignment()) {
- x = std::max(view_model_->ideal_bounds(indices.first).x(),
- drag_point.x() - drag_origin_.x());
- x = std::min(view_model_->ideal_bounds(last_drag_index).right() -
- view_model_->ideal_bounds(current_index).width(),
- x);
- if (drag_view_->x() == x)
- return;
- drag_view_->SetX(x);
- } else {
- y = std::max(view_model_->ideal_bounds(indices.first).y(),
- drag_point.y() - drag_origin_.y());
- y = std::min(view_model_->ideal_bounds(last_drag_index).bottom() -
- view_model_->ideal_bounds(current_index).height(),
- y);
- if (drag_view_->y() == y)
- return;
- drag_view_->SetY(y);
- }
-
- int target_index = views::ViewModelUtils::DetermineMoveIndex(
- *view_model_, drag_view_,
- wm_shelf_->IsHorizontalAlignment() ? views::ViewModelUtils::HORIZONTAL
- : views::ViewModelUtils::VERTICAL,
- x, y);
- target_index =
- std::min(indices.second, std::max(target_index, indices.first));
-
- // The app list button is always first, and it is the only non-draggable item.
- int first_draggable_item = model_->GetItemIndexForType(TYPE_APP_LIST) + 1;
- DCHECK_EQ(1, first_draggable_item);
- target_index = std::max(target_index, first_draggable_item);
- DCHECK_LT(target_index, model_->item_count());
-
- if (target_index == current_index)
- return;
-
- // Change the model, the ShelfItemMoved() callback will handle the
- // |view_model_| update.
- model_->Move(current_index, target_index);
- bounds_animator_->StopAnimatingView(drag_view_);
-}
-
-bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
- int current_index = view_model_->GetIndexOfView(drag_view_);
- DCHECK_NE(-1, current_index);
- std::string dragged_app_id =
- delegate_->GetAppIDForShelfID(model_->items()[current_index].id);
-
- gfx::Point screen_location =
- WmWindow::Get(GetWidget()->GetNativeWindow())
- ->GetRootWindow()
- ->ConvertPointToScreen(event.root_location());
-
- // To avoid ugly forwards and backwards flipping we use different constants
- // for ripping off / re-inserting the items.
- if (dragged_off_shelf_) {
- // If the shelf/overflow bubble bounds contains |screen_location| we insert
- // the item back into the shelf.
- if (GetBoundsForDragInsertInScreen().Contains(screen_location)) {
- if (dragged_off_from_overflow_to_shelf_) {
- // During the dragging an item from Shelf to Overflow, it can enter here
- // directly because both are located very closly.
- main_shelf_->EndDrag(true);
- // Stops the animation of |drag_view_| and sets its bounds explicitly
- // becase ContinueDrag() stops its animation. Without this, unexpected
- // bounds will be set.
- bounds_animator_->StopAnimatingView(drag_view_);
- int drag_view_index = view_model_->GetIndexOfView(drag_view_);
- drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
- dragged_off_from_overflow_to_shelf_ = false;
- }
- // Destroy our proxy view item.
- DestroyDragIconProxy();
- // Re-insert the item and return simply false since the caller will handle
- // the move as in any normal case.
- dragged_off_shelf_ = false;
- drag_view_->layer()->SetOpacity(1.0f);
- // The size of Overflow bubble should be updated immediately when an item
- // is re-inserted.
- if (is_overflow_mode())
- PreferredSizeChanged();
- return false;
- } else if (is_overflow_mode() &&
- main_shelf_->GetBoundsForDragInsertInScreen().Contains(
- screen_location)) {
- if (!dragged_off_from_overflow_to_shelf_) {
- dragged_off_from_overflow_to_shelf_ = true;
- drag_image_->SetOpacity(1.0f);
- main_shelf_->StartDrag(dragged_app_id, screen_location);
- } else {
- main_shelf_->Drag(screen_location);
- }
- } else if (dragged_off_from_overflow_to_shelf_) {
- // Makes the |drag_image_| partially disappear again.
- dragged_off_from_overflow_to_shelf_ = false;
- drag_image_->SetOpacity(kDraggedImageOpacity);
- main_shelf_->EndDrag(true);
- bounds_animator_->StopAnimatingView(drag_view_);
- int drag_view_index = view_model_->GetIndexOfView(drag_view_);
- drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
- }
- // Move our proxy view item.
- UpdateDragIconProxy(screen_location);
- return true;
- }
- // Check if we are too far away from the shelf to enter the ripped off state.
- // Determine the distance to the shelf.
- int delta = CalculateShelfDistance(screen_location);
- if (delta > kRipOffDistance) {
- // Create a proxy view item which can be moved anywhere.
- CreateDragIconProxy(event.root_location(), drag_view_->GetImage(),
- drag_view_, gfx::Vector2d(0, 0),
- kDragAndDropProxyScale);
- drag_view_->layer()->SetOpacity(0.0f);
- dragged_off_shelf_ = true;
- if (RemovableByRipOff(current_index) == REMOVABLE) {
- // Move the item to the front of the first panel item and hide it.
- // ShelfItemMoved() callback will handle the |view_model_| update and
- // call AnimateToIdealBounds().
- if (current_index != model_->FirstPanelIndex() - 1) {
- model_->Move(current_index, model_->FirstPanelIndex() - 1);
- StartFadeInLastVisibleItem();
- } else if (is_overflow_mode()) {
- // Overflow bubble should be shrunk when an item is ripped off.
- PreferredSizeChanged();
- }
- // Make the item partially disappear to show that it will get removed if
- // dropped.
- drag_image_->SetOpacity(kDraggedImageOpacity);
- }
- return true;
- }
- return false;
-}
-
-void ShelfView::FinalizeRipOffDrag(bool cancel) {
- if (!dragged_off_shelf_)
- return;
- // Make sure we do not come in here again.
- dragged_off_shelf_ = false;
-
- // Coming here we should always have a |drag_view_|.
- DCHECK(drag_view_);
- int current_index = view_model_->GetIndexOfView(drag_view_);
- // If the view isn't part of the model anymore (|current_index| == -1), a sync
- // operation must have removed it. In that case we shouldn't change the model
- // and only delete the proxy image.
- if (current_index == -1) {
- DestroyDragIconProxy();
- return;
- }
-
- // Set to true when the animation should snap back to where it was before.
- bool snap_back = false;
- // Items which cannot be dragged off will be handled as a cancel.
- if (!cancel) {
- if (dragged_off_from_overflow_to_shelf_) {
- dragged_off_from_overflow_to_shelf_ = false;
- main_shelf_->EndDrag(false);
- drag_view_->layer()->SetOpacity(1.0f);
- } else if (RemovableByRipOff(current_index) != REMOVABLE) {
- // Make sure we do not try to remove un-removable items like items which
- // were not pinned or have to be always there.
- cancel = true;
- snap_back = true;
- } else {
- // Make sure the item stays invisible upon removal.
- drag_view_->SetVisible(false);
- std::string app_id =
- delegate_->GetAppIDForShelfID(model_->items()[current_index].id);
- delegate_->UnpinAppWithID(app_id);
- }
- }
- if (cancel || snap_back) {
- if (dragged_off_from_overflow_to_shelf_) {
- dragged_off_from_overflow_to_shelf_ = false;
- // Main shelf handles revert of dragged item.
- main_shelf_->EndDrag(true);
- drag_view_->layer()->SetOpacity(1.0f);
- } else if (!cancelling_drag_model_changed_) {
- // Only do something if the change did not come through a model change.
- gfx::Rect drag_bounds = drag_image_->GetBoundsInScreen();
- gfx::Point relative_to = GetBoundsInScreen().origin();
- gfx::Rect target(
- gfx::PointAtOffsetFromOrigin(drag_bounds.origin() - relative_to),
- drag_bounds.size());
- drag_view_->SetBoundsRect(target);
- // Hide the status from the active item since we snap it back now. Upon
- // animation end the flag gets cleared if |snap_back_from_rip_off_view_|
- // is set.
- snap_back_from_rip_off_view_ = drag_view_;
- drag_view_->AddState(ShelfButton::STATE_HIDDEN);
- // When a canceling drag model is happening, the view model is diverged
- // from the menu model and movements / animations should not be done.
- model_->Move(current_index, start_drag_index_);
- AnimateToIdealBounds();
- }
- drag_view_->layer()->SetOpacity(1.0f);
- }
- DestroyDragIconProxy();
-}
-
-ShelfView::RemovableState ShelfView::RemovableByRipOff(int index) const {
- DCHECK(index >= 0 && index < model_->item_count());
- ShelfItemType type = model_->items()[index].type;
- if (type == TYPE_APP_LIST || type == TYPE_DIALOG)
- return NOT_REMOVABLE;
-
- if (model_->items()[index].pinned_by_policy)
- return NOT_REMOVABLE;
-
- // Note: Only pinned app shortcuts can be removed!
- std::string app_id = delegate_->GetAppIDForShelfID(model_->items()[index].id);
- return (type == TYPE_APP_SHORTCUT && delegate_->IsAppPinned(app_id))
- ? REMOVABLE
- : DRAGGABLE;
-}
-
-bool ShelfView::SameDragType(ShelfItemType typea, ShelfItemType typeb) const {
- switch (typea) {
- case TYPE_APP_SHORTCUT:
- case TYPE_BROWSER_SHORTCUT:
- return (typeb == TYPE_APP_SHORTCUT || typeb == TYPE_BROWSER_SHORTCUT);
- case TYPE_APP_PANEL:
- case TYPE_APP_LIST:
- case TYPE_APP:
- case TYPE_DIALOG:
- return typeb == typea;
- case TYPE_UNDEFINED:
- NOTREACHED() << "ShelfItemType must be set.";
- return false;
- }
- NOTREACHED();
- return false;
-}
-
-std::pair<int, int> ShelfView::GetDragRange(int index) {
- int min_index = -1;
- int max_index = -1;
- ShelfItemType type = model_->items()[index].type;
- for (int i = 0; i < model_->item_count(); ++i) {
- if (SameDragType(model_->items()[i].type, type)) {
- if (min_index == -1)
- min_index = i;
- max_index = i;
- }
- }
- return std::pair<int, int>(min_index, max_index);
-}
-
-void ShelfView::ConfigureChildView(views::View* view) {
- view->SetPaintToLayer();
- view->layer()->SetFillsBoundsOpaquely(false);
-}
-
-void ShelfView::ToggleOverflowBubble() {
- if (IsShowingOverflowBubble()) {
- overflow_bubble_->Hide();
- return;
- }
-
- if (!overflow_bubble_)
- overflow_bubble_.reset(new OverflowBubble(wm_shelf_));
-
- ShelfView* overflow_view =
- new ShelfView(model_, delegate_, wm_shelf_, shelf_widget_);
- overflow_view->overflow_mode_ = true;
- overflow_view->Init();
- overflow_view->set_owner_overflow_bubble(overflow_bubble_.get());
- overflow_view->OnShelfAlignmentChanged();
- overflow_view->main_shelf_ = this;
- UpdateOverflowRange(overflow_view);
-
- overflow_bubble_->Show(overflow_button_, overflow_view);
-
- wm_shelf_->UpdateVisibilityState();
-}
-
-void ShelfView::OnFadeOutAnimationEnded() {
- AnimateToIdealBounds();
- StartFadeInLastVisibleItem();
-}
-
-void ShelfView::StartFadeInLastVisibleItem() {
- // If overflow button is visible and there is a valid new last item, fading
- // the new last item in after sliding animation is finished.
- if (overflow_button_->visible() && last_visible_index_ >= 0) {
- views::View* last_visible_view = view_model_->view_at(last_visible_index_);
- last_visible_view->layer()->SetOpacity(0);
- bounds_animator_->SetAnimationDelegate(
- last_visible_view,
- std::unique_ptr<gfx::AnimationDelegate>(
- new StartFadeAnimationDelegate(this, last_visible_view)));
- }
-}
-
-void ShelfView::UpdateOverflowRange(ShelfView* overflow_view) const {
- const int first_overflow_index = last_visible_index_ + 1;
- const int last_overflow_index = last_hidden_index_;
- DCHECK_LE(first_overflow_index, last_overflow_index);
- DCHECK_LT(last_overflow_index, view_model_->view_size());
-
- overflow_view->first_visible_index_ = first_overflow_index;
- overflow_view->last_visible_index_ = last_overflow_index;
-}
-
-gfx::Rect ShelfView::GetBoundsForDragInsertInScreen() {
- gfx::Size preferred_size;
- if (is_overflow_mode()) {
- DCHECK(owner_overflow_bubble_);
- gfx::Rect bubble_bounds =
- owner_overflow_bubble_->bubble_view()->GetBubbleBounds();
- preferred_size = bubble_bounds.size();
- } else {
- const int last_button_index = view_model_->view_size() - 1;
- gfx::Rect last_button_bounds =
- view_model_->view_at(last_button_index)->bounds();
- if (overflow_button_->visible() &&
- model_->GetItemIndexForType(TYPE_APP_PANEL) == -1) {
- // When overflow button is visible and shelf has no panel items,
- // last_button_bounds should be overflow button's bounds.
- last_button_bounds = overflow_button_->bounds();
- }
-
- if (wm_shelf_->IsHorizontalAlignment()) {
- preferred_size = gfx::Size(last_button_bounds.right() + leading_inset_,
- GetShelfConstant(SHELF_SIZE));
- } else {
- preferred_size = gfx::Size(GetShelfConstant(SHELF_SIZE),
- last_button_bounds.bottom() + leading_inset_);
- }
- }
- gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
-
- // In overflow mode, we should use OverflowBubbleView as a source for
- // converting |origin| to screen coordinates. When a scroll operation is
- // occurred in OverflowBubble, the bounds of ShelfView in OverflowBubble can
- // be changed.
- if (is_overflow_mode())
- ConvertPointToScreen(owner_overflow_bubble_->bubble_view(), &origin);
- else
- ConvertPointToScreen(this, &origin);
-
- return gfx::Rect(origin, preferred_size);
-}
-
-int ShelfView::CancelDrag(int modified_index) {
- FinalizeRipOffDrag(true);
- if (!drag_view_)
- return modified_index;
- bool was_dragging = dragging();
- int drag_view_index = view_model_->GetIndexOfView(drag_view_);
- drag_pointer_ = NONE;
- drag_view_ = nullptr;
- if (drag_view_index == modified_index) {
- // The view that was being dragged is being modified. Don't do anything.
- return modified_index;
- }
- if (!was_dragging)
- return modified_index;
-
- // Restore previous position, tracking the position of the modified view.
- bool at_end = modified_index == view_model_->view_size();
- views::View* modified_view = (modified_index >= 0 && !at_end)
- ? view_model_->view_at(modified_index)
- : nullptr;
- model_->Move(drag_view_index, start_drag_index_);
-
- // If the modified view will be at the end of the list, return the new end of
- // the list.
- if (at_end)
- return view_model_->view_size();
- return modified_view ? view_model_->GetIndexOfView(modified_view) : -1;
-}
-
-gfx::Size ShelfView::GetPreferredSize() const {
- IdealBounds ideal_bounds;
- CalculateIdealBounds(&ideal_bounds);
- const int shelf_size = GetShelfConstant(SHELF_SIZE);
-
- int last_button_index = last_visible_index_;
- if (!is_overflow_mode()) {
- if (last_hidden_index_ < view_model_->view_size() - 1)
- last_button_index = view_model_->view_size() - 1;
- else if (overflow_button_ && overflow_button_->visible())
- last_button_index++;
- }
-
- // When an item is dragged off from the overflow bubble, it is moved to last
- // position and and changed to invisible. Overflow bubble size should be
- // shrunk to fit only for visible items.
- // If |dragged_off_from_overflow_to_shelf_| is set, there will be no invisible
- // items in the shelf.
- if (is_overflow_mode() && dragged_off_shelf_ &&
- !dragged_off_from_overflow_to_shelf_ &&
- RemovableByRipOff(view_model_->GetIndexOfView(drag_view_)) == REMOVABLE)
- last_button_index--;
-
- const gfx::Rect last_button_bounds =
- last_button_index >= first_visible_index_
- ? view_model_->ideal_bounds(last_button_index)
- : gfx::Rect(gfx::Size(shelf_size, shelf_size));
-
- if (wm_shelf_->IsHorizontalAlignment())
- return gfx::Size(last_button_bounds.right() + leading_inset_, shelf_size);
-
- return gfx::Size(shelf_size, last_button_bounds.bottom() + leading_inset_);
-}
-
-void ShelfView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
- // This bounds change is produced by the shelf movement and all content has
- // to follow. Using an animation at that time would produce a time lag since
- // the animation of the BoundsAnimator has itself a delay before it arrives
- // at the required location. As such we tell the animator to go there
- // immediately.
- BoundsAnimatorDisabler disabler(bounds_animator_.get());
- LayoutToIdealBounds();
- wm_shelf_->NotifyShelfIconPositionsChanged();
-
- if (IsShowingOverflowBubble())
- overflow_bubble_->Hide();
-}
-
-views::FocusTraversable* ShelfView::GetPaneFocusTraversable() {
- return this;
-}
-
-void ShelfView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
- node_data->role = ui::AX_ROLE_TOOLBAR;
- node_data->SetName(l10n_util::GetStringUTF8(IDS_ASH_SHELF_ACCESSIBLE_NAME));
-}
-
-void ShelfView::ViewHierarchyChanged(
- const ViewHierarchyChangedDetails& details) {
- if (details.is_add && details.child == this)
- tooltip_.Init();
-}
-
-void ShelfView::OnGestureEvent(ui::GestureEvent* event) {
- if (wm_shelf_->ProcessGestureEvent(*event))
- event->StopPropagation();
-}
-
-void ShelfView::ShelfItemAdded(int model_index) {
- {
- base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_,
- true);
- model_index = CancelDrag(model_index);
- }
- views::View* view = CreateViewForItem(model_->items()[model_index]);
- AddChildView(view);
- // Hide the view, it'll be made visible when the animation is done. Using
- // opacity 0 here to avoid messing with CalculateIdealBounds which touches
- // the view's visibility.
- view->layer()->SetOpacity(0);
- view_model_->Add(view, model_index);
-
- // Give the button its ideal bounds. That way if we end up animating the
- // button before this animation completes it doesn't appear at some random
- // spot (because it was in the middle of animating from 0,0 0x0 to its
- // target).
- IdealBounds ideal_bounds;
- CalculateIdealBounds(&ideal_bounds);
- view->SetBoundsRect(view_model_->ideal_bounds(model_index));
-
- // The first animation moves all the views to their target position. |view|
- // is hidden, so it visually appears as though we are providing space for
- // it. When done we'll fade the view in.
- AnimateToIdealBounds();
- if (model_index <= last_visible_index_ ||
- model_index >= model_->FirstPanelIndex()) {
- bounds_animator_->SetAnimationDelegate(
- view, std::unique_ptr<gfx::AnimationDelegate>(
- new StartFadeAnimationDelegate(this, view)));
- } else {
- // Undo the hiding if animation does not run.
- view->layer()->SetOpacity(1.0f);
- }
-}
-
-void ShelfView::ShelfItemRemoved(int model_index, ShelfID id) {
- if (id == context_menu_id_)
- launcher_menu_runner_->Cancel();
- {
- base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_,
- true);
- model_index = CancelDrag(model_index);
- }
- views::View* view = view_model_->view_at(model_index);
- view_model_->Remove(model_index);
-
- // When the overflow bubble is visible, the overflow range needs to be set
- // before CalculateIdealBounds() gets called. Otherwise CalculateIdealBounds()
- // could trigger a ShelfItemChanged() by hiding the overflow bubble and
- // since the overflow bubble is not yet synced with the ShelfModel this
- // could cause a crash.
- if (overflow_bubble_ && overflow_bubble_->IsShowing()) {
- last_hidden_index_ =
- std::min(last_hidden_index_, view_model_->view_size() - 1);
- UpdateOverflowRange(overflow_bubble_->shelf_view());
- }
-
- if (view->visible()) {
- // The first animation fades out the view. When done we'll animate the rest
- // of the views to their target location.
- bounds_animator_->AnimateViewTo(view, view->bounds());
- bounds_animator_->SetAnimationDelegate(
- view, std::unique_ptr<gfx::AnimationDelegate>(
- new FadeOutAnimationDelegate(this, view)));
- } else {
- // We don't need to show a fade out animation for invisible |view|. When an
- // item is ripped out from the shelf, its |view| is already invisible.
- AnimateToIdealBounds();
- }
-
- if (view == tooltip_.GetCurrentAnchorView())
- tooltip_.Close();
-}
-
-void ShelfView::ShelfItemChanged(int model_index, const ShelfItem& old_item) {
- const ShelfItem& item(model_->items()[model_index]);
- if (old_item.type != item.type) {
- // Type changed, swap the views.
- model_index = CancelDrag(model_index);
- std::unique_ptr<views::View> old_view(view_model_->view_at(model_index));
- bounds_animator_->StopAnimatingView(old_view.get());
- // Removing and re-inserting a view in our view model will strip the ideal
- // bounds from the item. To avoid recalculation of everything the bounds
- // get remembered and restored after the insertion to the previous value.
- gfx::Rect old_ideal_bounds = view_model_->ideal_bounds(model_index);
- view_model_->Remove(model_index);
- views::View* new_view = CreateViewForItem(item);
- AddChildView(new_view);
- view_model_->Add(new_view, model_index);
- view_model_->set_ideal_bounds(model_index, old_ideal_bounds);
- new_view->SetBoundsRect(old_view->bounds());
- if (overflow_button_ && overflow_button_->visible())
- AnimateToIdealBounds();
- else
- bounds_animator_->AnimateViewTo(new_view, old_ideal_bounds);
- return;
- }
-
- views::View* view = view_model_->view_at(model_index);
- switch (item.type) {
- case TYPE_APP_PANEL:
- case TYPE_APP_SHORTCUT:
- case TYPE_BROWSER_SHORTCUT:
- case TYPE_APP:
- case TYPE_DIALOG: {
- CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
- ShelfButton* button = static_cast<ShelfButton*>(view);
- ReflectItemStatus(item, button);
- button->SetImage(item.image);
- button->SchedulePaint();
- break;
- }
-
- default:
- break;
- }
-}
-
-void ShelfView::ShelfItemMoved(int start_index, int target_index) {
- view_model_->Move(start_index, target_index);
- // When cancelling a drag due to a shelf item being added, the currently
- // dragged item is moved back to its initial position. AnimateToIdealBounds
- // will be called again when the new item is added to the |view_model_| but
- // at this time the |view_model_| is inconsistent with the |model_|.
- if (!cancelling_drag_model_changed_)
- AnimateToIdealBounds();
-}
-
-void ShelfView::OnSetShelfItemDelegate(ShelfID id,
- ShelfItemDelegate* item_delegate) {}
-
-bool ShelfView::ShowListMenuForView(const ShelfItem& item,
- views::View* source,
- const ui::Event& event,
- views::InkDrop* ink_drop) {
- ShelfItemDelegate* item_delegate = model_->GetShelfItemDelegate(item.id);
- ShelfAppMenuItemList items = item_delegate->GetAppMenuItems(event.flags());
-
- // The application list menu should only show for two or more items; return
- // false here to ensure that other behavior is triggered (eg. activating or
- // minimizing a single associated window, or launching a pinned shelf item).
- if (items.size() < 2)
- return false;
-
- ink_drop->AnimateToState(views::InkDropState::ACTIVATED);
- context_menu_id_ = item.id;
- ShowMenu(base::MakeUnique<ShelfApplicationMenuModel>(
- item.title, std::move(items), item_delegate),
- source, gfx::Point(), false, ui::GetMenuSourceTypeForEvent(event),
- ink_drop);
- return true;
-}
-
-void ShelfView::ShowContextMenuForView(views::View* source,
- const gfx::Point& point,
- ui::MenuSourceType source_type) {
- last_pressed_index_ = -1;
-
- const ShelfItem* item = ShelfItemForView(source);
- if (!item) {
- WmShell::Get()->ShowContextMenu(point, source_type);
- return;
- }
-
- std::unique_ptr<ui::MenuModel> context_menu_model(
- WmShell::Get()->delegate()->CreateContextMenu(wm_shelf_, item));
- if (!context_menu_model)
- return;
-
- context_menu_id_ = item ? item->id : 0;
- ShowMenu(std::move(context_menu_model), source, point, true, source_type,
- nullptr);
-}
-
-void ShelfView::ShowMenu(std::unique_ptr<ui::MenuModel> menu_model,
- views::View* source,
- const gfx::Point& click_point,
- bool context_menu,
- ui::MenuSourceType source_type,
- views::InkDrop* ink_drop) {
- menu_model_ = std::move(menu_model);
- menu_model_adapter_.reset(new views::MenuModelAdapter(
- menu_model_.get(),
- base::Bind(&ShelfView::OnMenuClosed, base::Unretained(this), ink_drop)));
-
- closing_event_time_ = base::TimeTicks();
- int run_types = views::MenuRunner::ASYNC;
- if (context_menu)
- run_types |= views::MenuRunner::CONTEXT_MENU;
- launcher_menu_runner_.reset(
- new views::MenuRunner(menu_model_adapter_->CreateMenu(), run_types));
-
- // Place new windows on the same display as the button that spawned the menu.
- WmWindow* window = WmWindow::Get(source->GetWidget()->GetNativeWindow());
- scoped_root_window_for_new_windows_.reset(
- new ScopedRootWindowForNewWindows(window->GetRootWindow()));
-
- views::MenuAnchorPosition menu_alignment = views::MENU_ANCHOR_TOPLEFT;
- gfx::Rect anchor = gfx::Rect(click_point, gfx::Size());
-
- if (!context_menu) {
- // Application lists use a bubble.
- // It is possible to invoke the menu while it is sliding into view. To cover
- // that case, the screen coordinates are offsetted by the animation delta.
- anchor = source->GetBoundsInScreen() + (window->GetTargetBounds().origin() -
- window->GetBounds().origin());
-
- // Adjust the anchor location for shelf items with asymmetrical borders.
- if (source->border())
- anchor.Inset(source->border()->GetInsets());
-
- // Determine the menu alignment dependent on the shelf.
- switch (wm_shelf_->GetAlignment()) {
- case SHELF_ALIGNMENT_BOTTOM:
- case SHELF_ALIGNMENT_BOTTOM_LOCKED:
- menu_alignment = views::MENU_ANCHOR_BUBBLE_ABOVE;
- break;
- case SHELF_ALIGNMENT_LEFT:
- menu_alignment = views::MENU_ANCHOR_BUBBLE_RIGHT;
- break;
- case SHELF_ALIGNMENT_RIGHT:
- menu_alignment = views::MENU_ANCHOR_BUBBLE_LEFT;
- break;
- }
- }
-
- // NOTE: if you convert to HAS_MNEMONICS be sure to update menu building code.
- launcher_menu_runner_->RunMenuAt(source->GetWidget(), nullptr, anchor,
- menu_alignment, source_type);
-}
-
-void ShelfView::OnMenuClosed(views::InkDrop* ink_drop) {
- context_menu_id_ = 0;
-
- // Hide the hide overflow bubble after showing a context menu for its items.
- if (owner_overflow_bubble_)
- owner_overflow_bubble_->Hide();
-
- closing_event_time_ = launcher_menu_runner_->closing_event_time();
-
- if (ink_drop)
- ink_drop->AnimateToState(views::InkDropState::DEACTIVATED);
-
- launcher_menu_runner_.reset();
- menu_model_adapter_.reset();
- menu_model_.reset();
- scoped_root_window_for_new_windows_.reset();
-
- // Auto-hide or alignment might have changed, but only for this shelf.
- wm_shelf_->UpdateVisibilityState();
-}
-
-void ShelfView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) {
- wm_shelf_->NotifyShelfIconPositionsChanged();
- PreferredSizeChanged();
-}
-
-void ShelfView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
- if (snap_back_from_rip_off_view_ && animator == bounds_animator_.get()) {
- if (!animator->IsAnimating(snap_back_from_rip_off_view_)) {
- // Coming here the animation of the ShelfButton is finished and the
- // previously hidden status can be shown again. Since the button itself
- // might have gone away or changed locations we check that the button
- // is still in the shelf and show its status again.
- for (int index = 0; index < view_model_->view_size(); index++) {
- views::View* view = view_model_->view_at(index);
- if (view == snap_back_from_rip_off_view_) {
- CHECK_EQ(ShelfButton::kViewClassName, view->GetClassName());
- ShelfButton* button = static_cast<ShelfButton*>(view);
- button->ClearState(ShelfButton::STATE_HIDDEN);
- break;
- }
- }
- snap_back_from_rip_off_view_ = nullptr;
- }
- }
-}
-
-bool ShelfView::IsRepostEvent(const ui::Event& event) {
- if (closing_event_time_.is_null())
- return false;
-
- // If the current (press down) event is a repost event, the time stamp of
- // these two events should be the same.
- return closing_event_time_ == event.time_stamp();
-}
-
-const ShelfItem* ShelfView::ShelfItemForView(const views::View* view) const {
- const int view_index = view_model_->GetIndexOfView(view);
- return (view_index < 0) ? nullptr : &(model_->items()[view_index]);
-}
-
-int ShelfView::CalculateShelfDistance(const gfx::Point& coordinate) const {
- const gfx::Rect bounds = GetBoundsInScreen();
- int distance = wm_shelf_->SelectValueForShelfAlignment(
- bounds.y() - coordinate.y(), coordinate.x() - bounds.right(),
- bounds.x() - coordinate.x());
- return distance > 0 ? distance : 0;
-}
-
-} // namespace ash
« no previous file with comments | « ash/common/shelf/shelf_view.h ('k') | ash/common/shelf/shelf_widget.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698