1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
6 #define UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
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"
30 #include "ui/base/dragdrop/drag_source_win.h"
41 class SynchronousDrag;
45 class AppsGridViewTestApi;
48 class ApplicationDragAndDropHost;
49 class AppListItemView;
50 class AppsGridViewDelegate;
51 class AppsGridViewFolderDelegate;
53 class PaginationController;
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 {
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();
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);
78 int cols() const { return cols_; }
79 int rows_per_page() const { return rows_per_page_; }
81 // This resets the grid view to a fresh state for showing the app list.
82 void ResetForShowApps();
84 // Sets |model| to use. Note this does not take ownership of |model|.
85 void SetModel(AppListModel* model);
87 // Sets the |item_list| to render. Note this does not take ownership of
89 void SetItemList(AppListItemList* item_list);
91 void SetSelectedView(views::View* view);
92 void ClearSelectedView(views::View* view);
93 void ClearAnySelectedView();
94 bool IsSelectedView(const views::View* view) const;
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);
100 void InitiateDrag(AppListItemView* view,
102 const ui::LocatedEvent& event);
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);
108 // Called when the user is dragging an app. |point| is in grid view
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);
116 // Set the drag and drop host for application links.
117 void SetDragAndDropHostOfCurrentAppList(
118 ApplicationDragAndDropHost* drag_and_drop_host);
120 // Prerenders the icons on and around the currently selected page.
123 // Return true if the |bounds_animator_| is animating |view|.
124 bool IsAnimatingView(views::View* view);
126 bool has_dragged_view() const { return drag_view_ != NULL; }
127 bool dragging() const { return drag_pointer_ != NONE; }
129 // Gets the PaginationModel used for the grid view.
130 PaginationModel* pagination_model() { return &pagination_model_; }
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(
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;
146 // Overridden from ui::EventHandler:
147 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
148 virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
150 // Stops the timer that triggers a page flip during a drag.
151 void StopPageFlipTimer();
153 // Returns the item view of the item at |index|.
154 AppListItemView* GetItemViewAt(int index) const;
156 // Show or hide the top item views.
157 void SetTopItemViewsVisible(bool visible);
159 // Schedules an animation to show or hide the view.
160 void ScheduleShowHideAnimation(bool show);
162 // Called to initiate drag for reparenting a folder item in root level grid
164 // Both |drag_view_rect| and |drag_pint| is in the coordinates of root level
166 void InitiateDragFromReparentItemInRootLevelGridView(
167 AppListItemView* original_drag_view,
168 const gfx::Rect& drag_view_rect,
169 const gfx::Point& drag_point);
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);
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);
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,
189 // Handles EndDrag event in the hidden folder grid view to end reparenting
191 void EndDragForReparentInHiddenFolderGridView();
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();
197 // Return the view model for test purposes.
198 const views::ViewModel* view_model_for_test() const { return &view_model_; }
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_; }
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_;
208 void set_folder_delegate(AppsGridViewFolderDelegate* folder_delegate) {
209 folder_delegate_ = folder_delegate;
212 AppListItemView* activated_folder_item_view() const {
213 return activated_folder_item_view_;
216 const AppListModel* model() const { return model_; }
219 friend class test::AppsGridViewTestApi;
227 // Represents the index to an item view in the grid.
229 Index() : page(-1), slot(-1) {}
230 Index(int page, int slot) : page(page), slot(slot) {}
232 bool operator==(const Index& other) const {
233 return page == other.page && slot == other.slot;
235 bool operator!=(const Index& other) const {
236 return page != other.page || slot != other.slot;
238 bool operator<(const Index& other) const {
239 if (page != other.page)
240 return page < other.page;
242 return slot < other.slot;
245 int page; // Which page an item view is on.
246 int slot; // Which slot in the page an item view is in.
249 int tiles_per_page() const { return cols_ * rows_per_page_; }
251 // Updates from model.
254 // Updates page splits for item views.
257 // Updates the number of pulsing block views based on AppListModel status and
259 void UpdatePulsingBlockViews();
261 views::View* CreateViewForItemAtIndex(size_t index);
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;
269 void SetSelectedItemByIndex(const Index& index);
270 bool IsValidIndex(const Index& index) const;
272 Index GetIndexOfView(const views::View* view) const;
273 views::View* GetViewAtIndex(const Index& index) const;
275 // Gets the index of the AppListItemView at the end of the view model.
276 Index GetLastViewIndex() const;
278 void MoveSelected(int page_delta, int slot_x_delta, int slot_y_delta);
280 void CalculateIdealBounds();
281 void AnimateToIdealBounds();
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,
294 const gfx::Rect& target);
296 // Extracts drag location info from |event| into |drag_point|.
297 void ExtractDragLocation(const ui::LocatedEvent& event,
298 gfx::Point* drag_point);
300 // Updates |reorder_drop_target_|, |folder_drop_target_| and |drop_attempt_|
301 // based on |drag_view_|'s position.
302 void CalculateDropTarget();
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;
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;
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);
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);
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);
326 // Invoked when |page_flip_timer_| fires.
327 void OnPageFlipTimer();
329 // Updates |model_| to move item represented by |item_view| to |target| slot.
330 void MoveItemInModel(views::View* item_view, const Index& target);
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);
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);
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);
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);
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);
356 // Cancels any context menus showing for app items on the current page.
357 void CancelContextMenusOnCurrentPage();
359 // Removes the AppListItemView at |index| in |view_model_| and deletes it.
360 void DeleteItemViewAtIndex(int index);
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;
366 // Overridden from views::ButtonListener:
367 virtual void ButtonPressed(views::Button* sender,
368 const ui::Event& event) OVERRIDE;
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,
375 AppListItem* item) OVERRIDE;
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;
383 // Overridden from AppListModelObserver:
384 virtual void OnAppListModelStatusChanged() OVERRIDE;
386 // ui::ImplicitAnimationObserver overrides:
387 virtual void OnImplicitAnimationsCompleted() OVERRIDE;
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);
395 // Whether the folder drag-and-drop UI should be enabled.
396 bool EnableFolderDragDropUI();
398 // Whether target specified by |drap_target| can accept more items to be
400 bool CanDropIntoTarget(const Index& drop_target) const;
402 // Returns the size of the entire tile grid.
403 gfx::Size GetTileGridSize() const;
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;
409 // Gets the bounds of the tile located at |slot| on the current page.
410 gfx::Rect GetExpectedTileBounds(int slot) const;
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;
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);
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);
424 // Invoked when |reorder_timer_| fires to show re-order preview UI.
425 void OnReorderTimer();
427 // Invoked when |folder_item_reparent_timer_| fires.
428 void OnFolderItemReparentTimer();
430 // Invoked when |folder_dropping_timer_| fires to show folder dropping
432 void OnFolderDroppingTimer();
434 // Updates drag state for dragging inside a folder's grid view.
435 void UpdateDragStateInsideFolder(Pointer pointer,
436 const gfx::Point& drag_point);
438 // Returns true if drag event is happening in the root level AppsGridView
439 // for reparenting a folder item.
440 bool IsDraggingForReparentInRootLevelGridView() const;
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;
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);
451 // Returns true if the grid view is under an OEM folder.
452 bool IsUnderOEMFolder();
454 void StartSettingUpSynchronousDrag();
455 bool RunSynchronousDrag();
456 void CleanUpSynchronousDrag();
458 void OnGotShortcutPath(scoped_refptr<SynchronousDrag> drag,
459 const base::FilePath& path);
462 AppListModel* model_; // Owned by AppListView.
463 AppListItemList* item_list_; // Not owned.
464 AppsGridViewDelegate* delegate_;
466 // This can be NULL. Only grid views inside folders have a folder delegate.
467 AppsGridViewFolderDelegate* folder_delegate_;
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.
477 // Tracks app item views. There is a view per item in |model_|.
478 views::ViewModel view_model_;
480 // Tracks pulsing block views.
481 views::ViewModel pulsing_blocks_model_;
483 views::View* selected_view_;
485 AppListItemView* drag_view_;
487 // The index of the drag_view_ when the drag starts.
488 Index drag_view_init_index_;
490 // The point where the drag started in AppListItemView coordinates.
491 gfx::Point drag_view_offset_;
493 // The point where the drag started in GridView coordinates.
494 gfx::Point drag_start_grid_view_;
496 // The location of |drag_view_| when the drag started.
497 gfx::Point drag_view_start_;
499 // Page the drag started on.
500 int drag_start_page_;
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_;
507 // Whether to use SynchronousDrag to support dropping to task bar etc.
508 bool use_synchronous_drag_;
511 Pointer drag_pointer_;
513 // The most recent reorder drop target.
514 Index reorder_drop_target_;
516 // The most recent folder drop target.
517 Index folder_drop_target_;
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_;
523 // The current action that ending a drag will perform.
524 DropAttempt drop_attempt_;
526 // Timer for re-ordering the |drop_target_| and |drag_view_|.
527 base::OneShotTimer<AppsGridView> reorder_timer_;
529 // Timer for dropping |drag_view_| into the folder containing
530 // the |drop_target_|.
531 base::OneShotTimer<AppsGridView> folder_dropping_timer_;
533 // Timer for dragging a folder item out of folder container ink bubble.
534 base::OneShotTimer<AppsGridView> folder_item_reparent_timer_;
536 // An application target drag and drop host which accepts dnd operations.
537 ApplicationDragAndDropHost* drag_and_drop_host_;
539 // The drag operation is currently inside the dnd host and events get
541 bool forward_events_to_drag_and_drop_host_;
543 // Last mouse drag location in this view's coordinates.
544 gfx::Point last_drag_point_;
546 // Timer to auto flip page when dragging an item near the left/right edges.
547 base::OneShotTimer<AppsGridView> page_flip_timer_;
549 // Target page to switch to when |page_flip_timer_| fires.
550 int page_flip_target_;
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_;
556 views::BoundsAnimator bounds_animator_;
558 // The most recent activated folder item view.
559 AppListItemView* activated_folder_item_view_;
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_;
565 // True if the drag_view_ item is a folder item being dragged for reparenting.
566 bool dragging_for_reparent_item_;
568 DISALLOW_COPY_AND_ASSIGN(AppsGridView);
571 } // namespace app_list
573 #endif // UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_