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 
     10 #include "base/basictypes.h"
     11 #include "base/compiler_specific.h"
     12 #include "base/timer/timer.h"
     13 #include "ui/app_list/app_list_export.h"
     14 #include "ui/app_list/app_list_model.h"
     15 #include "ui/app_list/app_list_model_observer.h"
     16 #include "ui/app_list/pagination_model_observer.h"
     17 #include "ui/base/models/list_model_observer.h"
     18 #include "ui/views/animation/bounds_animator.h"
     19 #include "ui/views/controls/button/button.h"
     20 #include "ui/views/view.h"
     21 #include "ui/views/view_model.h"
     22 
     23 #if defined(OS_WIN)
     24 #include "ui/base/dragdrop/drag_source_win.h"
     25 #endif
     26 
     27 namespace content {
     28 class WebContents;
     29 }
     30 
     31 namespace views {
     32 class ButtonListener;
     33 class DragImageView;
     34 class WebView;
     35 }
     36 
     37 namespace app_list {
     38 
     39 #if defined(OS_WIN)
     40 class SynchronousDrag;
     41 #endif
     42 
     43 namespace test {
     44 class AppsGridViewTestApi;
     45 }
     46 
     47 class ApplicationDragAndDropHost;
     48 class AppListItemView;
     49 class AppsGridViewDelegate;
     50 class PageSwitcher;
     51 class PaginationModel;
     52 
     53 // AppsGridView displays a grid for AppListItemList sub model.
     54 class APP_LIST_EXPORT AppsGridView : public views::View,
     55                                      public views::ButtonListener,
     56                                      public AppListItemListObserver,
     57                                      public PaginationModelObserver,
     58                                      public AppListModelObserver {
     59  public:
     60   enum Pointer {
     61     NONE,
     62     MOUSE,
     63     TOUCH,
     64   };
     65 
     66   // Constructs the app icon grid view. |delegate| is the delegate of this
     67   // view, which usually is the hosting AppListView. |pagination_model| is
     68   // the paging info shared within the launcher UI. |start_page_contents| is
     69   // the contents for the launcher start page. It could be NULL if the start
     70   // page is not available.
     71   AppsGridView(AppsGridViewDelegate* delegate,
     72                PaginationModel* pagination_model,
     73                content::WebContents* start_page_contents);
     74   virtual ~AppsGridView();
     75 
     76   // Sets fixed layout parameters. After setting this, CalculateLayout below
     77   // is no longer called to dynamically choosing those layout params.
     78   void SetLayout(int icon_size, int cols, int rows_per_page);
     79 
     80   // Sets |model| to use. Note this does not take ownership of |model|.
     81   void SetModel(AppListModel* model);
     82 
     83   // Sets the |item_list| to render. Note this does not take ownership of
     84   // |item_list|.
     85   void SetItemList(AppListItemList* item_list);
     86 
     87   void SetSelectedView(views::View* view);
     88   void ClearSelectedView(views::View* view);
     89   void ClearAnySelectedView();
     90   bool IsSelectedView(const views::View* view) const;
     91 
     92   // Ensures the view is visible. Note that if there is a running page
     93   // transition, this does nothing.
     94   void EnsureViewVisible(const views::View* view);
     95 
     96   void InitiateDrag(AppListItemView* view,
     97                     Pointer pointer,
     98                     const ui::LocatedEvent& event);
     99 
    100   // Called from AppListItemView when it receives a drag event.
    101   void UpdateDragFromItem(Pointer pointer,
    102                           const ui::LocatedEvent& event);
    103 
    104   // Called when the user is dragging an app. |point| is in grid view
    105   // coordinates.
    106   void UpdateDrag(Pointer pointer, const gfx::Point& point);
    107   void EndDrag(bool cancel);
    108   bool IsDraggedView(const views::View* view) const;
    109 
    110   void StartSettingUpSynchronousDrag();
    111   bool RunSynchronousDrag();
    112   void CleanUpSynchronousDrag();
    113   void OnGotShortcutPath(const base::FilePath& path);
    114 
    115   // Set the drag and drop host for application links.
    116   void SetDragAndDropHostOfCurrentAppList(
    117       ApplicationDragAndDropHost* drag_and_drop_host);
    118 
    119   // Prerenders the icons on and around |page_index|.
    120   void Prerender(int page_index);
    121 
    122   bool has_dragged_view() const { return drag_view_ != NULL; }
    123   bool dragging() const { return drag_pointer_ != NONE; }
    124 
    125   // Overridden from views::View:
    126   virtual gfx::Size GetPreferredSize() OVERRIDE;
    127   virtual void Layout() OVERRIDE;
    128   virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
    129   virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE;
    130   virtual void ViewHierarchyChanged(
    131       const ViewHierarchyChangedDetails& details) OVERRIDE;
    132   virtual bool GetDropFormats(
    133       int* formats,
    134       std::set<OSExchangeData::CustomFormat>* custom_formats) OVERRIDE;
    135   virtual bool CanDrop(const OSExchangeData& data) OVERRIDE;
    136   virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE;
    137 
    138   // Stops the timer that triggers a page flip during a drag.
    139   void StopPageFlipTimer();
    140 
    141   // Return the view model for test purposes.
    142   const views::ViewModel* view_model_for_test() const { return &view_model_; }
    143 
    144   // For test: Return if the drag and drop handler was set.
    145   bool has_drag_and_drop_host_for_test() { return NULL != drag_and_drop_host_; }
    146 
    147   // For test: Return if the drag and drop operation gets dispatched.
    148   bool forward_events_to_drag_and_drop_host_for_test() {
    149     return forward_events_to_drag_and_drop_host_;
    150   }
    151 
    152   void set_is_root_level(bool value) { is_root_level_ = value; }
    153 
    154  private:
    155   friend class test::AppsGridViewTestApi;
    156 
    157   enum DropAttempt {
    158     DROP_FOR_NONE,
    159     DROP_FOR_REORDER,
    160     DROP_FOR_FOLDER,
    161   };
    162 
    163   // Represents the index to an item view in the grid.
    164   struct Index {
    165     Index() : page(-1), slot(-1) {}
    166     Index(int page, int slot) : page(page), slot(slot) {}
    167 
    168     bool operator==(const Index& other) const {
    169       return page == other.page && slot == other.slot;
    170     }
    171     bool operator!=(const Index& other) const {
    172       return page != other.page || slot != other.slot;
    173     }
    174 
    175     int page;  // Which page an item view is on.
    176     int slot;  // Which slot in the page an item view is in.
    177   };
    178 
    179   int tiles_per_page() const { return cols_ * rows_per_page_; }
    180 
    181   // Updates from model.
    182   void Update();
    183 
    184   // Updates page splits for item views.
    185   void UpdatePaging();
    186 
    187   // Updates the number of pulsing block views based on AppListModel status and
    188   // number of apps.
    189   void UpdatePulsingBlockViews();
    190 
    191   views::View* CreateViewForItemAtIndex(size_t index);
    192 
    193   // Convert between the model index and the visual index. The model index
    194   // is the index of the item in AppListModel. The visual index is the Index
    195   // struct above with page/slot info of where to display the item.
    196   Index GetIndexFromModelIndex(int model_index) const;
    197   int GetModelIndexFromIndex(const Index& index) const;
    198 
    199   void SetSelectedItemByIndex(const Index& index);
    200   bool IsValidIndex(const Index& index) const;
    201 
    202   Index GetIndexOfView(const views::View* view) const;
    203   views::View* GetViewAtIndex(const Index& index) const;
    204 
    205   void MoveSelected(int page_delta, int slot_x_delta, int slot_y_delta);
    206 
    207   void CalculateIdealBounds();
    208   void AnimateToIdealBounds();
    209 
    210   // Invoked when the given |view|'s current bounds and target bounds are on
    211   // different rows. To avoid moving diagonally, |view| would be put into a
    212   // slot prior |target| and fade in while moving to |target|. In the meanwhile,
    213   // a layer copy of |view| would start at |current| and fade out while moving
    214   // to succeeding slot of |current|. |animate_current| controls whether to run
    215   // fading out animation from |current|. |animate_target| controls whether to
    216   // run fading in animation to |target|.
    217   void AnimationBetweenRows(views::View* view,
    218                             bool animate_current,
    219                             const gfx::Rect& current,
    220                             bool animate_target,
    221                             const gfx::Rect& target);
    222 
    223   // Extracts drag location info from |event| into |drag_point|.
    224   void ExtractDragLocation(const ui::LocatedEvent& event,
    225                            gfx::Point* drag_point);
    226 
    227   // Calculates |drop_target_| based on |drag_point|. |drag_point| is in the
    228   // grid view's coordinates. When |use_page_button_hovering| is true and
    229   // |drag_point| is hovering on a page button, use the last slot on that page
    230   // as drop target.
    231   void CalculateDropTarget(const gfx::Point& drag_point,
    232                            bool use_page_button_hovering);
    233 
    234   // Same as CalculateDropTarget, but with folder UI enabled. The |drop_target_|
    235   // can be either a target for re-ordering, or a target folder to move the
    236   // dragged item into if |drag_view_| enters its re-ordering or folder
    237   // dropping circle.
    238   void CalculateDropTargetWithFolderEnabled(const gfx::Point& drag_point,
    239                                             bool use_page_button_hovering);
    240 
    241   // Prepares |drag_and_drop_host_| for dragging. |grid_location| contains
    242   // the drag point in this grid view's coordinates.
    243   void StartDragAndDropHostDrag(const gfx::Point& grid_location);
    244 
    245   // Dispatch the drag and drop update event to the dnd host (if needed).
    246   void DispatchDragEventToDragAndDropHost(
    247       const gfx::Point& location_in_screen_coordinates);
    248 
    249   // Starts the page flip timer if |drag_point| is in left/right side page flip
    250   // zone or is over page switcher.
    251   void MaybeStartPageFlipTimer(const gfx::Point& drag_point);
    252 
    253   // Invoked when |page_flip_timer_| fires.
    254   void OnPageFlipTimer();
    255 
    256   // Updates |model_| to move item represented by |item_view| to |target| slot.
    257   void MoveItemInModel(views::View* item_view, const Index& target);
    258 
    259   // Updates |model_| to move item represented by |item_view| into a folder
    260   // containing item located at |target| slot, also update |view_model_| for
    261   // the related view changes.
    262   void MoveItemToFolder(views::View* item_view, const Index& target);
    263 
    264   // Cancels any context menus showing for app items on the current page.
    265   void CancelContextMenusOnCurrentPage();
    266 
    267   // Returnes true if |point| lies within the bounds of this grid view plus a
    268   // buffer area surrounding it.
    269   bool IsPointWithinDragBuffer(const gfx::Point& point) const;
    270 
    271   // Handles start page layout and transition animation.
    272   void LayoutStartPage();
    273 
    274   // Overridden from views::ButtonListener:
    275   virtual void ButtonPressed(views::Button* sender,
    276                              const ui::Event& event) OVERRIDE;
    277 
    278   // Overridden from AppListItemListObserver:
    279   virtual void OnListItemAdded(size_t index, AppListItemModel* item) OVERRIDE;
    280   virtual void OnListItemRemoved(size_t index, AppListItemModel* item) OVERRIDE;
    281   virtual void OnListItemMoved(size_t from_index,
    282                                size_t to_index,
    283                                AppListItemModel* item) OVERRIDE;
    284 
    285   // Overridden from PaginationModelObserver:
    286   virtual void TotalPagesChanged() OVERRIDE;
    287   virtual void SelectedPageChanged(int old_selected, int new_selected) OVERRIDE;
    288   virtual void TransitionStarted() OVERRIDE;
    289   virtual void TransitionChanged() OVERRIDE;
    290 
    291   // Overridden from AppListModelObserver:
    292   virtual void OnAppListModelStatusChanged() OVERRIDE;
    293 
    294   // Hide a given view temporarily without losing (mouse) events and / or
    295   // changing the size of it. If |immediate| is set the change will be
    296   // immediately applied - otherwise it will change gradually.
    297   // If |hide| is set the view will get hidden, otherwise it gets shown.
    298   void SetViewHidden(views::View* view, bool hide, bool immediate);
    299 
    300   // Whether the folder drag-and-drop UI should be enabled.
    301   bool EnableFolderDragDropUI();
    302 
    303   // Whether target specified by |drap_target| can accept more items to be
    304   // dropped into it.
    305   bool CanDropIntoTarget(const Index& drop_target);
    306 
    307   // Returns the visual index of the nearest tile in which |drag_view_| enters
    308   // either its re-ordering or folder dropping circle.
    309   Index GetNearestTileForDragView();
    310 
    311   // Calculates |nearest_tile| in which |vertex| of the |drag_view| is
    312   // enclosed.
    313   // *|nearest_tile| and *|d_min| will be updated based on the calculation.
    314   // *|d_min| is the distance between |nearest_tile| and |drag_view_|.
    315   void CalculateNearestTileForVertex(
    316       const gfx::Point& vertex, Index* nearest_tile, int* d_min);
    317 
    318   // Returns the bounds of the tile in which |point| is enclosed if there
    319   // is a valid item sits on the tile.
    320   gfx::Rect GetTileBoundsForPoint(const gfx::Point& point, Index* tile_index);
    321 
    322   // Gets the bounds of the tile located at |row| and |col| on current page.
    323   gfx::Rect GetTileBounds(int row, int col) const;
    324 
    325   // Gets the item view located at |slot| on the current page. If there is
    326   // no item located at |slot|, returns NULL.
    327   views::View* GetViewAtSlotOnCurrentPage(int slot);
    328 
    329   // Sets state of the view with |target_index| to |is_target_folder| for
    330   // dropping |drag_view_|.
    331   void SetAsFolderDroppingTarget(const Index& target_index,
    332                                  bool is_target_folder);
    333 
    334   // Invoked when |reorder_timer_| fires to show re-order preview UI.
    335   void OnReorderTimer();
    336 
    337   // Invoked when |folder_dropping_timer_| fires to show folder dropping
    338   // preview UI.
    339   void OnFolderDroppingTimer();
    340 
    341   AppListModel* model_;  // Owned by AppListView.
    342   AppListItemList* item_list_;  // Not owned.
    343   AppsGridViewDelegate* delegate_;
    344   PaginationModel* pagination_model_;  // Owned by AppListController.
    345   PageSwitcher* page_switcher_view_;  // Owned by views hierarchy.
    346   views::WebView* start_page_view_;  // Owned by views hierarchy.
    347 
    348   gfx::Size icon_size_;
    349   int cols_;
    350   int rows_per_page_;
    351 
    352   // Tracks app item views. There is a view per item in |model_|.
    353   views::ViewModel view_model_;
    354 
    355   // Tracks pulsing block views.
    356   views::ViewModel pulsing_blocks_model_;
    357 
    358   views::View* selected_view_;
    359 
    360   AppListItemView* drag_view_;
    361 
    362   // The point where the drag started in AppListItemView coordinates.
    363   gfx::Point drag_view_offset_;
    364 
    365   // The point where the drag started in GridView coordinates.
    366   gfx::Point drag_start_grid_view_;
    367 
    368   // The location of |drag_view_| when the drag started.
    369   gfx::Point drag_view_start_;
    370 
    371   // Page the drag started on.
    372   int drag_start_page_;
    373 
    374 #if defined(OS_WIN)
    375   // Created when a drag is started (ie: drag exceeds the drag threshold), but
    376   // not Run() until supplied with a shortcut path.
    377   scoped_refptr<SynchronousDrag> synchronous_drag_;
    378 #endif
    379 
    380   Pointer drag_pointer_;
    381   Index drop_target_;
    382   DropAttempt drop_attempt_;
    383 
    384   // Timer for re-ordering the |drop_target_| and |drag_view_|.
    385   base::OneShotTimer<AppsGridView> reorder_timer_;
    386 
    387   // Timer for dropping |drag_view_| into the folder containing
    388   // the |drop_target_|.
    389   base::OneShotTimer<AppsGridView> folder_dropping_timer_;
    390 
    391   // An application target drag and drop host which accepts dnd operations.
    392   ApplicationDragAndDropHost* drag_and_drop_host_;
    393 
    394   // The drag operation is currently inside the dnd host and events get
    395   // forwarded.
    396   bool forward_events_to_drag_and_drop_host_;
    397 
    398   // Last mouse drag location in this view's coordinates.
    399   gfx::Point last_drag_point_;
    400 
    401   // Timer to auto flip page when dragging an item near the left/right edges.
    402   base::OneShotTimer<AppsGridView> page_flip_timer_;
    403 
    404   // Target page to switch to when |page_flip_timer_| fires.
    405   int page_flip_target_;
    406 
    407   // Delay in milliseconds of when |page_flip_timer_| should fire after user
    408   // drags an item near the edges.
    409   int page_flip_delay_in_ms_;
    410 
    411   views::BoundsAnimator bounds_animator_;
    412 
    413   // If true, AppsGridView is rending items at the root level of the app list.
    414   bool is_root_level_;
    415 
    416   DISALLOW_COPY_AND_ASSIGN(AppsGridView);
    417 };
    418 
    419 }  // namespace app_list
    420 
    421 #endif  // UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
    422