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