| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 #import "ui/app_list/cocoa/apps_collection_view_drag_manager.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "base/mac/foundation_util.h" | |
| 9 #import "ui/app_list/cocoa/apps_grid_controller.h" | |
| 10 #import "ui/app_list/cocoa/apps_grid_view_item.h" | |
| 11 #import "ui/app_list/cocoa/item_drag_controller.h" | |
| 12 | |
| 13 namespace { | |
| 14 | |
| 15 // Distance cursor must travel in either x or y direction to start a drag. | |
| 16 const CGFloat kDragThreshold = 5; | |
| 17 | |
| 18 } // namespace | |
| 19 | |
| 20 @interface AppsCollectionViewDragManager () | |
| 21 | |
| 22 // Returns the item index that |theEvent| would hit in the page at |pageIndex| | |
| 23 // or NSNotFound if no item is hit. | |
| 24 - (size_t)itemIndexForPage:(size_t)pageIndex | |
| 25 hitWithEvent:(NSEvent*)theEvent; | |
| 26 | |
| 27 - (void)initiateDrag:(NSEvent*)theEvent; | |
| 28 - (void)updateDrag:(NSEvent*)theEvent; | |
| 29 - (void)completeDrag; | |
| 30 | |
| 31 - (NSMenu*)menuForEvent:(NSEvent*)theEvent | |
| 32 inPage:(NSCollectionView*)page; | |
| 33 | |
| 34 @end | |
| 35 | |
| 36 // An NSCollectionView that forwards mouse events to the factory they share. | |
| 37 @interface GridCollectionView : NSCollectionView { | |
| 38 @private | |
| 39 AppsCollectionViewDragManager* factory_; | |
| 40 } | |
| 41 | |
| 42 @property(assign, nonatomic) AppsCollectionViewDragManager* factory; | |
| 43 | |
| 44 @end | |
| 45 | |
| 46 @implementation AppsCollectionViewDragManager | |
| 47 | |
| 48 - (id)initWithCellSize:(NSSize)cellSize | |
| 49 rows:(size_t)rows | |
| 50 columns:(size_t)columns | |
| 51 gridController:(AppsGridController*)gridController { | |
| 52 if ((self = [super init])) { | |
| 53 cellSize_ = cellSize; | |
| 54 rows_ = rows; | |
| 55 columns_ = columns; | |
| 56 gridController_ = gridController; | |
| 57 } | |
| 58 return self; | |
| 59 } | |
| 60 | |
| 61 - (NSCollectionView*)makePageWithFrame:(NSRect)pageFrame { | |
| 62 base::scoped_nsobject<GridCollectionView> itemCollectionView( | |
| 63 [[GridCollectionView alloc] initWithFrame:pageFrame]); | |
| 64 [itemCollectionView setFactory:self]; | |
| 65 [itemCollectionView setMaxNumberOfRows:rows_]; | |
| 66 [itemCollectionView setMinItemSize:cellSize_]; | |
| 67 [itemCollectionView setMaxItemSize:cellSize_]; | |
| 68 [itemCollectionView setSelectable:YES]; | |
| 69 [itemCollectionView setFocusRingType:NSFocusRingTypeNone]; | |
| 70 [itemCollectionView setBackgroundColors: | |
| 71 [NSArray arrayWithObject:[NSColor clearColor]]]; | |
| 72 [itemCollectionView setDelegate:gridController_]; | |
| 73 | |
| 74 base::scoped_nsobject<AppsGridViewItem> itemPrototype( | |
| 75 [[AppsGridViewItem alloc] initWithSize:cellSize_]); | |
| 76 [[itemPrototype button] setTarget:gridController_]; | |
| 77 [[itemPrototype button] setAction:@selector(onItemClicked:)]; | |
| 78 | |
| 79 [itemCollectionView setItemPrototype:itemPrototype]; | |
| 80 return itemCollectionView.autorelease(); | |
| 81 } | |
| 82 | |
| 83 - (void)onMouseDownInPage:(NSCollectionView*)page | |
| 84 withEvent:(NSEvent*)theEvent { | |
| 85 size_t pageIndex = [gridController_ pageIndexForCollectionView:page]; | |
| 86 itemHitIndex_ = [self itemIndexForPage:pageIndex | |
| 87 hitWithEvent:theEvent]; | |
| 88 if (itemHitIndex_ == NSNotFound) | |
| 89 return; | |
| 90 | |
| 91 mouseDownLocation_ = [theEvent locationInWindow]; | |
| 92 [[[gridController_ itemAtIndex:itemHitIndex_] view] mouseDown:theEvent]; | |
| 93 } | |
| 94 | |
| 95 - (void)onMouseDragged:(NSEvent*)theEvent { | |
| 96 if (itemHitIndex_ == NSNotFound) | |
| 97 return; | |
| 98 | |
| 99 if (dragging_) { | |
| 100 [self updateDrag:theEvent]; | |
| 101 return; | |
| 102 } | |
| 103 | |
| 104 NSPoint mouseLocation = [theEvent locationInWindow]; | |
| 105 CGFloat deltaX = mouseLocation.x - mouseDownLocation_.x; | |
| 106 CGFloat deltaY = mouseLocation.y - mouseDownLocation_.y; | |
| 107 if (deltaX * deltaX + deltaY * deltaY > kDragThreshold * kDragThreshold) { | |
| 108 [self initiateDrag:theEvent]; | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 [[[gridController_ itemAtIndex:itemHitIndex_] view] mouseDragged:theEvent]; | |
| 113 } | |
| 114 | |
| 115 - (void)onMouseUp:(NSEvent*)theEvent { | |
| 116 if (itemHitIndex_ == NSNotFound) | |
| 117 return; | |
| 118 | |
| 119 if (dragging_) { | |
| 120 [self completeDrag]; | |
| 121 return; | |
| 122 } | |
| 123 | |
| 124 [[[gridController_ itemAtIndex:itemHitIndex_] view] mouseUp:theEvent]; | |
| 125 } | |
| 126 | |
| 127 - (size_t)itemIndexForPage:(size_t)pageIndex | |
| 128 hitWithEvent:(NSEvent*)theEvent { | |
| 129 NSCollectionView* page = | |
| 130 [gridController_ collectionViewAtPageIndex:pageIndex]; | |
| 131 NSPoint pointInView = [page convertPoint:[theEvent locationInWindow] | |
| 132 fromView:nil]; | |
| 133 | |
| 134 const size_t itemsInPage = [[page content] count]; | |
| 135 for (size_t indexInPage = 0; indexInPage < itemsInPage; ++indexInPage) { | |
| 136 if ([page mouse:pointInView | |
| 137 inRect:[page frameForItemAtIndex:indexInPage]]) { | |
| 138 return indexInPage + pageIndex * rows_ * columns_; | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 return NSNotFound; | |
| 143 } | |
| 144 | |
| 145 - (void)initiateDrag:(NSEvent*)theEvent { | |
| 146 DCHECK_LT(itemHitIndex_, [gridController_ itemCount]); | |
| 147 dragging_ = YES; | |
| 148 | |
| 149 if (!itemDragController_) { | |
| 150 itemDragController_.reset( | |
| 151 [[ItemDragController alloc] initWithGridCellSize:cellSize_]); | |
| 152 [[[gridController_ view] superview] addSubview:[itemDragController_ view]]; | |
| 153 } | |
| 154 | |
| 155 [itemDragController_ initiate:[gridController_ itemAtIndex:itemHitIndex_] | |
| 156 mouseDownLocation:mouseDownLocation_ | |
| 157 currentLocation:[theEvent locationInWindow] | |
| 158 timestamp:[theEvent timestamp]]; | |
| 159 | |
| 160 itemDragIndex_ = itemHitIndex_; | |
| 161 [self updateDrag:theEvent]; | |
| 162 } | |
| 163 | |
| 164 - (void)updateDrag:(NSEvent*)theEvent { | |
| 165 [itemDragController_ update:[theEvent locationInWindow] | |
| 166 timestamp:[theEvent timestamp]]; | |
| 167 [gridController_ maybeChangePageForPoint:[theEvent locationInWindow]]; | |
| 168 | |
| 169 size_t visiblePage = [gridController_ visiblePage]; | |
| 170 size_t itemIndexOver = [self itemIndexForPage:visiblePage | |
| 171 hitWithEvent:theEvent]; | |
| 172 DCHECK_NE(0u, [gridController_ itemCount]); | |
| 173 if (itemIndexOver == NSNotFound) { | |
| 174 if (visiblePage != [gridController_ pageCount] - 1) | |
| 175 return; | |
| 176 | |
| 177 // If nothing was hit, but the last page is shown, move to the end. | |
| 178 itemIndexOver = [gridController_ itemCount] - 1; | |
| 179 } | |
| 180 | |
| 181 if (itemDragIndex_ == itemIndexOver) | |
| 182 return; | |
| 183 | |
| 184 [gridController_ moveItemInView:itemDragIndex_ | |
| 185 toItemIndex:itemIndexOver]; | |
| 186 // A new item may be created when moving between pages. Ensure it is hidden. | |
| 187 [[[gridController_ itemAtIndex:itemIndexOver] button] setHidden:YES]; | |
| 188 itemDragIndex_ = itemIndexOver; | |
| 189 } | |
| 190 | |
| 191 - (void)cancelDrag { | |
| 192 if (!dragging_) | |
| 193 return; | |
| 194 | |
| 195 [gridController_ moveItemInView:itemDragIndex_ | |
| 196 toItemIndex:itemHitIndex_]; | |
| 197 itemDragIndex_ = itemHitIndex_; | |
| 198 [self completeDrag]; | |
| 199 itemHitIndex_ = NSNotFound; // Ignore future mouse events for this drag. | |
| 200 } | |
| 201 | |
| 202 - (void)completeDrag { | |
| 203 DCHECK_GE(itemDragIndex_, 0u); | |
| 204 [gridController_ cancelScrollTimer]; | |
| 205 AppsGridViewItem* item = [gridController_ itemAtIndex:itemDragIndex_]; | |
| 206 | |
| 207 // The item could still be animating in the NSCollectionView, so ask it where | |
| 208 // it would place the item. | |
| 209 NSCollectionView* pageView = base::mac::ObjCCastStrict<NSCollectionView>( | |
| 210 [[item view] superview]); | |
| 211 size_t indexInPage = itemDragIndex_ % (rows_ * columns_); | |
| 212 NSPoint targetOrigin = [[[itemDragController_ view] superview] | |
| 213 convertPoint:[pageView frameForItemAtIndex:indexInPage].origin | |
| 214 fromView:pageView]; | |
| 215 | |
| 216 [itemDragController_ complete:item | |
| 217 targetOrigin:targetOrigin]; | |
| 218 [gridController_ moveItemWithIndex:itemHitIndex_ | |
| 219 toModelIndex:itemDragIndex_]; | |
| 220 | |
| 221 dragging_ = NO; | |
| 222 } | |
| 223 | |
| 224 - (NSMenu*)menuForEvent:(NSEvent*)theEvent | |
| 225 inPage:(NSCollectionView*)page { | |
| 226 size_t pageIndex = [gridController_ pageIndexForCollectionView:page]; | |
| 227 size_t itemIndex = [self itemIndexForPage:pageIndex | |
| 228 hitWithEvent:theEvent]; | |
| 229 if (itemIndex == NSNotFound) | |
| 230 return nil; | |
| 231 | |
| 232 return [[gridController_ itemAtIndex:itemIndex] contextMenu]; | |
| 233 } | |
| 234 | |
| 235 @end | |
| 236 | |
| 237 @implementation GridCollectionView | |
| 238 | |
| 239 @synthesize factory = factory_; | |
| 240 | |
| 241 - (NSMenu*)menuForEvent:(NSEvent*)theEvent { | |
| 242 return [factory_ menuForEvent:theEvent | |
| 243 inPage:self]; | |
| 244 } | |
| 245 | |
| 246 - (void)mouseDown:(NSEvent*)theEvent { | |
| 247 [factory_ onMouseDownInPage:self | |
| 248 withEvent:theEvent]; | |
| 249 } | |
| 250 | |
| 251 - (void)mouseDragged:(NSEvent*)theEvent { | |
| 252 [factory_ onMouseDragged:theEvent]; | |
| 253 } | |
| 254 | |
| 255 - (void)mouseUp:(NSEvent*)theEvent { | |
| 256 [factory_ onMouseUp:theEvent]; | |
| 257 } | |
| 258 | |
| 259 - (BOOL)acceptsFirstResponder { | |
| 260 return NO; | |
| 261 } | |
| 262 | |
| 263 @end | |
| OLD | NEW |