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

Side by Side Diff: ui/app_list/cocoa/apps_grid_controller.mm

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

Powered by Google App Engine
This is Rietveld 408576698