Home | History | Annotate | Download | only in views
      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.
      4 
      5 #ifndef UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
      6 #define UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
      7 
      8 #include <set>
      9 #include <string>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/memory/ref_counted.h"
     14 #include "base/timer/timer.h"
     15 #include "ui/app_list/app_list_export.h"
     16 #include "ui/app_list/app_list_model.h"
     17 #include "ui/app_list/app_list_model_observer.h"
     18 #include "ui/app_list/pagination_model.h"
     19 #include "ui/app_list/pagination_model_observer.h"
     20 #include "ui/base/models/list_model_observer.h"
     21 #include "ui/compositor/layer_animation_observer.h"
     22 #include "ui/gfx/image/image_skia_operations.h"
     23 #include "ui/views/animation/bounds_animator.h"
     24 #include "ui/views/controls/button/button.h"
     25 #include "ui/views/controls/image_view.h"
     26 #include "ui/views/view.h"
     27 #include "ui/views/view_model.h"
     28 
     29 #if defined(OS_WIN)
     30 #include "ui/base/dragdrop/drag_source_win.h"
     31 #endif
     32 
     33 namespace views {
     34 class ButtonListener;
     35 class DragImageView;
     36 }
     37 
     38 namespace app_list {
     39 
     40 #if defined(OS_WIN)
     41 class SynchronousDrag;
     42 #endif
     43 
     44 namespace test {
     45 class AppsGridViewTestApi;
     46 }
     47 
     48 class ApplicationDragAndDropHost;
     49 class AppListItemView;
     50 class AppsGridViewDelegate;
     51 class AppsGridViewFolderDelegate;
     52 class PageSwitcher;
     53 class PaginationController;
     54 
     55 // AppsGridView displays a grid for AppListItemList sub model.
     56 class APP_LIST_EXPORT AppsGridView : public views::View,
     57                                      public views::ButtonListener,
     58                                      public AppListItemListObserver,
     59                                      public PaginationModelObserver,
     60                                      public AppListModelObserver,
     61                                      public ui::ImplicitAnimationObserver {
     62  public:
     63   enum Pointer {
     64     NONE,
     65     MOUSE,
     66     TOUCH,
     67   };
     68 
     69   // Constructs the app icon grid view. |delegate| is the delegate of this
     70   // view, which usually is the hosting AppListView.
     71   explicit AppsGridView(AppsGridViewDelegate* delegate);
     72   virtual ~AppsGridView();
     73 
     74   // Sets fixed layout parameters. After setting this, CalculateLayout below
     75   // is no longer called to dynamically choosing those layout params.
     76   void SetLayout(int cols, int rows_per_page);
     77 
     78   int cols() const { return cols_; }
     79   int rows_per_page() const { return rows_per_page_; }
     80 
     81   // This resets the grid view to a fresh state for showing the app list.
     82   void ResetForShowApps();
     83 
     84   // Sets |model| to use. Note this does not take ownership of |model|.
     85   void SetModel(AppListModel* model);
     86 
     87   // Sets the |item_list| to render. Note this does not take ownership of
     88   // |item_list|.
     89   void SetItemList(AppListItemList* item_list);
     90 
     91   void SetSelectedView(views::View* view);
     92   void ClearSelectedView(views::View* view);
     93   void ClearAnySelectedView();
     94   bool IsSelectedView(const views::View* view) const;
     95 
     96   // Ensures the view is visible. Note that if there is a running page
     97   // transition, this does nothing.
     98   void EnsureViewVisible(const views::View* view);
     99 
    100   void InitiateDrag(AppListItemView* view,
    101                     Pointer pointer,
    102                     const ui::LocatedEvent& event);
    103 
    104   // Called from AppListItemView when it receives a drag event. Returns true
    105   // if the drag is still happening.
    106   bool UpdateDragFromItem(Pointer pointer, const ui::LocatedEvent& event);
    107 
    108   // Called when the user is dragging an app. |point| is in grid view
    109   // coordinates.
    110   void UpdateDrag(Pointer pointer, const gfx::Point& point);
    111   void EndDrag(bool cancel);
    112   bool IsDraggedView(const views::View* view) const;
    113   void ClearDragState();
    114   void SetDragViewVisible(bool visible);
    115 
    116   // Set the drag and drop host for application links.
    117   void SetDragAndDropHostOfCurrentAppList(
    118       ApplicationDragAndDropHost* drag_and_drop_host);
    119 
    120   // Prerenders the icons on and around the currently selected page.
    121   void Prerender();
    122 
    123   // Return true if the |bounds_animator_| is animating |view|.
    124   bool IsAnimatingView(views::View* view);
    125 
    126   bool has_dragged_view() const { return drag_view_ != NULL; }
    127   bool dragging() const { return drag_pointer_ != NONE; }
    128 
    129   // Gets the PaginationModel used for the grid view.
    130   PaginationModel* pagination_model() { return &pagination_model_; }
    131 
    132   // Overridden from views::View:
    133   virtual gfx::Size GetPreferredSize() const OVERRIDE;
    134   virtual void Layout() OVERRIDE;
    135   virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
    136   virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE;
    137   virtual bool OnMouseWheel(const ui::MouseWheelEvent& event) OVERRIDE;
    138   virtual void ViewHierarchyChanged(
    139       const ViewHierarchyChangedDetails& details) OVERRIDE;
    140   virtual bool GetDropFormats(
    141       int* formats,
    142       std::set<OSExchangeData::CustomFormat>* custom_formats) OVERRIDE;
    143   virtual bool CanDrop(const OSExchangeData& data) OVERRIDE;
    144   virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE;
    145 
    146   // Overridden from ui::EventHandler:
    147   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
    148   virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
    149 
    150   // Stops the timer that triggers a page flip during a drag.
    151   void StopPageFlipTimer();
    152 
    153   // Returns the item view of the item at |index|.
    154   AppListItemView* GetItemViewAt(int index) const;
    155 
    156   // Show or hide the top item views.
    157   void SetTopItemViewsVisible(bool visible);
    158 
    159   // Schedules an animation to show or hide the view.
    160   void ScheduleShowHideAnimation(bool show);
    161 
    162   // Called to initiate drag for reparenting a folder item in root level grid
    163   // view.
    164   // Both |drag_view_rect| and |drag_pint| is in the coordinates of root level
    165   // grid view.
    166   void InitiateDragFromReparentItemInRootLevelGridView(
    167       AppListItemView* original_drag_view,
    168       const gfx::Rect& drag_view_rect,
    169       const gfx::Point& drag_point);
    170 
    171   // Updates drag in the root level grid view when receiving the drag event
    172   // dispatched from the hidden grid view for reparenting a folder item.
    173   void UpdateDragFromReparentItem(Pointer pointer,
    174                                   const gfx::Point& drag_point);
    175 
    176   // Dispatches the drag event from hidden grid view to the top level grid view.
    177   void DispatchDragEventForReparent(Pointer pointer,
    178                                     const gfx::Point& drag_point);
    179 
    180   // Handles EndDrag event dispatched from the hidden folder grid view in the
    181   // root level grid view to end reparenting a folder item.
    182   // |events_forwarded_to_drag_drop_host|: True if the dragged item is dropped
    183   // to the drag_drop_host, eg. dropped on shelf.
    184   // |cancel_drag|: True if the drag is ending because it has been canceled.
    185   void EndDragFromReparentItemInRootLevel(
    186       bool events_forwarded_to_drag_drop_host,
    187       bool cancel_drag);
    188 
    189   // Handles EndDrag event in the hidden folder grid view to end reparenting
    190   // a folder item.
    191   void EndDragForReparentInHiddenFolderGridView();
    192 
    193   // Called when the folder item associated with the grid view is removed.
    194   // The grid view must be inside a folder view.
    195   void OnFolderItemRemoved();
    196 
    197   // Return the view model for test purposes.
    198   const views::ViewModel* view_model_for_test() const { return &view_model_; }
    199 
    200   // For test: Return if the drag and drop handler was set.
    201   bool has_drag_and_drop_host_for_test() { return NULL != drag_and_drop_host_; }
    202 
    203   // For test: Return if the drag and drop operation gets dispatched.
    204   bool forward_events_to_drag_and_drop_host_for_test() {
    205     return forward_events_to_drag_and_drop_host_;
    206   }
    207 
    208   void set_folder_delegate(AppsGridViewFolderDelegate* folder_delegate) {
    209     folder_delegate_ = folder_delegate;
    210   }
    211 
    212   AppListItemView* activated_folder_item_view() const {
    213     return activated_folder_item_view_;
    214   }
    215 
    216   const AppListModel* model() const { return model_; }
    217 
    218  private:
    219   friend class test::AppsGridViewTestApi;
    220 
    221   enum DropAttempt {
    222     DROP_FOR_NONE,
    223     DROP_FOR_REORDER,
    224     DROP_FOR_FOLDER,
    225   };
    226 
    227   // Represents the index to an item view in the grid.
    228   struct Index {
    229     Index() : page(-1), slot(-1) {}
    230     Index(int page, int slot) : page(page), slot(slot) {}
    231 
    232     bool operator==(const Index& other) const {
    233       return page == other.page && slot == other.slot;
    234     }
    235     bool operator!=(const Index& other) const {
    236       return page != other.page || slot != other.slot;
    237     }
    238     bool operator<(const Index& other) const {
    239       if (page != other.page)
    240         return page < other.page;
    241 
    242       return slot < other.slot;
    243     }
    244 
    245     int page;  // Which page an item view is on.
    246     int slot;  // Which slot in the page an item view is in.
    247   };
    248 
    249   int tiles_per_page() const { return cols_ * rows_per_page_; }
    250 
    251   // Updates from model.
    252   void Update();
    253 
    254   // Updates page splits for item views.
    255   void UpdatePaging();
    256 
    257   // Updates the number of pulsing block views based on AppListModel status and
    258   // number of apps.
    259   void UpdatePulsingBlockViews();
    260 
    261   views::View* CreateViewForItemAtIndex(size_t index);
    262 
    263   // Convert between the model index and the visual index. The model index
    264   // is the index of the item in AppListModel. The visual index is the Index
    265   // struct above with page/slot info of where to display the item.
    266   Index GetIndexFromModelIndex(int model_index) const;
    267   int GetModelIndexFromIndex(const Index& index) const;
    268 
    269   void SetSelectedItemByIndex(const Index& index);
    270   bool IsValidIndex(const Index& index) const;
    271 
    272   Index GetIndexOfView(const views::View* view) const;
    273   views::View* GetViewAtIndex(const Index& index) const;
    274 
    275   // Gets the index of the AppListItemView at the end of the view model.
    276   Index GetLastViewIndex() const;
    277 
    278   void MoveSelected(int page_delta, int slot_x_delta, int slot_y_delta);
    279 
    280   void CalculateIdealBounds();
    281   void AnimateToIdealBounds();
    282 
    283   // Invoked when the given |view|'s current bounds and target bounds are on
    284   // different rows. To avoid moving diagonally, |view| would be put into a
    285   // slot prior |target| and fade in while moving to |target|. In the meanwhile,
    286   // a layer copy of |view| would start at |current| and fade out while moving
    287   // to succeeding slot of |current|. |animate_current| controls whether to run
    288   // fading out animation from |current|. |animate_target| controls whether to
    289   // run fading in animation to |target|.
    290   void AnimationBetweenRows(views::View* view,
    291                             bool animate_current,
    292                             const gfx::Rect& current,
    293                             bool animate_target,
    294                             const gfx::Rect& target);
    295 
    296   // Extracts drag location info from |event| into |drag_point|.
    297   void ExtractDragLocation(const ui::LocatedEvent& event,
    298                            gfx::Point* drag_point);
    299 
    300   // Updates |reorder_drop_target_|, |folder_drop_target_| and |drop_attempt_|
    301   // based on |drag_view_|'s position.
    302   void CalculateDropTarget();
    303 
    304   // If |point| is a valid folder drop target, returns true and sets
    305   // |drop_target| to the index of the view to do a folder drop for.
    306   bool CalculateFolderDropTarget(const gfx::Point& point,
    307                                  Index* drop_target) const;
    308 
    309   // Calculates the reorder target |point| and sets |drop_target| to the index
    310   // of the view to reorder.
    311   void CalculateReorderDropTarget(const gfx::Point& point,
    312                                   Index* drop_target) const;
    313 
    314   // Prepares |drag_and_drop_host_| for dragging. |grid_location| contains
    315   // the drag point in this grid view's coordinates.
    316   void StartDragAndDropHostDrag(const gfx::Point& grid_location);
    317 
    318   // Dispatch the drag and drop update event to the dnd host (if needed).
    319   void DispatchDragEventToDragAndDropHost(
    320       const gfx::Point& location_in_screen_coordinates);
    321 
    322   // Starts the page flip timer if |drag_point| is in left/right side page flip
    323   // zone or is over page switcher.
    324   void MaybeStartPageFlipTimer(const gfx::Point& drag_point);
    325 
    326   // Invoked when |page_flip_timer_| fires.
    327   void OnPageFlipTimer();
    328 
    329   // Updates |model_| to move item represented by |item_view| to |target| slot.
    330   void MoveItemInModel(views::View* item_view, const Index& target);
    331 
    332   // Updates |model_| to move item represented by |item_view| into a folder
    333   // containing item located at |target| slot, also update |view_model_| for
    334   // the related view changes.
    335   void MoveItemToFolder(views::View* item_view, const Index& target);
    336 
    337   // Updates both data model and view_model_ for re-parenting a folder item to a
    338   // new position in top level item list.
    339   void ReparentItemForReorder(views::View* item_view, const Index& target);
    340 
    341   // Updates both data model and view_model_ for re-parenting a folder item
    342   // to anther folder target.
    343   void ReparentItemToAnotherFolder(views::View* item_view, const Index& target);
    344 
    345   // If there is only 1 item left in the source folder after reparenting an item
    346   // from it, updates both data model and view_model_ for removing last item
    347   // from the source folder and removes the source folder.
    348   void RemoveLastItemFromReparentItemFolderIfNecessary(
    349       const std::string& source_folder_id);
    350 
    351   // If user does not drop the re-parenting folder item to any valid target,
    352   // cancel the re-parenting action, let the item go back to its original
    353   // parent folder with UI animation.
    354   void CancelFolderItemReparent(AppListItemView* drag_item_view);
    355 
    356   // Cancels any context menus showing for app items on the current page.
    357   void CancelContextMenusOnCurrentPage();
    358 
    359   // Removes the AppListItemView at |index| in |view_model_| and deletes it.
    360   void DeleteItemViewAtIndex(int index);
    361 
    362   // Returns true if |point| lies within the bounds of this grid view plus a
    363   // buffer area surrounding it.
    364   bool IsPointWithinDragBuffer(const gfx::Point& point) const;
    365 
    366   // Overridden from views::ButtonListener:
    367   virtual void ButtonPressed(views::Button* sender,
    368                              const ui::Event& event) OVERRIDE;
    369 
    370   // Overridden from AppListItemListObserver:
    371   virtual void OnListItemAdded(size_t index, AppListItem* item) OVERRIDE;
    372   virtual void OnListItemRemoved(size_t index, AppListItem* item) OVERRIDE;
    373   virtual void OnListItemMoved(size_t from_index,
    374                                size_t to_index,
    375                                AppListItem* item) OVERRIDE;
    376 
    377   // Overridden from PaginationModelObserver:
    378   virtual void TotalPagesChanged() OVERRIDE;
    379   virtual void SelectedPageChanged(int old_selected, int new_selected) OVERRIDE;
    380   virtual void TransitionStarted() OVERRIDE;
    381   virtual void TransitionChanged() OVERRIDE;
    382 
    383   // Overridden from AppListModelObserver:
    384   virtual void OnAppListModelStatusChanged() OVERRIDE;
    385 
    386   // ui::ImplicitAnimationObserver overrides:
    387   virtual void OnImplicitAnimationsCompleted() OVERRIDE;
    388 
    389   // Hide a given view temporarily without losing (mouse) events and / or
    390   // changing the size of it. If |immediate| is set the change will be
    391   // immediately applied - otherwise it will change gradually.
    392   // If |hide| is set the view will get hidden, otherwise it gets shown.
    393   void SetViewHidden(views::View* view, bool hide, bool immediate);
    394 
    395   // Whether the folder drag-and-drop UI should be enabled.
    396   bool EnableFolderDragDropUI();
    397 
    398   // Whether target specified by |drap_target| can accept more items to be
    399   // dropped into it.
    400   bool CanDropIntoTarget(const Index& drop_target) const;
    401 
    402   // Returns the size of the entire tile grid.
    403   gfx::Size GetTileGridSize() const;
    404 
    405   // Returns the slot number which the given |point| falls into or the closest
    406   // slot if |point| is outside the page's bounds.
    407   Index GetNearestTileIndexForPoint(const gfx::Point& point) const;
    408 
    409   // Gets the bounds of the tile located at |slot| on the current page.
    410   gfx::Rect GetExpectedTileBounds(int slot) const;
    411 
    412   // Gets the bounds of the tile located at |row| and |col| on the current page.
    413   gfx::Rect GetExpectedTileBounds(int row, int col) const;
    414 
    415   // Gets the item view located at |slot| on the current page. If there is
    416   // no item located at |slot|, returns NULL.
    417   views::View* GetViewAtSlotOnCurrentPage(int slot) const;
    418 
    419   // Sets state of the view with |target_index| to |is_target_folder| for
    420   // dropping |drag_view_|.
    421   void SetAsFolderDroppingTarget(const Index& target_index,
    422                                  bool is_target_folder);
    423 
    424   // Invoked when |reorder_timer_| fires to show re-order preview UI.
    425   void OnReorderTimer();
    426 
    427   // Invoked when |folder_item_reparent_timer_| fires.
    428   void OnFolderItemReparentTimer();
    429 
    430   // Invoked when |folder_dropping_timer_| fires to show folder dropping
    431   // preview UI.
    432   void OnFolderDroppingTimer();
    433 
    434   // Updates drag state for dragging inside a folder's grid view.
    435   void UpdateDragStateInsideFolder(Pointer pointer,
    436                                    const gfx::Point& drag_point);
    437 
    438   // Returns true if drag event is happening in the root level AppsGridView
    439   // for reparenting a folder item.
    440   bool IsDraggingForReparentInRootLevelGridView() const;
    441 
    442   // Returns true if drag event is happening in the hidden AppsGridView of the
    443   // folder during reparenting a folder item.
    444   bool IsDraggingForReparentInHiddenGridView() const;
    445 
    446   // Returns the target icon bounds for |drag_item_view| to fly back
    447   // to its parent |folder_item_view| in animation.
    448   gfx::Rect GetTargetIconRectInFolder(AppListItemView* drag_item_view,
    449       AppListItemView* folder_item_view);
    450 
    451   // Returns true if the grid view is under an OEM folder.
    452   bool IsUnderOEMFolder();
    453 
    454   void StartSettingUpSynchronousDrag();
    455   bool RunSynchronousDrag();
    456   void CleanUpSynchronousDrag();
    457 #if defined(OS_WIN)
    458   void OnGotShortcutPath(scoped_refptr<SynchronousDrag> drag,
    459                          const base::FilePath& path);
    460 #endif
    461 
    462   AppListModel* model_;  // Owned by AppListView.
    463   AppListItemList* item_list_;  // Not owned.
    464   AppsGridViewDelegate* delegate_;
    465 
    466   // This can be NULL. Only grid views inside folders have a folder delegate.
    467   AppsGridViewFolderDelegate* folder_delegate_;
    468 
    469   PaginationModel pagination_model_;
    470   // Must appear after |pagination_model_|.
    471   scoped_ptr<PaginationController> pagination_controller_;
    472   PageSwitcher* page_switcher_view_;  // Owned by views hierarchy.
    473 
    474   int cols_;
    475   int rows_per_page_;
    476 
    477   // Tracks app item views. There is a view per item in |model_|.
    478   views::ViewModel view_model_;
    479 
    480   // Tracks pulsing block views.
    481   views::ViewModel pulsing_blocks_model_;
    482 
    483   views::View* selected_view_;
    484 
    485   AppListItemView* drag_view_;
    486 
    487   // The index of the drag_view_ when the drag starts.
    488   Index drag_view_init_index_;
    489 
    490   // The point where the drag started in AppListItemView coordinates.
    491   gfx::Point drag_view_offset_;
    492 
    493   // The point where the drag started in GridView coordinates.
    494   gfx::Point drag_start_grid_view_;
    495 
    496   // The location of |drag_view_| when the drag started.
    497   gfx::Point drag_view_start_;
    498 
    499   // Page the drag started on.
    500   int drag_start_page_;
    501 
    502 #if defined(OS_WIN)
    503   // Created when a drag is started (ie: drag exceeds the drag threshold), but
    504   // not Run() until supplied with a shortcut path.
    505   scoped_refptr<SynchronousDrag> synchronous_drag_;
    506 
    507   // Whether to use SynchronousDrag to support dropping to task bar etc.
    508   bool use_synchronous_drag_;
    509 #endif
    510 
    511   Pointer drag_pointer_;
    512 
    513   // The most recent reorder drop target.
    514   Index reorder_drop_target_;
    515 
    516   // The most recent folder drop target.
    517   Index folder_drop_target_;
    518 
    519   // The index where an empty slot has been left as a placeholder for the
    520   // reorder drop target. This updates when the reorder animation triggers.
    521   Index reorder_placeholder_;
    522 
    523   // The current action that ending a drag will perform.
    524   DropAttempt drop_attempt_;
    525 
    526   // Timer for re-ordering the |drop_target_| and |drag_view_|.
    527   base::OneShotTimer<AppsGridView> reorder_timer_;
    528 
    529   // Timer for dropping |drag_view_| into the folder containing
    530   // the |drop_target_|.
    531   base::OneShotTimer<AppsGridView> folder_dropping_timer_;
    532 
    533   // Timer for dragging a folder item out of folder container ink bubble.
    534   base::OneShotTimer<AppsGridView> folder_item_reparent_timer_;
    535 
    536   // An application target drag and drop host which accepts dnd operations.
    537   ApplicationDragAndDropHost* drag_and_drop_host_;
    538 
    539   // The drag operation is currently inside the dnd host and events get
    540   // forwarded.
    541   bool forward_events_to_drag_and_drop_host_;
    542 
    543   // Last mouse drag location in this view's coordinates.
    544   gfx::Point last_drag_point_;
    545 
    546   // Timer to auto flip page when dragging an item near the left/right edges.
    547   base::OneShotTimer<AppsGridView> page_flip_timer_;
    548 
    549   // Target page to switch to when |page_flip_timer_| fires.
    550   int page_flip_target_;
    551 
    552   // Delay in milliseconds of when |page_flip_timer_| should fire after user
    553   // drags an item near the edges.
    554   int page_flip_delay_in_ms_;
    555 
    556   views::BoundsAnimator bounds_animator_;
    557 
    558   // The most recent activated folder item view.
    559   AppListItemView* activated_folder_item_view_;
    560 
    561   // Tracks if drag_view_ is dragged out of the folder container bubble
    562   // when dragging a item inside a folder.
    563   bool drag_out_of_folder_container_;
    564 
    565   // True if the drag_view_ item is a folder item being dragged for reparenting.
    566   bool dragging_for_reparent_item_;
    567 
    568   DISALLOW_COPY_AND_ASSIGN(AppsGridView);
    569 };
    570 
    571 }  // namespace app_list
    572 
    573 #endif  // UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
    574