Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: ui/app_list/views/apps_grid_view.cc

Issue 553753003: Rework app list item drag zones. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@fasdaj
Patch Set: fix test Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/app_list/views/apps_grid_view.h ('k') | ui/app_list/views/apps_grid_view_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/app_list/views/apps_grid_view.h" 5 #include "ui/app_list/views/apps_grid_view.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <set> 8 #include <set>
9 #include <string> 9 #include <string>
10 10
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 const int kFolderDroppingDelay = 150; 87 const int kFolderDroppingDelay = 150;
88 88
89 // Delays in milliseconds to show re-order preview. 89 // Delays in milliseconds to show re-order preview.
90 const int kReorderDelay = 120; 90 const int kReorderDelay = 120;
91 91
92 // Delays in milliseconds to show folder item reparent UI. 92 // Delays in milliseconds to show folder item reparent UI.
93 const int kFolderItemReparentDelay = 50; 93 const int kFolderItemReparentDelay = 50;
94 94
95 // Radius of the circle, in which if entered, show folder dropping preview 95 // Radius of the circle, in which if entered, show folder dropping preview
96 // UI. 96 // UI.
97 const int kFolderDroppingCircleRadius = 15; 97 const int kFolderDroppingCircleRadius = 39;
98 98
99 // RowMoveAnimationDelegate is used when moving an item into a different row. 99 // RowMoveAnimationDelegate is used when moving an item into a different row.
100 // Before running the animation, the item's layer is re-created and kept in 100 // Before running the animation, the item's layer is re-created and kept in
101 // the original position, then the item is moved to just before its target 101 // the original position, then the item is moved to just before its target
102 // position and opacity set to 0. When the animation runs, this delegate moves 102 // position and opacity set to 0. When the animation runs, this delegate moves
103 // the layer and fades it out while fading in the item at the same time. 103 // the layer and fades it out while fading in the item at the same time.
104 class RowMoveAnimationDelegate : public gfx::AnimationDelegate { 104 class RowMoveAnimationDelegate : public gfx::AnimationDelegate {
105 public: 105 public:
106 RowMoveAnimationDelegate(views::View* view, 106 RowMoveAnimationDelegate(views::View* view,
107 ui::Layer* layer, 107 ui::Layer* layer,
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE { 182 virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE {
183 view_->SchedulePaint(); 183 view_->SchedulePaint();
184 } 184 }
185 185
186 private: 186 private:
187 views::View* view_; 187 views::View* view_;
188 188
189 DISALLOW_COPY_AND_ASSIGN(ItemMoveAnimationDelegate); 189 DISALLOW_COPY_AND_ASSIGN(ItemMoveAnimationDelegate);
190 }; 190 };
191 191
192 // Gets the distance between the centers of the |rect_1| and |rect_2|.
193 int GetDistanceBetweenRects(gfx::Rect rect_1,
194 gfx::Rect rect_2) {
195 return (rect_1.CenterPoint() - rect_2.CenterPoint()).Length();
196 }
197
198 // Returns true if the |item| is an folder item. 192 // Returns true if the |item| is an folder item.
199 bool IsFolderItem(AppListItem* item) { 193 bool IsFolderItem(AppListItem* item) {
200 return (item->GetItemType() == AppListFolderItem::kItemType); 194 return (item->GetItemType() == AppListFolderItem::kItemType);
201 } 195 }
202 196
203 bool IsOEMFolderItem(AppListItem* item) { 197 bool IsOEMFolderItem(AppListItem* item) {
204 return IsFolderItem(item) && 198 return IsFolderItem(item) &&
205 (static_cast<AppListFolderItem*>(item))->folder_type() == 199 (static_cast<AppListFolderItem*>(item))->folder_type() ==
206 AppListFolderItem::FOLDER_TYPE_OEM; 200 AppListFolderItem::FOLDER_TYPE_OEM;
207 } 201 }
208 202
203 int ClampToRange(int value, int min, int max) {
204 return std::min(std::max(value, min), max);
205 }
206
209 } // namespace 207 } // namespace
210 208
211 #if defined(OS_WIN) 209 #if defined(OS_WIN)
212 // Interprets drag events sent from Windows via the drag/drop API and forwards 210 // Interprets drag events sent from Windows via the drag/drop API and forwards
213 // them to AppsGridView. 211 // them to AppsGridView.
214 // On Windows, in order to have the OS perform the drag properly we need to 212 // On Windows, in order to have the OS perform the drag properly we need to
215 // provide it with a shortcut file which may or may not exist at the time the 213 // provide it with a shortcut file which may or may not exist at the time the
216 // drag is started. Therefore while waiting for that shortcut to be located we 214 // drag is started. Therefore while waiting for that shortcut to be located we
217 // just do a regular "internal" drag and transition into the synchronous drag 215 // just do a regular "internal" drag and transition into the synchronous drag
218 // when the shortcut is found/created. Hence a synchronous drag is an optional 216 // when the shortcut is found/created. Hence a synchronous drag is an optional
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
481 Pointer pointer, 479 Pointer pointer,
482 const ui::LocatedEvent& event) { 480 const ui::LocatedEvent& event) {
483 DCHECK(view); 481 DCHECK(view);
484 if (drag_view_ || pulsing_blocks_model_.view_size()) 482 if (drag_view_ || pulsing_blocks_model_.view_size())
485 return; 483 return;
486 484
487 drag_view_ = view; 485 drag_view_ = view;
488 drag_view_init_index_ = GetIndexOfView(drag_view_); 486 drag_view_init_index_ = GetIndexOfView(drag_view_);
489 drag_view_offset_ = event.location(); 487 drag_view_offset_ = event.location();
490 drag_start_page_ = pagination_model_.selected_page(); 488 drag_start_page_ = pagination_model_.selected_page();
489 reorder_placeholder_ = drag_view_init_index_;
491 ExtractDragLocation(event, &drag_start_grid_view_); 490 ExtractDragLocation(event, &drag_start_grid_view_);
492 drag_view_start_ = gfx::Point(drag_view_->x(), drag_view_->y()); 491 drag_view_start_ = gfx::Point(drag_view_->x(), drag_view_->y());
493 } 492 }
494 493
495 void AppsGridView::StartSettingUpSynchronousDrag() { 494 void AppsGridView::StartSettingUpSynchronousDrag() {
496 #if defined(OS_WIN) 495 #if defined(OS_WIN)
497 if (!delegate_ || !use_synchronous_drag_) 496 if (!delegate_ || !use_synchronous_drag_)
498 return; 497 return;
499 498
500 // Folders and downloading items can't be integrated with the OS. 499 // Folders and downloading items can't be integrated with the OS.
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
608 return; 607 return;
609 608
610 StartSettingUpSynchronousDrag(); 609 StartSettingUpSynchronousDrag();
611 if (!dragging_for_reparent_item_) 610 if (!dragging_for_reparent_item_)
612 StartDragAndDropHostDrag(point); 611 StartDragAndDropHostDrag(point);
613 } 612 }
614 613
615 if (drag_pointer_ != pointer) 614 if (drag_pointer_ != pointer)
616 return; 615 return;
617 616
617 drag_view_->SetPosition(drag_view_start_ + drag_vector);
618
618 last_drag_point_ = point; 619 last_drag_point_ = point;
619 const Index last_reorder_drop_target = reorder_drop_target_; 620 const Index last_reorder_drop_target = reorder_drop_target_;
620 const Index last_folder_drop_target = folder_drop_target_; 621 const Index last_folder_drop_target = folder_drop_target_;
621 DropAttempt last_drop_attempt = drop_attempt_; 622 DropAttempt last_drop_attempt = drop_attempt_;
622 CalculateDropTarget(last_drag_point_, false); 623 CalculateDropTarget();
623 624
624 if (IsPointWithinDragBuffer(last_drag_point_)) 625 MaybeStartPageFlipTimer(last_drag_point_);
625 MaybeStartPageFlipTimer(last_drag_point_);
626 else
627 StopPageFlipTimer();
628 626
629 if (page_switcher_view_) { 627 if (page_switcher_view_) {
630 gfx::Point page_switcher_point(last_drag_point_); 628 gfx::Point page_switcher_point(last_drag_point_);
631 views::View::ConvertPointToTarget( 629 views::View::ConvertPointToTarget(
632 this, page_switcher_view_, &page_switcher_point); 630 this, page_switcher_view_, &page_switcher_point);
633 page_switcher_view_->UpdateUIForDragPoint(page_switcher_point); 631 page_switcher_view_->UpdateUIForDragPoint(page_switcher_point);
634 } 632 }
635 633
636 if (!EnableFolderDragDropUI()) {
637 if (last_reorder_drop_target != reorder_drop_target_)
638 AnimateToIdealBounds();
639 drag_view_->SetPosition(drag_view_start_ + drag_vector);
640 return;
641 }
642
643 // Update drag with folder UI enabled.
644 if (last_folder_drop_target != folder_drop_target_ || 634 if (last_folder_drop_target != folder_drop_target_ ||
645 last_reorder_drop_target != reorder_drop_target_ || 635 last_reorder_drop_target != reorder_drop_target_ ||
646 last_drop_attempt != drop_attempt_) { 636 last_drop_attempt != drop_attempt_) {
647 if (drop_attempt_ == DROP_FOR_REORDER) { 637 if (drop_attempt_ == DROP_FOR_REORDER) {
648 folder_dropping_timer_.Stop(); 638 folder_dropping_timer_.Stop();
649 reorder_timer_.Start(FROM_HERE, 639 reorder_timer_.Start(FROM_HERE,
650 base::TimeDelta::FromMilliseconds(kReorderDelay), 640 base::TimeDelta::FromMilliseconds(kReorderDelay),
651 this, &AppsGridView::OnReorderTimer); 641 this, &AppsGridView::OnReorderTimer);
652 } else if (drop_attempt_ == DROP_FOR_FOLDER) { 642 } else if (drop_attempt_ == DROP_FOR_FOLDER) {
653 reorder_timer_.Stop(); 643 reorder_timer_.Stop();
654 folder_dropping_timer_.Start(FROM_HERE, 644 folder_dropping_timer_.Start(FROM_HERE,
655 base::TimeDelta::FromMilliseconds(kFolderDroppingDelay), 645 base::TimeDelta::FromMilliseconds(kFolderDroppingDelay),
656 this, &AppsGridView::OnFolderDroppingTimer); 646 this, &AppsGridView::OnFolderDroppingTimer);
657 } 647 }
658 648
659 // Reset the previous drop target. 649 // Reset the previous drop target.
660 SetAsFolderDroppingTarget(last_folder_drop_target, false); 650 SetAsFolderDroppingTarget(last_folder_drop_target, false);
661 } 651 }
662
663 drag_view_->SetPosition(drag_view_start_ + drag_vector);
664 } 652 }
665 653
666 void AppsGridView::EndDrag(bool cancel) { 654 void AppsGridView::EndDrag(bool cancel) {
667 // EndDrag was called before if |drag_view_| is NULL. 655 // EndDrag was called before if |drag_view_| is NULL.
668 if (!drag_view_) 656 if (!drag_view_)
669 return; 657 return;
670 658
671 // Coming here a drag and drop was in progress. 659 // Coming here a drag and drop was in progress.
672 bool landed_in_drag_and_drop_host = forward_events_to_drag_and_drop_host_; 660 bool landed_in_drag_and_drop_host = forward_events_to_drag_and_drop_host_;
673 if (forward_events_to_drag_and_drop_host_) { 661 if (forward_events_to_drag_and_drop_host_) {
(...skipping 18 matching lines...) Expand all
692 if (IsDraggingForReparentInRootLevelGridView()) { 680 if (IsDraggingForReparentInRootLevelGridView()) {
693 // An EndDrag can be received during a reparent via a model change. This 681 // An EndDrag can be received during a reparent via a model change. This
694 // is always a cancel and needs to be forwarded to the folder. 682 // is always a cancel and needs to be forwarded to the folder.
695 DCHECK(cancel); 683 DCHECK(cancel);
696 delegate_->CancelDragInActiveFolder(); 684 delegate_->CancelDragInActiveFolder();
697 return; 685 return;
698 } 686 }
699 687
700 if (!cancel && dragging()) { 688 if (!cancel && dragging()) {
701 // Regular drag ending path, ie, not for reparenting. 689 // Regular drag ending path, ie, not for reparenting.
702 CalculateDropTarget(last_drag_point_, true); 690 CalculateDropTarget();
703 if (EnableFolderDragDropUI() && drop_attempt_ == DROP_FOR_FOLDER && 691 if (EnableFolderDragDropUI() && drop_attempt_ == DROP_FOR_FOLDER &&
704 IsValidIndex(folder_drop_target_)) { 692 IsValidIndex(folder_drop_target_)) {
705 MoveItemToFolder(drag_view_, folder_drop_target_); 693 MoveItemToFolder(drag_view_, folder_drop_target_);
706 } else if (IsValidIndex(reorder_drop_target_)) { 694 } else if (IsValidIndex(reorder_drop_target_)) {
707 MoveItemInModel(drag_view_, reorder_drop_target_); 695 MoveItemInModel(drag_view_, reorder_drop_target_);
708 } 696 }
709 } 697 }
710 } 698 }
711 699
712 if (drag_and_drop_host_) { 700 if (drag_and_drop_host_) {
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
777 layer()->SetOpacity(show ? 1.0f : 0.0f); 765 layer()->SetOpacity(show ? 1.0f : 0.0f);
778 } 766 }
779 767
780 void AppsGridView::InitiateDragFromReparentItemInRootLevelGridView( 768 void AppsGridView::InitiateDragFromReparentItemInRootLevelGridView(
781 AppListItemView* original_drag_view, 769 AppListItemView* original_drag_view,
782 const gfx::Rect& drag_view_rect, 770 const gfx::Rect& drag_view_rect,
783 const gfx::Point& drag_point) { 771 const gfx::Point& drag_point) {
784 DCHECK(original_drag_view && !drag_view_); 772 DCHECK(original_drag_view && !drag_view_);
785 DCHECK(!dragging_for_reparent_item_); 773 DCHECK(!dragging_for_reparent_item_);
786 774
775 // Since the item is new, its placeholder is conceptually at the back of the
776 // entire apps grid.
777 reorder_placeholder_ = GetLastViewIndex();
778
787 // Create a new AppListItemView to duplicate the original_drag_view in the 779 // Create a new AppListItemView to duplicate the original_drag_view in the
788 // folder's grid view. 780 // folder's grid view.
789 AppListItemView* view = new AppListItemView(this, original_drag_view->item()); 781 AppListItemView* view = new AppListItemView(this, original_drag_view->item());
790 AddChildView(view); 782 AddChildView(view);
791 drag_view_ = view; 783 drag_view_ = view;
792 drag_view_->SetPaintToLayer(true); 784 drag_view_->SetPaintToLayer(true);
793 // Note: For testing purpose, SetFillsBoundsOpaquely can be set to true to 785 // Note: For testing purpose, SetFillsBoundsOpaquely can be set to true to
794 // show the gray background. 786 // show the gray background.
795 drag_view_->SetFillsBoundsOpaquely(false); 787 drag_view_->SetFillsBoundsOpaquely(false);
796 drag_view_->SetBoundsRect(drag_view_rect); 788 drag_view_->SetBoundsRect(drag_view_rect);
(...skipping 29 matching lines...) Expand all
826 818
827 bool AppsGridView::IsDraggedView(const views::View* view) const { 819 bool AppsGridView::IsDraggedView(const views::View* view) const {
828 return drag_view_ == view; 820 return drag_view_ == view;
829 } 821 }
830 822
831 void AppsGridView::ClearDragState() { 823 void AppsGridView::ClearDragState() {
832 drop_attempt_ = DROP_FOR_NONE; 824 drop_attempt_ = DROP_FOR_NONE;
833 drag_pointer_ = NONE; 825 drag_pointer_ = NONE;
834 reorder_drop_target_ = Index(); 826 reorder_drop_target_ = Index();
835 folder_drop_target_ = Index(); 827 folder_drop_target_ = Index();
828 reorder_placeholder_ = Index();
836 drag_start_grid_view_ = gfx::Point(); 829 drag_start_grid_view_ = gfx::Point();
837 drag_start_page_ = -1; 830 drag_start_page_ = -1;
838 drag_view_offset_ = gfx::Point(); 831 drag_view_offset_ = gfx::Point();
839 832
840 if (drag_view_) { 833 if (drag_view_) {
841 drag_view_->OnDragEnded(); 834 drag_view_->OnDragEnded();
842 if (IsDraggingForReparentInRootLevelGridView()) { 835 if (IsDraggingForReparentInRootLevelGridView()) {
843 const int drag_view_index = view_model_.GetIndexOfView(drag_view_); 836 const int drag_view_index = view_model_.GetIndexOfView(drag_view_);
844 CHECK_EQ(view_model_.view_size() - 1, drag_view_index); 837 CHECK_EQ(view_model_.view_size() - 1, drag_view_index);
845 DeleteItemViewAtIndex(drag_view_index); 838 DeleteItemViewAtIndex(drag_view_index);
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
1108 } 1101 }
1109 1102
1110 views::View* AppsGridView::GetViewAtIndex(const Index& index) const { 1103 views::View* AppsGridView::GetViewAtIndex(const Index& index) const {
1111 if (!IsValidIndex(index)) 1104 if (!IsValidIndex(index))
1112 return NULL; 1105 return NULL;
1113 1106
1114 const int model_index = GetModelIndexFromIndex(index); 1107 const int model_index = GetModelIndexFromIndex(index);
1115 return view_model_.view_at(model_index); 1108 return view_model_.view_at(model_index);
1116 } 1109 }
1117 1110
1111 AppsGridView::Index AppsGridView::GetLastViewIndex() const {
1112 DCHECK_LT(0, view_model_.view_size());
1113 int view_index = view_model_.view_size() - 1;
1114 return Index(view_index / tiles_per_page(), view_index % tiles_per_page());
1115 }
1116
1118 void AppsGridView::MoveSelected(int page_delta, 1117 void AppsGridView::MoveSelected(int page_delta,
1119 int slot_x_delta, 1118 int slot_x_delta,
1120 int slot_y_delta) { 1119 int slot_y_delta) {
1121 if (!selected_view_) 1120 if (!selected_view_)
1122 return SetSelectedItemByIndex(Index(pagination_model_.selected_page(), 0)); 1121 return SetSelectedItemByIndex(Index(pagination_model_.selected_page(), 0));
1123 1122
1124 const Index& selected = GetIndexOfView(selected_view_); 1123 const Index& selected = GetIndexOfView(selected_view_);
1125 int target_slot = selected.slot + slot_x_delta + slot_y_delta * cols_; 1124 int target_slot = selected.slot + slot_x_delta + slot_y_delta * cols_;
1126 1125
1127 if (selected.slot % cols_ == 0 && slot_x_delta == -1) { 1126 if (selected.slot % cols_ == 0 && slot_x_delta == -1) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1171 pagination_model_.transition(); 1170 pagination_model_.transition();
1172 const bool is_valid = pagination_model_.is_valid_page(transition.target_page); 1171 const bool is_valid = pagination_model_.is_valid_page(transition.target_page);
1173 1172
1174 // Transition to previous page means negative offset. 1173 // Transition to previous page means negative offset.
1175 const int dir = transition.target_page > current_page ? -1 : 1; 1174 const int dir = transition.target_page > current_page ? -1 : 1;
1176 1175
1177 const int total_views = 1176 const int total_views =
1178 view_model_.view_size() + pulsing_blocks_model_.view_size(); 1177 view_model_.view_size() + pulsing_blocks_model_.view_size();
1179 int slot_index = 0; 1178 int slot_index = 0;
1180 for (int i = 0; i < total_views; ++i) { 1179 for (int i = 0; i < total_views; ++i) {
1181 if (i < view_model_.view_size() && view_model_.view_at(i) == drag_view_) { 1180 if (i < view_model_.view_size() && view_model_.view_at(i) == drag_view_)
1182 if (EnableFolderDragDropUI() && drop_attempt_ == DROP_FOR_FOLDER)
1183 ++slot_index;
1184 continue; 1181 continue;
1185 }
1186 1182
1187 Index view_index = GetIndexFromModelIndex(slot_index); 1183 Index view_index = GetIndexFromModelIndex(slot_index);
1188 1184
1189 // Leaves a blank space in the grid for the current reorder drop target. 1185 // Leaves a blank space in the grid for the current reorder placeholder.
1190 if (reorder_drop_target_ == view_index && 1186 if (reorder_placeholder_ == view_index) {
1191 drop_attempt_ == DROP_FOR_REORDER) {
1192 ++slot_index; 1187 ++slot_index;
1193 view_index = GetIndexFromModelIndex(slot_index); 1188 view_index = GetIndexFromModelIndex(slot_index);
1194 } 1189 }
1195 1190
1196 // Decide the x or y offset for current item. 1191 // Decide the x or y offset for current item.
1197 int x_offset = 0; 1192 int x_offset = 0;
1198 int y_offset = 0; 1193 int y_offset = 0;
1199 1194
1200 if (pagination_controller_->scroll_axis() == 1195 if (pagination_controller_->scroll_axis() ==
1201 PaginationController::SCROLL_AXIS_HORIZONTAL) { 1196 PaginationController::SCROLL_AXIS_HORIZONTAL) {
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
1334 views::View::ConvertPointFromWidget(this, drag_point); 1329 views::View::ConvertPointFromWidget(this, drag_point);
1335 #else 1330 #else
1336 // For non-aura, root location is not clearly defined but |drag_view_| does 1331 // For non-aura, root location is not clearly defined but |drag_view_| does
1337 // not have the scale transform. So no round error would be introduced and 1332 // not have the scale transform. So no round error would be introduced and
1338 // it's okay to use View::ConvertPointToTarget. 1333 // it's okay to use View::ConvertPointToTarget.
1339 *drag_point = event.location(); 1334 *drag_point = event.location();
1340 views::View::ConvertPointToTarget(drag_view_, this, drag_point); 1335 views::View::ConvertPointToTarget(drag_view_, this, drag_point);
1341 #endif 1336 #endif
1342 } 1337 }
1343 1338
1344 void AppsGridView::CalculateDropTarget(const gfx::Point& drag_point, 1339 void AppsGridView::CalculateDropTarget() {
1345 bool use_page_button_hovering) { 1340 DCHECK(drag_view_);
1346 if (EnableFolderDragDropUI()) { 1341
1347 CalculateDropTargetWithFolderEnabled(drag_point, use_page_button_hovering); 1342 gfx::Point point = drag_view_->icon()->bounds().CenterPoint();
1343 views::View::ConvertPointToTarget(drag_view_, this, &point);
1344 if (!IsPointWithinDragBuffer(point)) {
1345 // Reset the reorder target to the original position if the cursor is
1346 // outside the drag buffer.
1347 if (IsDraggingForReparentInRootLevelGridView()) {
1348 drop_attempt_ = DROP_FOR_NONE;
1349 return;
1350 }
1351
1352 reorder_drop_target_ = drag_view_init_index_;
1353 drop_attempt_ = DROP_FOR_REORDER;
1354 return;
1355 }
1356
1357 if (EnableFolderDragDropUI() &&
1358 CalculateFolderDropTarget(point, &folder_drop_target_)) {
1359 drop_attempt_ = DROP_FOR_FOLDER;
1348 return; 1360 return;
1349 } 1361 }
1350 1362
1351 drop_attempt_ = DROP_FOR_REORDER; 1363 drop_attempt_ = DROP_FOR_REORDER;
1364 CalculateReorderDropTarget(point, &reorder_drop_target_);
1365 }
1352 1366
1353 int current_page = pagination_model_.selected_page(); 1367 bool AppsGridView::CalculateFolderDropTarget(const gfx::Point& point,
1354 gfx::Point point(drag_point); 1368 Index* drop_target) const {
1355 if (!IsPointWithinDragBuffer(drag_point)) { 1369 Index nearest_tile_index(GetNearestTileIndexForPoint(point));
1356 point = drag_start_grid_view_; 1370 int distance_to_tile_center =
1357 current_page = drag_start_page_; 1371 (point - GetExpectedTileBounds(nearest_tile_index.slot).CenterPoint())
1372 .Length();
1373 if (nearest_tile_index != reorder_placeholder_ &&
1374 distance_to_tile_center < kFolderDroppingCircleRadius &&
1375 !IsFolderItem(drag_view_->item()) &&
1376 CanDropIntoTarget(nearest_tile_index)) {
1377 *drop_target = nearest_tile_index;
1378 return true;
1358 } 1379 }
1359 1380
1360 if (use_page_button_hovering && page_switcher_view_ && 1381 return false;
1361 page_switcher_view_->bounds().Contains(point)) { 1382 }
1362 gfx::Point page_switcher_point(point); 1383
1363 views::View::ConvertPointToTarget(this, page_switcher_view_, 1384 void AppsGridView::CalculateReorderDropTarget(const gfx::Point& point,
1364 &page_switcher_point); 1385 Index* drop_target) const {
1365 int page = page_switcher_view_->GetPageForPoint(page_switcher_point); 1386 gfx::Rect bounds = GetContentsBounds();
1366 if (pagination_model_.is_valid_page(page)) { 1387 Index grid_index = GetNearestTileIndexForPoint(point);
1367 reorder_drop_target_.page = page; 1388 gfx::Point reorder_placeholder_center =
1368 reorder_drop_target_.slot = tiles_per_page() - 1; 1389 GetExpectedTileBounds(reorder_placeholder_.slot).CenterPoint();
1369 } 1390
1391 int x_offset_direction = 0;
1392 if (grid_index == reorder_placeholder_) {
1393 x_offset_direction = reorder_placeholder_center.x() < point.x() ? -1 : 1;
1370 } else { 1394 } else {
1371 gfx::Rect bounds(GetContentsBounds()); 1395 x_offset_direction = reorder_placeholder_ < grid_index ? -1 : 1;
1372 const int drop_row = (point.y() - bounds.y()) / kPreferredTileHeight;
1373 const int drop_col = std::min(cols_ - 1,
1374 (point.x() - bounds.x()) / kPreferredTileWidth);
1375
1376 reorder_drop_target_.page = current_page;
1377 reorder_drop_target_.slot = std::max(
1378 0, std::min(tiles_per_page() - 1, drop_row * cols_ + drop_col));
1379 } 1396 }
1380 1397
1381 // Limits to the last possible slot on last page. 1398 int row = grid_index.slot / cols_;
1382 if (reorder_drop_target_.page == pagination_model_.total_pages() - 1) { 1399
1383 reorder_drop_target_.slot = 1400 // Offset the target column based on the direction of the target. This will
1384 std::min((view_model_.view_size() - 1) % tiles_per_page(), 1401 // result in earlier targets getting their reorder zone shifted backwards
1385 reorder_drop_target_.slot); 1402 // and later targets getting their reorder zones shifted forwards.
1403 //
1404 // This makes eordering feel like the user is slotting items into the spaces
1405 // between apps.
1406 int x_offset = x_offset_direction *
1407 (kPreferredTileWidth - kFolderDroppingCircleRadius) / 2;
1408 int col = (point.x() - bounds.x() + x_offset) / kPreferredTileWidth;
1409 col = ClampToRange(col, 0, cols_ - 1);
1410 *drop_target =
1411 std::min(Index(pagination_model_.selected_page(), row * cols_ + col),
1412 GetLastViewIndex());
1413 }
1414
1415 void AppsGridView::OnReorderTimer() {
1416 if (drop_attempt_ == DROP_FOR_REORDER) {
1417 reorder_placeholder_ = reorder_drop_target_;
1418 AnimateToIdealBounds();
1386 } 1419 }
1387 } 1420 }
1388 1421
1389
1390 void AppsGridView::CalculateDropTargetWithFolderEnabled(
1391 const gfx::Point& drag_point,
1392 bool use_page_button_hovering) {
1393 gfx::Point point(drag_point);
1394 if (!IsPointWithinDragBuffer(drag_point)) {
1395 point = drag_start_grid_view_;
1396 }
1397
1398 if (use_page_button_hovering && page_switcher_view_ &&
1399 page_switcher_view_->bounds().Contains(point)) {
1400 gfx::Point page_switcher_point(point);
1401 views::View::ConvertPointToTarget(this, page_switcher_view_,
1402 &page_switcher_point);
1403 int page = page_switcher_view_->GetPageForPoint(page_switcher_point);
1404 if (pagination_model_.is_valid_page(page))
1405 drop_attempt_ = DROP_FOR_NONE;
1406 } else {
1407 DCHECK(drag_view_);
1408 // Try to find the nearest target for folder dropping or re-ordering.
1409 CalculateNearestTileForDragView();
1410 }
1411 }
1412
1413 void AppsGridView::OnReorderTimer() {
1414 if (drop_attempt_ == DROP_FOR_REORDER)
1415 AnimateToIdealBounds();
1416 }
1417
1418 void AppsGridView::OnFolderItemReparentTimer() { 1422 void AppsGridView::OnFolderItemReparentTimer() {
1419 DCHECK(folder_delegate_); 1423 DCHECK(folder_delegate_);
1420 if (drag_out_of_folder_container_ && drag_view_) { 1424 if (drag_out_of_folder_container_ && drag_view_) {
1421 folder_delegate_->ReparentItem(drag_view_, last_drag_point_); 1425 folder_delegate_->ReparentItem(drag_view_, last_drag_point_);
1422 1426
1423 // Set the flag in the folder's grid view. 1427 // Set the flag in the folder's grid view.
1424 dragging_for_reparent_item_ = true; 1428 dragging_for_reparent_item_ = true;
1425 1429
1426 // Do not observe any data change since it is going to be hidden. 1430 // Do not observe any data change since it is going to be hidden.
1427 item_list_->RemoveObserver(this); 1431 item_list_->RemoveObserver(this);
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
1506 void AppsGridView::EndDragFromReparentItemInRootLevel( 1510 void AppsGridView::EndDragFromReparentItemInRootLevel(
1507 bool events_forwarded_to_drag_drop_host, 1511 bool events_forwarded_to_drag_drop_host,
1508 bool cancel_drag) { 1512 bool cancel_drag) {
1509 // EndDrag was called before if |drag_view_| is NULL. 1513 // EndDrag was called before if |drag_view_| is NULL.
1510 if (!drag_view_) 1514 if (!drag_view_)
1511 return; 1515 return;
1512 1516
1513 DCHECK(IsDraggingForReparentInRootLevelGridView()); 1517 DCHECK(IsDraggingForReparentInRootLevelGridView());
1514 bool cancel_reparent = cancel_drag || drop_attempt_ == DROP_FOR_NONE; 1518 bool cancel_reparent = cancel_drag || drop_attempt_ == DROP_FOR_NONE;
1515 if (!events_forwarded_to_drag_drop_host && !cancel_reparent) { 1519 if (!events_forwarded_to_drag_drop_host && !cancel_reparent) {
1516 CalculateDropTarget(last_drag_point_, true); 1520 CalculateDropTarget();
1517 if (drop_attempt_ == DROP_FOR_REORDER && 1521 if (drop_attempt_ == DROP_FOR_REORDER &&
1518 IsValidIndex(reorder_drop_target_)) { 1522 IsValidIndex(reorder_drop_target_)) {
1519 ReparentItemForReorder(drag_view_, reorder_drop_target_); 1523 ReparentItemForReorder(drag_view_, reorder_drop_target_);
1520 } else if (drop_attempt_ == DROP_FOR_FOLDER && 1524 } else if (drop_attempt_ == DROP_FOR_FOLDER &&
1521 IsValidIndex(folder_drop_target_)) { 1525 IsValidIndex(folder_drop_target_)) {
1522 ReparentItemToAnotherFolder(drag_view_, folder_drop_target_); 1526 ReparentItemToAnotherFolder(drag_view_, folder_drop_target_);
1523 } 1527 }
1524 SetViewHidden(drag_view_, false /* show */, true /* no animate */); 1528 SetViewHidden(drag_view_, false /* show */, true /* no animate */);
1525 } 1529 }
1526 1530
(...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after
1992 1996
1993 UpdatePaging(); 1997 UpdatePaging();
1994 AnimateToIdealBounds(); 1998 AnimateToIdealBounds();
1995 } 1999 }
1996 2000
1997 void AppsGridView::TotalPagesChanged() { 2001 void AppsGridView::TotalPagesChanged() {
1998 } 2002 }
1999 2003
2000 void AppsGridView::SelectedPageChanged(int old_selected, int new_selected) { 2004 void AppsGridView::SelectedPageChanged(int old_selected, int new_selected) {
2001 if (dragging()) { 2005 if (dragging()) {
2002 CalculateDropTarget(last_drag_point_, true); 2006 CalculateDropTarget();
2003 Layout(); 2007 Layout();
2004 MaybeStartPageFlipTimer(last_drag_point_); 2008 MaybeStartPageFlipTimer(last_drag_point_);
2005 } else { 2009 } else {
2006 ClearSelectedView(selected_view_); 2010 ClearSelectedView(selected_view_);
2007 Layout(); 2011 Layout();
2008 } 2012 }
2009 } 2013 }
2010 2014
2011 void AppsGridView::TransitionStarted() { 2015 void AppsGridView::TransitionStarted() {
2012 CancelContextMenusOnCurrentPage(); 2016 CancelContextMenusOnCurrentPage();
(...skipping 22 matching lines...) Expand all
2035 view->layer()->SetOpacity(hide ? 0 : 1); 2039 view->layer()->SetOpacity(hide ? 0 : 1);
2036 } 2040 }
2037 2041
2038 void AppsGridView::OnImplicitAnimationsCompleted() { 2042 void AppsGridView::OnImplicitAnimationsCompleted() {
2039 if (layer()->opacity() == 0.0f) 2043 if (layer()->opacity() == 0.0f)
2040 SetVisible(false); 2044 SetVisible(false);
2041 } 2045 }
2042 2046
2043 bool AppsGridView::EnableFolderDragDropUI() { 2047 bool AppsGridView::EnableFolderDragDropUI() {
2044 // Enable drag and drop folder UI only if it is at the app list root level 2048 // Enable drag and drop folder UI only if it is at the app list root level
2045 // and the switch is on and the target folder can still accept new items. 2049 // and the switch is on.
2046 return model_->folders_enabled() && !folder_delegate_ && 2050 return model_->folders_enabled() && !folder_delegate_;
2047 CanDropIntoTarget(folder_drop_target_);
2048 } 2051 }
2049 2052
2050 bool AppsGridView::CanDropIntoTarget(const Index& drop_target) { 2053 bool AppsGridView::CanDropIntoTarget(const Index& drop_target) const {
2051 views::View* target_view = GetViewAtSlotOnCurrentPage(drop_target.slot); 2054 views::View* target_view = GetViewAtIndex(drop_target);
2052 if (!target_view) 2055 if (!target_view)
2053 return true; 2056 return false;
2054 2057
2055 AppListItem* target_item = 2058 AppListItem* target_item =
2056 static_cast<AppListItemView*>(target_view)->item(); 2059 static_cast<AppListItemView*>(target_view)->item();
2057 // Items can be dropped into non-folders (which have no children) or folders 2060 // Items can be dropped into non-folders (which have no children) or folders
2058 // that have fewer than the max allowed items. 2061 // that have fewer than the max allowed items.
2059 // OEM folder does not allow to drag/drop other items in it. 2062 // OEM folder does not allow to drag/drop other items in it.
2060 return target_item->ChildItemCount() < kMaxFolderItems && 2063 return target_item->ChildItemCount() < kMaxFolderItems &&
2061 !IsOEMFolderItem(target_item); 2064 !IsOEMFolderItem(target_item);
2062 } 2065 }
2063 2066
2064 // TODO(jennyz): Optimize the calculation for finding nearest tile. 2067 AppsGridView::Index AppsGridView::GetNearestTileIndexForPoint(
2065 void AppsGridView::CalculateNearestTileForDragView() { 2068 const gfx::Point& point) const {
2066 Index nearest_tile; 2069 gfx::Rect bounds = GetContentsBounds();
2067 nearest_tile.page = -1; 2070 int col =
2068 nearest_tile.slot = -1; 2071 ClampToRange((point.x() - bounds.x()) / kPreferredTileWidth, 0, cols_);
2069 int d_min = -1; 2072 int row = ClampToRange(
2070 2073 (point.y() - bounds.y()) / kPreferredTileHeight, 0, rows_per_page_);
2071 // Calculate the top left tile |drag_view| intersects. 2074 return Index(pagination_model_.selected_page(), row * cols_ + col);
2072 gfx::Point pt = drag_view_->bounds().origin();
2073 CalculateNearestTileForVertex(pt, &nearest_tile, &d_min);
2074
2075 // Calculate the top right tile |drag_view| intersects.
2076 pt = drag_view_->bounds().top_right();
2077 CalculateNearestTileForVertex(pt, &nearest_tile, &d_min);
2078
2079 // Calculate the bottom left tile |drag_view| intersects.
2080 pt = drag_view_->bounds().bottom_left();
2081 CalculateNearestTileForVertex(pt, &nearest_tile, &d_min);
2082
2083 // Calculate the bottom right tile |drag_view| intersects.
2084 pt = drag_view_->bounds().bottom_right();
2085 CalculateNearestTileForVertex(pt, &nearest_tile, &d_min);
2086
2087 const int d_folder_dropping =
2088 kFolderDroppingCircleRadius + kGridIconDimension / 2;
2089 const int d_reorder = kReorderDroppingCircleRadius + kGridIconDimension / 2;
2090
2091 // If user drags an item across pages to the last page, and targets it
2092 // to the last empty slot on it, push the last item for re-ordering.
2093 if (IsLastPossibleDropTarget(nearest_tile) && d_min < d_reorder) {
2094 drop_attempt_ = DROP_FOR_REORDER;
2095 nearest_tile.slot = nearest_tile.slot - 1;
2096 reorder_drop_target_ = nearest_tile;
2097 return;
2098 }
2099
2100 if (IsValidIndex(nearest_tile)) {
2101 if (d_min < d_folder_dropping) {
2102 views::View* target_view = GetViewAtSlotOnCurrentPage(nearest_tile.slot);
2103 if (target_view &&
2104 !IsFolderItem(static_cast<AppListItemView*>(drag_view_)->item())) {
2105 // If a non-folder item is dragged to the target slot with an item
2106 // sitting on it, attempt to drop the dragged item into the folder
2107 // containing the item on nearest_tile.
2108 drop_attempt_ = DROP_FOR_FOLDER;
2109 folder_drop_target_ = nearest_tile;
2110 return;
2111 } else {
2112 // If the target slot is blank, or the dragged item is a folder, attempt
2113 // to re-order.
2114 drop_attempt_ = DROP_FOR_REORDER;
2115 reorder_drop_target_ = nearest_tile;
2116 return;
2117 }
2118 } else if (d_min < d_reorder) {
2119 // Entering the re-order circle of the slot.
2120 drop_attempt_ = DROP_FOR_REORDER;
2121 reorder_drop_target_ = nearest_tile;
2122 return;
2123 }
2124 }
2125
2126 // If |drag_view| is not entering the re-order or folder dropping region of
2127 // any items, cancel any previous re-order or folder dropping timer.
2128 drop_attempt_ = DROP_FOR_NONE;
2129 reorder_timer_.Stop();
2130 folder_dropping_timer_.Stop();
2131
2132 // When dragging for reparent a folder item, it should go back to its parent
2133 // folder item if there is no drop target.
2134 if (IsDraggingForReparentInRootLevelGridView()) {
2135 DCHECK(activated_folder_item_view_);
2136 folder_drop_target_ = GetIndexOfView(activated_folder_item_view_);
2137 }
2138 }
2139
2140 void AppsGridView::CalculateNearestTileForVertex(const gfx::Point& vertex,
2141 Index* nearest_tile,
2142 int* d_min) {
2143 Index target_index;
2144 gfx::Rect target_bounds = GetTileBoundsForPoint(vertex, &target_index);
2145
2146 if (target_bounds.IsEmpty() || target_index == *nearest_tile)
2147 return;
2148
2149 // Do not count the tile, where drag_view_ used to sit on and is still moving
2150 // on top of it, in calculating nearest tile for drag_view_.
2151 views::View* target_view = GetViewAtSlotOnCurrentPage(target_index.slot);
2152 if (target_index == drag_view_init_index_ && !target_view &&
2153 !IsDraggingForReparentInRootLevelGridView()) {
2154 return;
2155 }
2156
2157 int d_center = GetDistanceBetweenRects(drag_view_->bounds(), target_bounds);
2158 if (*d_min < 0 || d_center < *d_min) {
2159 *d_min = d_center;
2160 *nearest_tile = target_index;
2161 }
2162 }
2163
2164 gfx::Rect AppsGridView::GetTileBoundsForPoint(const gfx::Point& point,
2165 Index *tile_index) {
2166 // Check if |point| is outside of contents bounds.
2167 gfx::Rect bounds(GetContentsBounds());
2168 if (!bounds.Contains(point))
2169 return gfx::Rect();
2170
2171 // Calculate which tile |point| is enclosed in.
2172 int x = point.x();
2173 int y = point.y();
2174 int col = (x - bounds.x()) / kPreferredTileWidth;
2175 int row = (y - bounds.y()) / kPreferredTileHeight;
2176 gfx::Rect tile_rect = GetExpectedTileBounds(row, col);
2177
2178 // Check if |point| is outside a valid item's tile.
2179 Index index(pagination_model_.selected_page(), row * cols_ + col);
2180 *tile_index = index;
2181 return tile_rect;
2182 } 2075 }
2183 2076
2184 gfx::Size AppsGridView::GetTileGridSize() const { 2077 gfx::Size AppsGridView::GetTileGridSize() const {
2185 gfx::Rect bounds = GetExpectedTileBounds(0, 0); 2078 gfx::Rect bounds = GetExpectedTileBounds(0, 0);
2186 bounds.Union(GetExpectedTileBounds(rows_per_page_ - 1, cols_ - 1)); 2079 bounds.Union(GetExpectedTileBounds(rows_per_page_ - 1, cols_ - 1));
2187 return bounds.size(); 2080 return bounds.size();
2188 } 2081 }
2189 2082
2083 gfx::Rect AppsGridView::GetExpectedTileBounds(int slot) const {
2084 return GetExpectedTileBounds(slot / cols_, slot % cols_);
2085 }
2086
2190 gfx::Rect AppsGridView::GetExpectedTileBounds(int row, int col) const { 2087 gfx::Rect AppsGridView::GetExpectedTileBounds(int row, int col) const {
2191 gfx::Rect bounds(GetContentsBounds()); 2088 gfx::Rect bounds(GetContentsBounds());
2192 gfx::Size tile_size(kPreferredTileWidth, kPreferredTileHeight); 2089 gfx::Size tile_size(kPreferredTileWidth, kPreferredTileHeight);
2193 return gfx::Rect(gfx::Point(bounds.x() + col * tile_size.width(), 2090 return gfx::Rect(gfx::Point(bounds.x() + col * tile_size.width(),
2194 bounds.y() + row * tile_size.height()), 2091 bounds.y() + row * tile_size.height()),
2195 tile_size); 2092 tile_size);
2196 } 2093 }
2197 2094
2198 bool AppsGridView::IsLastPossibleDropTarget(const Index& index) const {
2199 int last_possible_slot = view_model_.view_size() % tiles_per_page();
2200 return (index.page == pagination_model_.total_pages() - 1 &&
2201 index.slot == last_possible_slot + 1);
2202 }
2203
2204 views::View* AppsGridView::GetViewAtSlotOnCurrentPage(int slot) { 2095 views::View* AppsGridView::GetViewAtSlotOnCurrentPage(int slot) {
2205 if (slot < 0) 2096 if (slot < 0)
2206 return NULL; 2097 return NULL;
2207 2098
2208 // Calculate the original bound of the tile at |index|. 2099 // Calculate the original bound of the tile at |index|.
2209 int row = slot / cols_; 2100 int row = slot / cols_;
2210 int col = slot % cols_; 2101 int col = slot % cols_;
2211 gfx::Rect tile_rect = GetExpectedTileBounds(row, col); 2102 gfx::Rect tile_rect = GetExpectedTileBounds(row, col);
2212 2103
2213 for (int i = 0; i < view_model_.view_size(); ++i) { 2104 for (int i = 0; i < view_model_.view_size(); ++i) {
2214 views::View* view = view_model_.view_at(i); 2105 views::View* view = view_model_.view_at(i);
2215 if (view->bounds() == tile_rect && view != drag_view_) 2106 if (view->bounds() == tile_rect && view != drag_view_)
2216 return view; 2107 return view;
2217 } 2108 }
2218 return NULL; 2109 return NULL;
2219 } 2110 }
2220 2111
2221 void AppsGridView::SetAsFolderDroppingTarget(const Index& target_index, 2112 void AppsGridView::SetAsFolderDroppingTarget(const Index& target_index,
2222 bool is_target_folder) { 2113 bool is_target_folder) {
2223 AppListItemView* target_view = 2114 AppListItemView* target_view =
2224 static_cast<AppListItemView*>( 2115 static_cast<AppListItemView*>(
2225 GetViewAtSlotOnCurrentPage(target_index.slot)); 2116 GetViewAtSlotOnCurrentPage(target_index.slot));
2226 if (target_view) 2117 if (target_view)
2227 target_view->SetAsAttemptedFolderTarget(is_target_folder); 2118 target_view->SetAsAttemptedFolderTarget(is_target_folder);
2228 } 2119 }
2229 2120
2230 } // namespace app_list 2121 } // namespace app_list
OLDNEW
« no previous file with comments | « ui/app_list/views/apps_grid_view.h ('k') | ui/app_list/views/apps_grid_view_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698