OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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 #import "ui/app_list/cocoa/apps_grid_controller.h" | 5 #import "ui/app_list/cocoa/apps_grid_controller.h" |
6 | 6 |
7 #include "base/mac/foundation_util.h" | 7 #include "base/mac/foundation_util.h" |
8 #include "ui/app_list/app_list_item.h" | 8 #include "ui/app_list/app_list_item.h" |
9 #include "ui/app_list/app_list_model.h" | 9 #include "ui/app_list/app_list_model.h" |
10 #include "ui/app_list/app_list_model_observer.h" | 10 #include "ui/app_list/app_list_model_observer.h" |
11 #include "ui/app_list/app_list_view_delegate.h" | 11 #include "ui/app_list/app_list_view_delegate.h" |
12 #import "ui/app_list/cocoa/apps_collection_view_drag_manager.h" | |
13 #import "ui/app_list/cocoa/apps_grid_view_item.h" | |
14 #import "ui/app_list/cocoa/apps_pagination_model_observer.h" | 12 #import "ui/app_list/cocoa/apps_pagination_model_observer.h" |
| 13 #include "ui/app_list/pagination_model.h" |
| 14 #include "ui/app_list/views/apps_grid_view.h" |
| 15 #include "ui/app_list/views/apps_grid_view_delegate.h" |
15 #include "ui/base/models/list_model_observer.h" | 16 #include "ui/base/models/list_model_observer.h" |
16 | 17 |
17 namespace { | 18 namespace { |
18 | 19 |
19 // OSX app list has hardcoded rows and columns for now. | 20 // OSX app list has hardcoded rows and columns for now. |
20 const int kFixedRows = 4; | 21 const int kFixedRows = 4; |
21 const int kFixedColumns = 4; | 22 const int kFixedColumns = 4; |
22 const int kItemsPerPage = kFixedRows * kFixedColumns; | 23 const CGFloat kIconSize = 48; |
23 | 24 |
24 // Padding space in pixels for fixed layout. | |
25 const CGFloat kGridTopPadding = 1; | |
26 const CGFloat kLeftRightPadding = 21; | |
27 const CGFloat kScrollerPadding = 16; | |
28 | |
29 // Preferred tile size when showing in fixed layout. These should be even | |
30 // numbers to ensure that if they are grown 50% they remain integers. | |
31 const CGFloat kPreferredTileWidth = 88; | |
32 const CGFloat kPreferredTileHeight = 98; | |
33 | |
34 const CGFloat kViewWidth = | |
35 kFixedColumns * kPreferredTileWidth + 2 * kLeftRightPadding; | |
36 const CGFloat kViewHeight = kFixedRows * kPreferredTileHeight; | |
37 | |
38 const NSTimeInterval kScrollWhileDraggingDelay = 1.0; | |
39 NSTimeInterval g_scroll_duration = 0.18; | 25 NSTimeInterval g_scroll_duration = 0.18; |
40 | 26 |
41 } // namespace | 27 } // namespace |
42 | 28 |
43 @interface AppsGridController () | |
44 | |
45 - (void)scrollToPageWithTimer:(size_t)targetPage; | |
46 - (void)onTimer:(NSTimer*)theTimer; | |
47 | |
48 // Cancel a currently running scroll animation. | |
49 - (void)cancelScrollAnimation; | |
50 | |
51 // Index of the page with the most content currently visible. | |
52 - (size_t)nearestPageIndex; | |
53 | |
54 // Bootstrap the views this class controls. | |
55 - (void)loadAndSetView; | |
56 | |
57 - (void)boundsDidChange:(NSNotification*)notification; | |
58 | |
59 // Action for buttons in the grid. | |
60 - (void)onItemClicked:(id)sender; | |
61 | |
62 - (AppsGridViewItem*)itemAtPageIndex:(size_t)pageIndex | |
63 indexInPage:(size_t)indexInPage; | |
64 | |
65 // Return the button of the selected item. | |
66 - (NSButton*)selectedButton; | |
67 | |
68 // The scroll view holding the grid pages. | |
69 - (NSScrollView*)gridScrollView; | |
70 | |
71 - (NSView*)pagesContainerView; | |
72 | |
73 // Create any new pages after updating |items_|. | |
74 - (void)updatePages:(size_t)startItemIndex; | |
75 | |
76 - (void)updatePageContent:(size_t)pageIndex | |
77 resetModel:(BOOL)resetModel; | |
78 | |
79 // Bridged methods for AppListItemListObserver. | |
80 - (void)listItemAdded:(size_t)index | |
81 item:(app_list::AppListItem*)item; | |
82 | |
83 - (void)listItemRemoved:(size_t)index; | |
84 | |
85 - (void)listItemMovedFromIndex:(size_t)fromIndex | |
86 toModelIndex:(size_t)toIndex; | |
87 | |
88 // Moves the selection by |indexDelta| items. | |
89 - (BOOL)moveSelectionByDelta:(int)indexDelta; | |
90 | |
91 // -[NSCollectionView frameForItemAtIndex:] misreports the frame origin of an | |
92 // item when the method is called during a scroll animation provided by the | |
93 // NSScrollView. This returns the correct value. | |
94 - (NSRect)trueFrameForItemAtIndex:(size_t)itemIndex; | |
95 | |
96 @end | |
97 | |
98 namespace app_list { | 29 namespace app_list { |
99 | 30 |
100 class AppsGridDelegateBridge : public AppListItemListObserver { | 31 class AppsGridViewDelegateBridge : public AppsGridViewDelegate { |
101 public: | 32 public: |
102 AppsGridDelegateBridge(AppsGridController* parent) : parent_(parent) {} | 33 AppsGridViewDelegateBridge(AppsGridController* parent) : parent_(parent) { |
| 34 (void)parent_; |
| 35 } |
| 36 virtual ~AppsGridViewDelegateBridge() {} |
| 37 |
| 38 virtual void ActivateApp(AppListItem* item, int event_flags) OVERRIDE { |
| 39 DLOG(INFO) << "Activate: " << item->GetDisplayName(); |
| 40 item->Activate(event_flags); |
| 41 } |
| 42 |
| 43 virtual void GetShortcutPathForApp( |
| 44 const std::string& app_id, |
| 45 const base::Callback<void(const base::FilePath&)>& callback) OVERRIDE {} |
103 | 46 |
104 private: | 47 private: |
105 // Overridden from AppListItemListObserver: | 48 AppsGridController* parent_; // Weak. Owns us. |
106 virtual void OnListItemAdded(size_t index, AppListItem* item) OVERRIDE { | |
107 [parent_ listItemAdded:index | |
108 item:item]; | |
109 } | |
110 virtual void OnListItemRemoved(size_t index, AppListItem* item) OVERRIDE { | |
111 [parent_ listItemRemoved:index]; | |
112 } | |
113 virtual void OnListItemMoved(size_t from_index, | |
114 size_t to_index, | |
115 AppListItem* item) OVERRIDE { | |
116 [parent_ listItemMovedFromIndex:from_index | |
117 toModelIndex:to_index]; | |
118 } | |
119 | |
120 AppsGridController* parent_; // Weak, owns us. | |
121 | |
122 DISALLOW_COPY_AND_ASSIGN(AppsGridDelegateBridge); | |
123 }; | 49 }; |
124 | 50 |
125 } // namespace app_list | 51 } // namespace app_list |
126 | 52 |
127 @interface PageContainerView : NSView; | |
128 @end | |
129 | |
130 // The container view needs to flip coordinates so that it is laid out | |
131 // correctly whether or not there is a horizontal scrollbar. | |
132 @implementation PageContainerView | |
133 | |
134 - (BOOL)isFlipped { | |
135 return YES; | |
136 } | |
137 | |
138 @end | |
139 | |
140 @implementation AppsGridController | 53 @implementation AppsGridController |
141 | 54 |
142 + (void)setScrollAnimationDuration:(NSTimeInterval)duration { | 55 + (void)setScrollAnimationDuration:(NSTimeInterval)duration { |
143 g_scroll_duration = duration; | 56 g_scroll_duration = duration; |
144 } | 57 } |
145 | 58 |
146 + (CGFloat)scrollerPadding { | 59 + (CGFloat)scrollerPadding { |
147 return kScrollerPadding; | 60 return 0; |
148 } | 61 } |
149 | 62 |
150 @synthesize paginationObserver = paginationObserver_; | 63 @synthesize paginationObserver = paginationObserver_; |
151 | 64 |
152 - (id)init { | 65 - (id)init { |
153 if ((self = [super init])) { | 66 if ((self = [super init])) { |
154 bridge_.reset(new app_list::AppsGridDelegateBridge(self)); | 67 gridDelegate_.reset(new app_list::AppsGridViewDelegateBridge(self)); |
155 NSSize cellSize = NSMakeSize(kPreferredTileWidth, kPreferredTileHeight); | 68 paginationModel_.reset(new app_list::PaginationModel()); |
156 dragManager_.reset( | 69 gridView_.reset(new app_list::AppsGridView(gridDelegate_.get(), |
157 [[AppsCollectionViewDragManager alloc] initWithCellSize:cellSize | 70 paginationModel_.get())); |
158 rows:kFixedRows | 71 gridView_->SetBounds(0, 0, 400, 440); |
159 columns:kFixedColumns | 72 [self setView:gridView_.GetNSView()]; |
160 gridController:self]); | |
161 pages_.reset([[NSMutableArray alloc] init]); | |
162 items_.reset([[NSMutableArray alloc] init]); | |
163 [self loadAndSetView]; | |
164 [self updatePages:0]; | |
165 } | 73 } |
166 return self; | 74 return self; |
167 } | 75 } |
168 | 76 |
169 - (void)dealloc { | |
170 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
171 [super dealloc]; | |
172 } | |
173 | |
174 - (NSCollectionView*)collectionViewAtPageIndex:(size_t)pageIndex { | 77 - (NSCollectionView*)collectionViewAtPageIndex:(size_t)pageIndex { |
175 return [pages_ objectAtIndex:pageIndex]; | 78 return nil; |
176 } | 79 } |
177 | 80 |
178 - (size_t)pageIndexForCollectionView:(NSCollectionView*)page { | 81 - (size_t)pageIndexForCollectionView:(NSCollectionView*)page { |
179 for (size_t pageIndex = 0; pageIndex < [pages_ count]; ++pageIndex) { | |
180 if (page == [self collectionViewAtPageIndex:pageIndex]) | |
181 return pageIndex; | |
182 } | |
183 return NSNotFound; | 82 return NSNotFound; |
184 } | 83 } |
185 | 84 |
186 - (app_list::AppListModel*)model { | 85 - (app_list::AppListModel*)model { |
187 return delegate_ ? delegate_->GetModel() : NULL; | 86 return delegate_ ? delegate_->GetModel() : NULL; |
188 } | 87 } |
189 | 88 |
190 - (void)setDelegate:(app_list::AppListViewDelegate*)newDelegate { | 89 - (void)setDelegate:(app_list::AppListViewDelegate*)newDelegate { |
191 if (delegate_) { | 90 NSView* parentView = [[self view] superview]; |
192 app_list::AppListModel* oldModel = delegate_->GetModel(); | 91 gridView_.reset(); |
193 if (oldModel) | |
194 oldModel->top_level_item_list()->RemoveObserver(bridge_.get()); | |
195 } | |
196 | |
197 // Since the old model may be getting deleted, and the AppKit objects might | |
198 // be sitting in an NSAutoreleasePool, ensure there are no references to | |
199 // the model. | |
200 for (size_t i = 0; i < [items_ count]; ++i) | |
201 [[self itemAtIndex:i] setModel:NULL]; | |
202 | |
203 [items_ removeAllObjects]; | |
204 [self updatePages:0]; | |
205 [self scrollToPage:0]; | |
206 | 92 |
207 delegate_ = newDelegate; | 93 delegate_ = newDelegate; |
208 if (!delegate_) | 94 if (!delegate_) |
209 return; | 95 return; |
210 | 96 |
211 app_list::AppListModel* newModel = delegate_->GetModel(); | 97 gridView_.reset(new app_list::AppsGridView(gridDelegate_.get(), |
212 if (!newModel) | 98 paginationModel_.get())); |
213 return; | 99 gridView_->SetLayout(kIconSize, kFixedColumns, kFixedRows); |
214 | 100 gridView_->SetModel([self model]); |
215 newModel->top_level_item_list()->AddObserver(bridge_.get()); | 101 gridView_->SetItemList([self model]->top_level_item_list()); |
216 for (size_t i = 0; i < newModel->top_level_item_list()->item_count(); ++i) { | 102 gridView_->SetBounds(0, 0, 400, 440); |
217 app_list::AppListItem* itemModel = | 103 gridView_->SetVisible(true); |
218 newModel->top_level_item_list()->item_at(i); | 104 [self setView:gridView_.GetNSView()]; |
219 [items_ insertObject:[NSValue valueWithPointer:itemModel] | 105 [parentView addSubview:[self view]]; |
220 atIndex:i]; | |
221 } | |
222 [self updatePages:0]; | |
223 } | 106 } |
224 | 107 |
225 - (size_t)visiblePage { | 108 - (size_t)visiblePage { |
226 return visiblePage_; | 109 return 0; |
227 } | 110 } |
228 | 111 |
229 - (void)activateSelection { | 112 - (void)activateSelection { |
230 [[self selectedButton] performClick:self]; | 113 NOTIMPLEMENTED(); |
231 } | 114 } |
232 | 115 |
233 - (size_t)pageCount { | 116 - (size_t)pageCount { |
234 return [pages_ count]; | 117 return 1; |
235 } | 118 } |
236 | 119 |
237 - (size_t)itemCount { | 120 - (size_t)itemCount { |
238 return [items_ count]; | 121 return 0; |
239 } | 122 } |
240 | 123 |
241 - (void)scrollToPage:(size_t)pageIndex { | 124 - (void)scrollToPage:(size_t)pageIndex { |
242 NSClipView* clipView = [[self gridScrollView] contentView]; | 125 NOTIMPLEMENTED(); |
243 NSPoint newOrigin = [clipView bounds].origin; | |
244 | |
245 // Scrolling outside of this range is edge elasticity, which animates | |
246 // automatically. | |
247 if ((pageIndex == 0 && (newOrigin.x <= 0)) || | |
248 (pageIndex + 1 == [self pageCount] && | |
249 newOrigin.x >= pageIndex * kViewWidth)) { | |
250 return; | |
251 } | |
252 | |
253 // Clear any selection on the current page (unless it has been removed). | |
254 if (visiblePage_ < [pages_ count]) { | |
255 [[self collectionViewAtPageIndex:visiblePage_] | |
256 setSelectionIndexes:[NSIndexSet indexSet]]; | |
257 } | |
258 | |
259 newOrigin.x = pageIndex * kViewWidth; | |
260 [NSAnimationContext beginGrouping]; | |
261 [[NSAnimationContext currentContext] setDuration:g_scroll_duration]; | |
262 [[clipView animator] setBoundsOrigin:newOrigin]; | |
263 [NSAnimationContext endGrouping]; | |
264 animatingScroll_ = YES; | |
265 targetScrollPage_ = pageIndex; | |
266 [self cancelScrollTimer]; | |
267 } | 126 } |
268 | 127 |
269 - (void)maybeChangePageForPoint:(NSPoint)locationInWindow { | 128 - (void)maybeChangePageForPoint:(NSPoint)locationInWindow { |
270 NSPoint pointInView = [[self view] convertPoint:locationInWindow | 129 NOTIMPLEMENTED(); |
271 fromView:nil]; | |
272 // Check if the point is outside the view on the left or right. | |
273 if (pointInView.x <= 0 || pointInView.x >= NSWidth([[self view] bounds])) { | |
274 size_t targetPage = visiblePage_; | |
275 if (pointInView.x <= 0) | |
276 targetPage -= targetPage != 0 ? 1 : 0; | |
277 else | |
278 targetPage += targetPage < [pages_ count] - 1 ? 1 : 0; | |
279 [self scrollToPageWithTimer:targetPage]; | |
280 return; | |
281 } | |
282 | |
283 if (paginationObserver_) { | |
284 NSInteger segment = | |
285 [paginationObserver_ pagerSegmentAtLocation:locationInWindow]; | |
286 if (segment >= 0 && static_cast<size_t>(segment) != targetScrollPage_) { | |
287 [self scrollToPageWithTimer:segment]; | |
288 return; | |
289 } | |
290 } | |
291 | |
292 // Otherwise the point may have moved back into the view. | |
293 [self cancelScrollTimer]; | |
294 } | 130 } |
295 | 131 |
296 - (void)cancelScrollTimer { | 132 - (void)cancelScrollTimer { |
297 scheduledScrollPage_ = targetScrollPage_; | 133 NOTIMPLEMENTED(); |
298 [scrollWhileDraggingTimer_ invalidate]; | |
299 } | 134 } |
300 | 135 |
301 - (void)scrollToPageWithTimer:(size_t)targetPage { | 136 - (void)scrollToPageWithTimer:(size_t)targetPage { |
302 if (targetPage == targetScrollPage_) { | 137 NOTIMPLEMENTED(); |
303 [self cancelScrollTimer]; | |
304 return; | |
305 } | |
306 | |
307 if (targetPage == scheduledScrollPage_) | |
308 return; | |
309 | |
310 scheduledScrollPage_ = targetPage; | |
311 [scrollWhileDraggingTimer_ invalidate]; | |
312 scrollWhileDraggingTimer_.reset( | |
313 [[NSTimer scheduledTimerWithTimeInterval:kScrollWhileDraggingDelay | |
314 target:self | |
315 selector:@selector(onTimer:) | |
316 userInfo:nil | |
317 repeats:NO] retain]); | |
318 } | |
319 | |
320 - (void)onTimer:(NSTimer*)theTimer { | |
321 if (scheduledScrollPage_ == targetScrollPage_) | |
322 return; // Already animating scroll. | |
323 | |
324 [self scrollToPage:scheduledScrollPage_]; | |
325 } | |
326 | |
327 - (void)cancelScrollAnimation { | |
328 NSClipView* clipView = [[self gridScrollView] contentView]; | |
329 [NSAnimationContext beginGrouping]; | |
330 [[NSAnimationContext currentContext] setDuration:0]; | |
331 [[clipView animator] setBoundsOrigin:[clipView bounds].origin]; | |
332 [NSAnimationContext endGrouping]; | |
333 animatingScroll_ = NO; | |
334 } | |
335 | |
336 - (size_t)nearestPageIndex { | |
337 return lround( | |
338 NSMinX([[[self gridScrollView] contentView] bounds]) / kViewWidth); | |
339 } | |
340 | |
341 - (void)userScrolling:(BOOL)isScrolling { | |
342 if (isScrolling) { | |
343 if (animatingScroll_) | |
344 [self cancelScrollAnimation]; | |
345 } else { | |
346 [self scrollToPage:[self nearestPageIndex]]; | |
347 } | |
348 } | |
349 | |
350 - (void)loadAndSetView { | |
351 base::scoped_nsobject<PageContainerView> pagesContainer( | |
352 [[PageContainerView alloc] initWithFrame:NSZeroRect]); | |
353 | |
354 NSRect scrollFrame = NSMakeRect(0, kGridTopPadding, kViewWidth, | |
355 kViewHeight + kScrollerPadding); | |
356 base::scoped_nsobject<ScrollViewWithNoScrollbars> scrollView( | |
357 [[ScrollViewWithNoScrollbars alloc] initWithFrame:scrollFrame]); | |
358 [scrollView setBorderType:NSNoBorder]; | |
359 [scrollView setLineScroll:kViewWidth]; | |
360 [scrollView setPageScroll:kViewWidth]; | |
361 [scrollView setDelegate:self]; | |
362 [scrollView setDocumentView:pagesContainer]; | |
363 [scrollView setDrawsBackground:NO]; | |
364 | |
365 [[NSNotificationCenter defaultCenter] | |
366 addObserver:self | |
367 selector:@selector(boundsDidChange:) | |
368 name:NSViewBoundsDidChangeNotification | |
369 object:[scrollView contentView]]; | |
370 | |
371 [self setView:scrollView]; | |
372 } | |
373 | |
374 - (void)boundsDidChange:(NSNotification*)notification { | |
375 size_t newPage = [self nearestPageIndex]; | |
376 if (newPage == visiblePage_) { | |
377 [paginationObserver_ pageVisibilityChanged]; | |
378 return; | |
379 } | |
380 | |
381 visiblePage_ = newPage; | |
382 [paginationObserver_ selectedPageChanged:newPage]; | |
383 [paginationObserver_ pageVisibilityChanged]; | |
384 } | |
385 | |
386 - (void)onItemClicked:(id)sender { | |
387 for (size_t i = 0; i < [items_ count]; ++i) { | |
388 AppsGridViewItem* gridItem = [self itemAtIndex:i]; | |
389 if ([[gridItem button] isEqual:sender]) | |
390 [gridItem model]->Activate(0); | |
391 } | |
392 } | |
393 | |
394 - (AppsGridViewItem*)itemAtPageIndex:(size_t)pageIndex | |
395 indexInPage:(size_t)indexInPage { | |
396 return base::mac::ObjCCastStrict<AppsGridViewItem>( | |
397 [[self collectionViewAtPageIndex:pageIndex] itemAtIndex:indexInPage]); | |
398 } | |
399 | |
400 - (AppsGridViewItem*)itemAtIndex:(size_t)itemIndex { | |
401 const size_t pageIndex = itemIndex / kItemsPerPage; | |
402 return [self itemAtPageIndex:pageIndex | |
403 indexInPage:itemIndex - pageIndex * kItemsPerPage]; | |
404 } | 138 } |
405 | 139 |
406 - (NSUInteger)selectedItemIndex { | 140 - (NSUInteger)selectedItemIndex { |
407 NSCollectionView* page = [self collectionViewAtPageIndex:visiblePage_]; | 141 return NSNotFound; |
408 NSUInteger indexOnPage = [[page selectionIndexes] firstIndex]; | |
409 if (indexOnPage == NSNotFound) | |
410 return NSNotFound; | |
411 | |
412 return indexOnPage + visiblePage_ * kItemsPerPage; | |
413 } | |
414 | |
415 - (NSButton*)selectedButton { | |
416 NSUInteger index = [self selectedItemIndex]; | |
417 if (index == NSNotFound) | |
418 return nil; | |
419 | |
420 return [[self itemAtIndex:index] button]; | |
421 } | |
422 | |
423 - (NSScrollView*)gridScrollView { | |
424 return base::mac::ObjCCastStrict<NSScrollView>([self view]); | |
425 } | |
426 | |
427 - (NSView*)pagesContainerView { | |
428 return [[self gridScrollView] documentView]; | |
429 } | |
430 | |
431 - (void)updatePages:(size_t)startItemIndex { | |
432 // Note there is always at least one page. | |
433 size_t targetPages = 1; | |
434 if ([items_ count] != 0) | |
435 targetPages = ([items_ count] - 1) / kItemsPerPage + 1; | |
436 | |
437 const size_t currentPages = [self pageCount]; | |
438 // First see if the number of pages have changed. | |
439 if (targetPages != currentPages) { | |
440 if (targetPages < currentPages) { | |
441 // Pages need to be removed. | |
442 [pages_ removeObjectsInRange:NSMakeRange(targetPages, | |
443 currentPages - targetPages)]; | |
444 } else { | |
445 // Pages need to be added. | |
446 for (size_t i = currentPages; i < targetPages; ++i) { | |
447 NSRect pageFrame = NSMakeRect( | |
448 kLeftRightPadding + kViewWidth * i, 0, | |
449 kViewWidth, kViewHeight); | |
450 [pages_ addObject:[dragManager_ makePageWithFrame:pageFrame]]; | |
451 } | |
452 } | |
453 | |
454 [[self pagesContainerView] setSubviews:pages_]; | |
455 NSSize pagesSize = NSMakeSize(kViewWidth * targetPages, kViewHeight); | |
456 [[self pagesContainerView] setFrameSize:pagesSize]; | |
457 [paginationObserver_ totalPagesChanged]; | |
458 } | |
459 | |
460 const size_t startPage = startItemIndex / kItemsPerPage; | |
461 // All pages on or after |startPage| may need items added or removed. | |
462 for (size_t pageIndex = startPage; pageIndex < targetPages; ++pageIndex) { | |
463 [self updatePageContent:pageIndex | |
464 resetModel:YES]; | |
465 } | |
466 } | |
467 | |
468 - (void)updatePageContent:(size_t)pageIndex | |
469 resetModel:(BOOL)resetModel { | |
470 NSCollectionView* pageView = [self collectionViewAtPageIndex:pageIndex]; | |
471 if (resetModel) { | |
472 // Clear the models first, otherwise removed items could be autoreleased at | |
473 // an unknown point in the future, when the model owner may have gone away. | |
474 for (size_t i = 0; i < [[pageView content] count]; ++i) { | |
475 AppsGridViewItem* gridItem = base::mac::ObjCCastStrict<AppsGridViewItem>( | |
476 [pageView itemAtIndex:i]); | |
477 [gridItem setModel:NULL]; | |
478 } | |
479 } | |
480 | |
481 NSRange inPageRange = NSIntersectionRange( | |
482 NSMakeRange(pageIndex * kItemsPerPage, kItemsPerPage), | |
483 NSMakeRange(0, [items_ count])); | |
484 NSArray* pageContent = [items_ subarrayWithRange:inPageRange]; | |
485 [pageView setContent:pageContent]; | |
486 if (!resetModel) | |
487 return; | |
488 | |
489 for (size_t i = 0; i < [pageContent count]; ++i) { | |
490 AppsGridViewItem* gridItem = base::mac::ObjCCastStrict<AppsGridViewItem>( | |
491 [pageView itemAtIndex:i]); | |
492 [gridItem setModel:static_cast<app_list::AppListItem*>( | |
493 [[pageContent objectAtIndex:i] pointerValue])]; | |
494 [gridItem setInitialFrameRect:[self trueFrameForItemAtIndex:i]]; | |
495 } | |
496 } | 142 } |
497 | 143 |
498 - (void)moveItemInView:(size_t)fromIndex | 144 - (void)moveItemInView:(size_t)fromIndex |
499 toItemIndex:(size_t)toIndex { | 145 toItemIndex:(size_t)toIndex { |
500 base::scoped_nsobject<NSValue> item( | 146 NOTIMPLEMENTED(); |
501 [[items_ objectAtIndex:fromIndex] retain]); | |
502 [items_ removeObjectAtIndex:fromIndex]; | |
503 [items_ insertObject:item | |
504 atIndex:toIndex]; | |
505 | |
506 size_t fromPageIndex = fromIndex / kItemsPerPage; | |
507 size_t toPageIndex = toIndex / kItemsPerPage; | |
508 if (fromPageIndex == toPageIndex) { | |
509 [self updatePageContent:fromPageIndex | |
510 resetModel:NO]; // Just reorder items. | |
511 return; | |
512 } | |
513 | |
514 if (fromPageIndex > toPageIndex) | |
515 std::swap(fromPageIndex, toPageIndex); | |
516 | |
517 for (size_t i = fromPageIndex; i <= toPageIndex; ++i) { | |
518 [self updatePageContent:i | |
519 resetModel:YES]; | |
520 } | |
521 } | 147 } |
522 | 148 |
523 // Compare with views implementation in AppsGridView::MoveItemInModel(). | |
524 - (void)moveItemWithIndex:(size_t)itemIndex | 149 - (void)moveItemWithIndex:(size_t)itemIndex |
525 toModelIndex:(size_t)modelIndex { | 150 toModelIndex:(size_t)modelIndex { |
526 // Ingore no-op moves. Note that this is always the case when canceled. | 151 NOTIMPLEMENTED(); |
527 if (itemIndex == modelIndex) | |
528 return; | |
529 | |
530 app_list::AppListItemList* itemList = [self model]->top_level_item_list(); | |
531 itemList->RemoveObserver(bridge_.get()); | |
532 itemList->MoveItem(itemIndex, modelIndex); | |
533 itemList->AddObserver(bridge_.get()); | |
534 } | 152 } |
535 | 153 |
536 - (AppsCollectionViewDragManager*)dragManager { | 154 - (void)selectItemAtIndex:(NSUInteger)index { |
537 return dragManager_; | 155 NOTIMPLEMENTED(); |
538 } | 156 } |
539 | 157 |
540 - (size_t)scheduledScrollPage { | 158 - (BOOL)handleCommandBySelector:(SEL)command { |
541 return scheduledScrollPage_; | 159 NOTIMPLEMENTED(); |
542 } | 160 return TRUE; |
543 | |
544 - (void)listItemAdded:(size_t)index | |
545 item:(app_list::AppListItem*)itemModel { | |
546 // Cancel any drag, to ensure the model stays consistent. | |
547 [dragManager_ cancelDrag]; | |
548 | |
549 [items_ insertObject:[NSValue valueWithPointer:itemModel] | |
550 atIndex:index]; | |
551 | |
552 [self updatePages:index]; | |
553 } | |
554 | |
555 - (void)listItemRemoved:(size_t)index { | |
556 [dragManager_ cancelDrag]; | |
557 | |
558 // Clear the models explicitly to avoid surprises from autorelease. | |
559 [[self itemAtIndex:index] setModel:NULL]; | |
560 | |
561 [items_ removeObjectsInRange:NSMakeRange(index, 1)]; | |
562 [self updatePages:index]; | |
563 } | |
564 | |
565 - (void)listItemMovedFromIndex:(size_t)fromIndex | |
566 toModelIndex:(size_t)toIndex { | |
567 [dragManager_ cancelDrag]; | |
568 [self moveItemInView:fromIndex | |
569 toItemIndex:toIndex]; | |
570 } | 161 } |
571 | 162 |
572 - (CGFloat)visiblePortionOfPage:(int)page { | 163 - (CGFloat)visiblePortionOfPage:(int)page { |
573 CGFloat scrollOffsetOfPage = | 164 return 0.5; |
574 NSMinX([[[self gridScrollView] contentView] bounds]) / kViewWidth - page; | |
575 if (scrollOffsetOfPage <= -1.0 || scrollOffsetOfPage >= 1.0) | |
576 return 0.0; | |
577 | |
578 if (scrollOffsetOfPage <= 0.0) | |
579 return scrollOffsetOfPage + 1.0; | |
580 | |
581 return -1.0 + scrollOffsetOfPage; | |
582 } | 165 } |
583 | 166 |
584 - (void)onPagerClicked:(AppListPagerView*)sender { | 167 - (void)onPagerClicked:(AppListPagerView*)sender { |
585 int selectedSegment = [sender selectedSegment]; | 168 NOTIMPLEMENTED(); |
586 if (selectedSegment < 0) | |
587 return; // No selection. | |
588 | |
589 int pageIndex = [[sender cell] tagForSegment:selectedSegment]; | |
590 if (pageIndex >= 0) | |
591 [self scrollToPage:pageIndex]; | |
592 } | |
593 | |
594 - (BOOL)moveSelectionByDelta:(int)indexDelta { | |
595 if (indexDelta == 0) | |
596 return NO; | |
597 | |
598 NSUInteger oldIndex = [self selectedItemIndex]; | |
599 | |
600 // If nothing is currently selected, select the first item on the page. | |
601 if (oldIndex == NSNotFound) { | |
602 [self selectItemAtIndex:visiblePage_ * kItemsPerPage]; | |
603 return YES; | |
604 } | |
605 | |
606 // Can't select a negative index. | |
607 if (indexDelta < 0 && static_cast<NSUInteger>(-indexDelta) > oldIndex) | |
608 return NO; | |
609 | |
610 // Can't select an index greater or equal to the number of items. | |
611 if (oldIndex + indexDelta >= [items_ count]) { | |
612 if (visiblePage_ == [pages_ count] - 1) | |
613 return NO; | |
614 | |
615 // If we're not on the last page, then select the last item. | |
616 [self selectItemAtIndex:[items_ count] - 1]; | |
617 return YES; | |
618 } | |
619 | |
620 [self selectItemAtIndex:oldIndex + indexDelta]; | |
621 return YES; | |
622 } | |
623 | |
624 - (NSRect)trueFrameForItemAtIndex:(size_t)itemIndex { | |
625 size_t column = itemIndex % kFixedColumns; | |
626 size_t row = itemIndex % kItemsPerPage / kFixedColumns; | |
627 return NSMakeRect(column * kPreferredTileWidth, | |
628 row * kPreferredTileHeight, | |
629 kPreferredTileWidth, | |
630 kPreferredTileHeight); | |
631 } | |
632 | |
633 - (void)selectItemAtIndex:(NSUInteger)index { | |
634 if (index >= [items_ count]) | |
635 return; | |
636 | |
637 if (index / kItemsPerPage != visiblePage_) | |
638 [self scrollToPage:index / kItemsPerPage]; | |
639 | |
640 [[self itemAtIndex:index] setSelected:YES]; | |
641 } | |
642 | |
643 - (BOOL)handleCommandBySelector:(SEL)command { | |
644 if (command == @selector(insertNewline:) || | |
645 command == @selector(insertLineBreak:)) { | |
646 [self activateSelection]; | |
647 return YES; | |
648 } | |
649 | |
650 NSUInteger oldIndex = [self selectedItemIndex]; | |
651 // If nothing is currently selected, select the first item on the page. | |
652 if (oldIndex == NSNotFound) { | |
653 [self selectItemAtIndex:visiblePage_ * kItemsPerPage]; | |
654 return YES; | |
655 } | |
656 | |
657 if (command == @selector(moveLeft:)) { | |
658 return oldIndex % kFixedColumns == 0 ? | |
659 [self moveSelectionByDelta:-kItemsPerPage + kFixedColumns - 1] : | |
660 [self moveSelectionByDelta:-1]; | |
661 } | |
662 | |
663 if (command == @selector(moveRight:)) { | |
664 return oldIndex % kFixedColumns == kFixedColumns - 1 ? | |
665 [self moveSelectionByDelta:+kItemsPerPage - kFixedColumns + 1] : | |
666 [self moveSelectionByDelta:1]; | |
667 } | |
668 | |
669 if (command == @selector(moveUp:)) { | |
670 return oldIndex / kFixedColumns % kFixedRows == 0 ? | |
671 NO : [self moveSelectionByDelta:-kFixedColumns]; | |
672 } | |
673 | |
674 if (command == @selector(moveDown:)) { | |
675 return oldIndex / kFixedColumns % kFixedRows == kFixedRows - 1 ? | |
676 NO : [self moveSelectionByDelta:kFixedColumns]; | |
677 } | |
678 | |
679 if (command == @selector(pageUp:) || | |
680 command == @selector(scrollPageUp:)) | |
681 return [self moveSelectionByDelta:-kItemsPerPage]; | |
682 | |
683 if (command == @selector(pageDown:) || | |
684 command == @selector(scrollPageDown:)) | |
685 return [self moveSelectionByDelta:kItemsPerPage]; | |
686 | |
687 return NO; | |
688 } | 169 } |
689 | 170 |
690 @end | 171 @end |
OLD | NEW |