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
;
54 class PulsingBlockView
;
56 // AppsGridView displays a grid for AppListItemList sub model.
57 class APP_LIST_EXPORT AppsGridView
: public views::View
,
58 public views::ButtonListener
,
59 public AppListItemListObserver
,
60 public PaginationModelObserver
,
61 public AppListModelObserver
,
62 public ui::ImplicitAnimationObserver
{
70 // Constructs the app icon grid view. |delegate| is the delegate of this
71 // view, which usually is the hosting AppListView.
72 explicit AppsGridView(AppsGridViewDelegate
* delegate
);
73 ~AppsGridView() override
;
75 // Sets fixed layout parameters. After setting this, CalculateLayout below
76 // is no longer called to dynamically choosing those layout params.
77 void SetLayout(int cols
, int rows_per_page
);
79 int cols() const { return cols_
; }
80 int rows_per_page() const { return rows_per_page_
; }
82 // Returns the size of a tile view including its padding.
83 static gfx::Size
GetTotalTileSize();
85 // This resets the grid view to a fresh state for showing the app list.
86 void ResetForShowApps();
88 // Sets |model| to use. Note this does not take ownership of |model|.
89 void SetModel(AppListModel
* model
);
91 // Sets the |item_list| to render. Note this does not take ownership of
93 void SetItemList(AppListItemList
* item_list
);
95 void SetSelectedView(AppListItemView
* view
);
96 void ClearSelectedView(AppListItemView
* view
);
97 void ClearAnySelectedView();
98 bool IsSelectedView(const AppListItemView
* view
) const;
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 AppListItemView
* 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(AppListItemView
* 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 gfx::Size
GetPreferredSize() const override
;
134 void Layout() override
;
135 bool OnKeyPressed(const ui::KeyEvent
& event
) override
;
136 bool OnKeyReleased(const ui::KeyEvent
& event
) override
;
137 bool OnMouseWheel(const ui::MouseWheelEvent
& event
) override
;
138 void ViewHierarchyChanged(
139 const ViewHierarchyChangedDetails
& details
) override
;
142 std::set
<OSExchangeData::CustomFormat
>* custom_formats
) override
;
143 bool CanDrop(const OSExchangeData
& data
) override
;
144 int OnDragUpdated(const ui::DropTargetEvent
& event
) override
;
146 // Overridden from ui::EventHandler:
147 void OnGestureEvent(ui::GestureEvent
* event
) override
;
148 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
,
170 bool has_native_drag
);
172 // Updates drag in the root level grid view when receiving the drag event
173 // dispatched from the hidden grid view for reparenting a folder item.
174 void UpdateDragFromReparentItem(Pointer pointer
,
175 const gfx::Point
& drag_point
);
177 // Dispatches the drag event from hidden grid view to the top level grid view.
178 void DispatchDragEventForReparent(Pointer pointer
,
179 const gfx::Point
& drag_point
);
181 // Handles EndDrag event dispatched from the hidden folder grid view in the
182 // root level grid view to end reparenting a folder item.
183 // |events_forwarded_to_drag_drop_host|: True if the dragged item is dropped
184 // to the drag_drop_host, eg. dropped on shelf.
185 // |cancel_drag|: True if the drag is ending because it has been canceled.
186 void EndDragFromReparentItemInRootLevel(
187 bool events_forwarded_to_drag_drop_host
,
190 // Handles EndDrag event in the hidden folder grid view to end reparenting
192 void EndDragForReparentInHiddenFolderGridView();
194 // Called when the folder item associated with the grid view is removed.
195 // The grid view must be inside a folder view.
196 void OnFolderItemRemoved();
198 // Return the view model for test purposes.
199 const views::ViewModelT
<AppListItemView
>* view_model_for_test() const {
203 // For test: Return if the drag and drop handler was set.
204 bool has_drag_and_drop_host_for_test() { return NULL
!= drag_and_drop_host_
; }
206 // For test: Return if the drag and drop operation gets dispatched.
207 bool forward_events_to_drag_and_drop_host_for_test() {
208 return forward_events_to_drag_and_drop_host_
;
211 void set_folder_delegate(AppsGridViewFolderDelegate
* folder_delegate
) {
212 folder_delegate_
= folder_delegate
;
215 AppListItemView
* activated_folder_item_view() const {
216 return activated_folder_item_view_
;
219 const AppListModel
* model() const { return model_
; }
222 friend class test::AppsGridViewTestApi
;
230 // Represents the index to an item view in the grid.
232 Index() : page(-1), slot(-1) {}
233 Index(int page
, int slot
) : page(page
), slot(slot
) {}
235 bool operator==(const Index
& other
) const {
236 return page
== other
.page
&& slot
== other
.slot
;
238 bool operator!=(const Index
& other
) const {
239 return page
!= other
.page
|| slot
!= other
.slot
;
241 bool operator<(const Index
& other
) const {
242 if (page
!= other
.page
)
243 return page
< other
.page
;
245 return slot
< other
.slot
;
248 int page
; // Which page an item view is on.
249 int slot
; // Which slot in the page an item view is in.
252 int tiles_per_page() const { return cols_
* rows_per_page_
; }
254 // Updates from model.
257 // Updates page splits for item views.
260 // Updates the number of pulsing block views based on AppListModel status and
262 void UpdatePulsingBlockViews();
264 AppListItemView
* CreateViewForItemAtIndex(size_t index
);
266 // Convert between the model index and the visual index. The model index
267 // is the index of the item in AppListModel. The visual index is the Index
268 // struct above with page/slot info of where to display the item.
269 Index
GetIndexFromModelIndex(int model_index
) const;
270 int GetModelIndexFromIndex(const Index
& index
) const;
272 // Ensures the view is visible. Note that if there is a running page
273 // transition, this does nothing.
274 void EnsureViewVisible(const Index
& index
);
276 void SetSelectedItemByIndex(const Index
& index
);
277 bool IsValidIndex(const Index
& index
) const;
279 Index
GetIndexOfView(const AppListItemView
* view
) const;
280 AppListItemView
* GetViewAtIndex(const Index
& index
) const;
282 // Gets the index of the AppListItemView at the end of the view model.
283 Index
GetLastViewIndex() const;
285 void MoveSelected(int page_delta
, int slot_x_delta
, int slot_y_delta
);
287 void CalculateIdealBounds();
288 void AnimateToIdealBounds();
290 // Invoked when the given |view|'s current bounds and target bounds are on
291 // different rows. To avoid moving diagonally, |view| would be put into a
292 // slot prior |target| and fade in while moving to |target|. In the meanwhile,
293 // a layer copy of |view| would start at |current| and fade out while moving
294 // to succeeding slot of |current|. |animate_current| controls whether to run
295 // fading out animation from |current|. |animate_target| controls whether to
296 // run fading in animation to |target|.
297 void AnimationBetweenRows(AppListItemView
* view
,
298 bool animate_current
,
299 const gfx::Rect
& current
,
301 const gfx::Rect
& target
);
303 // Extracts drag location info from |event| into |drag_point|.
304 void ExtractDragLocation(const ui::LocatedEvent
& event
,
305 gfx::Point
* drag_point
);
307 // Updates |reorder_drop_target_|, |folder_drop_target_| and |drop_attempt_|
308 // based on |drag_view_|'s position.
309 void CalculateDropTarget();
311 // If |point| is a valid folder drop target, returns true and sets
312 // |drop_target| to the index of the view to do a folder drop for.
313 bool CalculateFolderDropTarget(const gfx::Point
& point
,
314 Index
* drop_target
) const;
316 // Calculates the reorder target |point| and sets |drop_target| to the index
317 // of the view to reorder.
318 void CalculateReorderDropTarget(const gfx::Point
& point
,
319 Index
* drop_target
) const;
321 // Prepares |drag_and_drop_host_| for dragging. |grid_location| contains
322 // the drag point in this grid view's coordinates.
323 void StartDragAndDropHostDrag(const gfx::Point
& grid_location
);
325 // Dispatch the drag and drop update event to the dnd host (if needed).
326 void DispatchDragEventToDragAndDropHost(
327 const gfx::Point
& location_in_screen_coordinates
);
329 // Starts the page flip timer if |drag_point| is in left/right side page flip
330 // zone or is over page switcher.
331 void MaybeStartPageFlipTimer(const gfx::Point
& drag_point
);
333 // Invoked when |page_flip_timer_| fires.
334 void OnPageFlipTimer();
336 // Updates |model_| to move item represented by |item_view| to |target| slot.
337 void MoveItemInModel(AppListItemView
* item_view
, const Index
& target
);
339 // Updates |model_| to move item represented by |item_view| into a folder
340 // containing item located at |target| slot, also update |view_model_| for
341 // the related view changes.
342 void MoveItemToFolder(AppListItemView
* item_view
, const Index
& target
);
344 // Updates both data model and view_model_ for re-parenting a folder item to a
345 // new position in top level item list.
346 void ReparentItemForReorder(AppListItemView
* item_view
, const Index
& target
);
348 // Updates both data model and view_model_ for re-parenting a folder item
349 // to anther folder target. Returns whether the reparent succeeded.
350 bool ReparentItemToAnotherFolder(AppListItemView
* item_view
,
351 const Index
& target
);
353 // If there is only 1 item left in the source folder after reparenting an item
354 // from it, updates both data model and view_model_ for removing last item
355 // from the source folder and removes the source folder.
356 void RemoveLastItemFromReparentItemFolderIfNecessary(
357 const std::string
& source_folder_id
);
359 // If user does not drop the re-parenting folder item to any valid target,
360 // cancel the re-parenting action, let the item go back to its original
361 // parent folder with UI animation.
362 void CancelFolderItemReparent(AppListItemView
* drag_item_view
);
364 // Cancels any context menus showing for app items on the current page.
365 void CancelContextMenusOnCurrentPage();
367 // Removes the AppListItemView at |index| in |view_model_| and deletes it.
368 void DeleteItemViewAtIndex(int index
);
370 // Returns true if |point| lies within the bounds of this grid view plus a
371 // buffer area surrounding it.
372 bool IsPointWithinDragBuffer(const gfx::Point
& point
) const;
374 // Overridden from views::ButtonListener:
375 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
377 // Overridden from AppListItemListObserver:
378 void OnListItemAdded(size_t index
, AppListItem
* item
) override
;
379 void OnListItemRemoved(size_t index
, AppListItem
* item
) override
;
380 void OnListItemMoved(size_t from_index
,
382 AppListItem
* item
) override
;
383 void OnAppListItemHighlight(size_t index
, bool highlight
) override
;
385 // Overridden from PaginationModelObserver:
386 void TotalPagesChanged() override
;
387 void SelectedPageChanged(int old_selected
, int new_selected
) override
;
388 void TransitionStarted() override
;
389 void TransitionChanged() override
;
391 // Overridden from AppListModelObserver:
392 void OnAppListModelStatusChanged() override
;
394 // ui::ImplicitAnimationObserver overrides:
395 void OnImplicitAnimationsCompleted() override
;
397 // Hide a given view temporarily without losing (mouse) events and / or
398 // changing the size of it. If |immediate| is set the change will be
399 // immediately applied - otherwise it will change gradually.
400 // If |hide| is set the view will get hidden, otherwise it gets shown.
401 void SetViewHidden(AppListItemView
* view
, bool hide
, bool immediate
);
403 // Whether the folder drag-and-drop UI should be enabled.
404 bool EnableFolderDragDropUI();
406 // Returns the size of the entire tile grid.
407 gfx::Size
GetTileGridSize() const;
409 // Returns the slot number which the given |point| falls into or the closest
410 // slot if |point| is outside the page's bounds.
411 Index
GetNearestTileIndexForPoint(const gfx::Point
& point
) const;
413 // Gets the bounds of the tile located at |slot| on the current page.
414 gfx::Rect
GetExpectedTileBounds(int slot
) const;
416 // Gets the bounds of the tile located at |row| and |col| on the current page.
417 gfx::Rect
GetExpectedTileBounds(int row
, int col
) const;
419 // Gets the item view currently displayed at |slot| on the current page. If
420 // there is no item displayed at |slot|, returns NULL. Note that this finds an
421 // item *displayed* at a slot, which may differ from the item's location in
422 // the model (as it may have been temporarily moved during a drag operation).
423 AppListItemView
* GetViewDisplayedAtSlotOnCurrentPage(int slot
) const;
425 // Sets state of the view with |target_index| to |is_target_folder| for
426 // dropping |drag_view_|.
427 void SetAsFolderDroppingTarget(const Index
& target_index
,
428 bool is_target_folder
);
430 // Invoked when |reorder_timer_| fires to show re-order preview UI.
431 void OnReorderTimer();
433 // Invoked when |folder_item_reparent_timer_| fires.
434 void OnFolderItemReparentTimer();
436 // Invoked when |folder_dropping_timer_| fires to show folder dropping
438 void OnFolderDroppingTimer();
440 // Updates drag state for dragging inside a folder's grid view.
441 void UpdateDragStateInsideFolder(Pointer pointer
,
442 const gfx::Point
& drag_point
);
444 // Returns true if drag event is happening in the root level AppsGridView
445 // for reparenting a folder item.
446 bool IsDraggingForReparentInRootLevelGridView() const;
448 // Returns true if drag event is happening in the hidden AppsGridView of the
449 // folder during reparenting a folder item.
450 bool IsDraggingForReparentInHiddenGridView() const;
452 // Returns the target icon bounds for |drag_item_view| to fly back
453 // to its parent |folder_item_view| in animation.
454 gfx::Rect
GetTargetIconRectInFolder(AppListItemView
* drag_item_view
,
455 AppListItemView
* folder_item_view
);
457 // Returns true if the grid view is under an OEM folder.
458 bool IsUnderOEMFolder();
460 void StartSettingUpSynchronousDrag();
461 bool RunSynchronousDrag();
462 void CleanUpSynchronousDrag();
464 void OnGotShortcutPath(scoped_refptr
<SynchronousDrag
> drag
,
465 const base::FilePath
& path
);
468 AppListModel
* model_
; // Owned by AppListView.
469 AppListItemList
* item_list_
; // Not owned.
470 AppsGridViewDelegate
* delegate_
;
472 // This can be NULL. Only grid views inside folders have a folder delegate.
473 AppsGridViewFolderDelegate
* folder_delegate_
;
475 PaginationModel pagination_model_
;
476 // Must appear after |pagination_model_|.
477 scoped_ptr
<PaginationController
> pagination_controller_
;
478 PageSwitcher
* page_switcher_view_
; // Owned by views hierarchy.
483 // List of app item views. There is a view per item in |model_|.
484 views::ViewModelT
<AppListItemView
> view_model_
;
486 // List of pulsing block views.
487 views::ViewModelT
<PulsingBlockView
> pulsing_blocks_model_
;
489 AppListItemView
* selected_view_
;
491 AppListItemView
* drag_view_
;
493 // The index of the drag_view_ when the drag starts.
494 Index drag_view_init_index_
;
496 // The point where the drag started in AppListItemView coordinates.
497 gfx::Point drag_view_offset_
;
499 // The point where the drag started in GridView coordinates.
500 gfx::Point drag_start_grid_view_
;
502 // The location of |drag_view_| when the drag started.
503 gfx::Point drag_view_start_
;
505 // Page the drag started on.
506 int drag_start_page_
;
509 // Created when a drag is started (ie: drag exceeds the drag threshold), but
510 // not Run() until supplied with a shortcut path.
511 scoped_refptr
<SynchronousDrag
> synchronous_drag_
;
513 // Whether to use SynchronousDrag to support dropping to task bar etc.
514 bool use_synchronous_drag_
;
517 Pointer drag_pointer_
;
519 // The most recent reorder drop target.
520 Index reorder_drop_target_
;
522 // The most recent folder drop target.
523 Index folder_drop_target_
;
525 // The index where an empty slot has been left as a placeholder for the
526 // reorder drop target. This updates when the reorder animation triggers.
527 Index reorder_placeholder_
;
529 // The current action that ending a drag will perform.
530 DropAttempt drop_attempt_
;
532 // Timer for re-ordering the |drop_target_| and |drag_view_|.
533 base::OneShotTimer
<AppsGridView
> reorder_timer_
;
535 // Timer for dropping |drag_view_| into the folder containing
536 // the |drop_target_|.
537 base::OneShotTimer
<AppsGridView
> folder_dropping_timer_
;
539 // Timer for dragging a folder item out of folder container ink bubble.
540 base::OneShotTimer
<AppsGridView
> folder_item_reparent_timer_
;
542 // An application target drag and drop host which accepts dnd operations.
543 ApplicationDragAndDropHost
* drag_and_drop_host_
;
545 // The drag operation is currently inside the dnd host and events get
547 bool forward_events_to_drag_and_drop_host_
;
549 // Last mouse drag location in this view's coordinates.
550 gfx::Point last_drag_point_
;
552 // Timer to auto flip page when dragging an item near the left/right edges.
553 base::OneShotTimer
<AppsGridView
> page_flip_timer_
;
555 // Target page to switch to when |page_flip_timer_| fires.
556 int page_flip_target_
;
558 // Delay in milliseconds of when |page_flip_timer_| should fire after user
559 // drags an item near the edges.
560 int page_flip_delay_in_ms_
;
562 views::BoundsAnimator bounds_animator_
;
564 // The most recent activated folder item view.
565 AppListItemView
* activated_folder_item_view_
;
567 // Tracks if drag_view_ is dragged out of the folder container bubble
568 // when dragging a item inside a folder.
569 bool drag_out_of_folder_container_
;
571 // True if the drag_view_ item is a folder item being dragged for reparenting.
572 bool dragging_for_reparent_item_
;
574 DISALLOW_COPY_AND_ASSIGN(AppsGridView
);
577 } // namespace app_list
579 #endif // UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_