| 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_model.h" | 8 #include "ui/app_list/app_list_model.h" |
| 9 #include "ui/app_list/app_list_model_observer.h" | 9 #include "ui/app_list/app_list_model_observer.h" |
| 10 #include "ui/app_list/app_list_view_delegate.h" | 10 #include "ui/app_list/app_list_view_delegate.h" |
| 11 #include "ui/app_list/cocoa/apps_grid_view_item.h" | 11 #include "ui/app_list/cocoa/apps_grid_view_item.h" |
| 12 #import "ui/app_list/cocoa/apps_pagination_model_observer.h" |
| 12 #include "ui/base/models/list_model_observer.h" | 13 #include "ui/base/models/list_model_observer.h" |
| 13 | 14 |
| 14 namespace { | 15 namespace { |
| 15 | 16 |
| 16 // OSX app list has hardcoded rows and columns for now. | 17 // OSX app list has hardcoded rows and columns for now. |
| 17 const int kFixedRows = 4; | 18 const int kFixedRows = 4; |
| 18 const int kFixedColumns = 4; | 19 const int kFixedColumns = 4; |
| 19 const int kItemsPerPage = kFixedRows * kFixedColumns; | 20 const int kItemsPerPage = kFixedRows * kFixedColumns; |
| 20 | 21 |
| 21 // Padding space in pixels for fixed layout. | 22 // Padding space in pixels for fixed layout. |
| 22 const CGFloat kLeftRightPadding = 20; | 23 const CGFloat kLeftRightPadding = 16; |
| 23 const CGFloat kTopPadding = 16; | 24 const CGFloat kTopPadding = 30; |
| 24 | 25 |
| 25 // Preferred tile size when showing in fixed layout. | 26 // Preferred tile size when showing in fixed layout. |
| 26 const CGFloat kPreferredTileWidth = 88; | 27 const CGFloat kPreferredTileWidth = 88; |
| 27 const CGFloat kPreferredTileHeight = 98; | 28 const CGFloat kPreferredTileHeight = 98; |
| 28 | 29 |
| 29 const CGFloat kViewWidth = | 30 const CGFloat kViewWidth = |
| 30 kFixedColumns * kPreferredTileWidth + 2 * kLeftRightPadding; | 31 kFixedColumns * kPreferredTileWidth + 2 * kLeftRightPadding; |
| 31 const CGFloat kViewHeight = kFixedRows * kPreferredTileHeight; | 32 const CGFloat kViewHeight = kFixedRows * kPreferredTileHeight; |
| 32 | 33 |
| 34 NSTimeInterval g_scroll_duration = 0.18; |
| 35 |
| 33 } // namespace | 36 } // namespace |
| 34 | 37 |
| 35 @interface AppsGridController () | 38 @interface AppsGridController () |
| 36 | 39 |
| 37 // Cancel a currently running scroll animation. | 40 // Cancel a currently running scroll animation. |
| 38 - (void)cancelScrollAnimation; | 41 - (void)cancelScrollAnimation; |
| 39 | 42 |
| 40 // Index of the page with the most content currently visible. | 43 // Index of the page with the most content currently visible. |
| 41 - (size_t)nearestPageIndex; | 44 - (size_t)nearestPageIndex; |
| 42 | 45 |
| 43 // Make an empty NSCollectionView positioned horizontally for |pageIndex|. | 46 // Make an empty NSCollectionView positioned horizontally for |pageIndex|. |
| 44 - (NSCollectionView*)makePageForIndex:(size_t)pageIndex; | 47 - (NSCollectionView*)makePageForIndex:(size_t)pageIndex; |
| 45 | 48 |
| 46 // Bootstrap the views this class controls. | 49 // Bootstrap the views this class controls. |
| 47 - (void)loadAndSetView; | 50 - (void)loadAndSetView; |
| 48 | 51 |
| 52 - (void)boundsDidChange:(NSNotification*)notification; |
| 53 |
| 49 // Action for buttons in the grid. | 54 // Action for buttons in the grid. |
| 50 - (void)onItemClicked:(id)sender; | 55 - (void)onItemClicked:(id)sender; |
| 51 | 56 |
| 52 - (AppsGridViewItem*)itemAtPageIndex:(size_t)pageIndex | 57 - (AppsGridViewItem*)itemAtPageIndex:(size_t)pageIndex |
| 53 indexInPage:(size_t)indexInPage; | 58 indexInPage:(size_t)indexInPage; |
| 54 | 59 |
| 55 // Update the model in full, and rebuild subviews. | 60 // Update the model in full, and rebuild subviews. |
| 56 - (void)modelUpdated; | 61 - (void)modelUpdated; |
| 57 | 62 |
| 58 // Return the button selected in first page with a selection. | 63 // Return the button selected in first page with a selection. |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 91 | 96 |
| 92 AppsGridController* parent_; // Weak, owns us. | 97 AppsGridController* parent_; // Weak, owns us. |
| 93 | 98 |
| 94 DISALLOW_COPY_AND_ASSIGN(AppsGridDelegateBridge); | 99 DISALLOW_COPY_AND_ASSIGN(AppsGridDelegateBridge); |
| 95 }; | 100 }; |
| 96 | 101 |
| 97 } // namespace app_list | 102 } // namespace app_list |
| 98 | 103 |
| 99 @implementation AppsGridController | 104 @implementation AppsGridController |
| 100 | 105 |
| 101 - (id)initWithViewDelegate: | 106 + (void)setScrollAnimationDuration:(NSTimeInterval)duration { |
| 102 (scoped_ptr<app_list::AppListViewDelegate>)appListViewDelegate { | 107 g_scroll_duration = duration; |
| 108 } |
| 109 |
| 110 @synthesize paginationObserver = paginationObserver_; |
| 111 |
| 112 - (id)init { |
| 103 if ((self = [super init])) { | 113 if ((self = [super init])) { |
| 104 scoped_ptr<app_list::AppListModel> model(new app_list::AppListModel); | |
| 105 delegate_.reset(appListViewDelegate.release()); | |
| 106 bridge_.reset(new app_list::AppsGridDelegateBridge(self)); | 114 bridge_.reset(new app_list::AppsGridDelegateBridge(self)); |
| 107 pages_.reset([[NSMutableArray alloc] init]); | 115 pages_.reset([[NSMutableArray alloc] init]); |
| 108 items_.reset([[NSMutableArray alloc] init]); | 116 items_.reset([[NSMutableArray alloc] init]); |
| 109 if (delegate_) | |
| 110 delegate_->SetModel(model.get()); | |
| 111 [self loadAndSetView]; | 117 [self loadAndSetView]; |
| 112 [self setModel:model.Pass()]; | 118 [self updatePages:0]; |
| 113 } | 119 } |
| 114 return self; | 120 return self; |
| 115 } | 121 } |
| 116 | 122 |
| 117 - (void)dealloc { | 123 - (void)dealloc { |
| 124 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 118 [self setModel:scoped_ptr<app_list::AppListModel>()]; | 125 [self setModel:scoped_ptr<app_list::AppListModel>()]; |
| 119 [super dealloc]; | 126 [super dealloc]; |
| 120 } | 127 } |
| 121 | 128 |
| 122 - (NSCollectionView*)collectionViewAtPageIndex:(size_t)pageIndex { | 129 - (NSCollectionView*)collectionViewAtPageIndex:(size_t)pageIndex { |
| 123 return [pages_ objectAtIndex:pageIndex]; | 130 return [pages_ objectAtIndex:pageIndex]; |
| 124 } | 131 } |
| 125 | 132 |
| 126 - (app_list::AppListModel*)model { | 133 - (app_list::AppListModel*)model { |
| 127 return model_.get(); | 134 return model_.get(); |
| 128 } | 135 } |
| 129 | 136 |
| 130 - (app_list::AppListViewDelegate*)delegate { | 137 - (void)setModel:(scoped_ptr<app_list::AppListModel>)newModel { |
| 131 return delegate_.get(); | |
| 132 } | |
| 133 | |
| 134 - (void)setModel:(scoped_ptr<app_list::AppListModel>)model { | |
| 135 if (model_) { | 138 if (model_) { |
| 136 model_->apps()->RemoveObserver(bridge_.get()); | 139 model_->apps()->RemoveObserver(bridge_.get()); |
| 137 | 140 |
| 138 // Since the model is about to be deleted, and the AppKit objects might be | 141 // Since the model is about to be deleted, and the AppKit objects might be |
| 139 // sitting in an NSAutoreleasePool, ensure there are no references to the | 142 // sitting in an NSAutoreleasePool, ensure there are no references to the |
| 140 // model. | 143 // model. |
| 141 for (size_t i = 0; i < [items_ count]; ++i) | 144 for (size_t i = 0; i < [items_ count]; ++i) |
| 142 [[self itemAtIndex:i] setModel:NULL]; | 145 [[self itemAtIndex:i] setModel:NULL]; |
| 143 } | 146 } |
| 144 | 147 |
| 145 model_.reset(model.release()); | 148 model_.reset(newModel.release()); |
| 146 if (model_) | 149 if (model_) |
| 147 model_->apps()->AddObserver(bridge_.get()); | 150 model_->apps()->AddObserver(bridge_.get()); |
| 148 | 151 |
| 149 [self modelUpdated]; | 152 [self modelUpdated]; |
| 150 } | 153 } |
| 151 | 154 |
| 155 - (void)setDelegate:(app_list::AppListViewDelegate*)newDelegate { |
| 156 scoped_ptr<app_list::AppListModel> newModel(new app_list::AppListModel); |
| 157 delegate_ = newDelegate; |
| 158 if (delegate_) |
| 159 delegate_->SetModel(newModel.get()); // Populates items. |
| 160 [self setModel:newModel.Pass()]; |
| 161 } |
| 162 |
| 163 - (size_t)visiblePage { |
| 164 return visiblePage_; |
| 165 } |
| 166 |
| 152 - (void)activateSelection { | 167 - (void)activateSelection { |
| 153 [[self selectedButton] performClick:self]; | 168 [[self selectedButton] performClick:self]; |
| 154 } | 169 } |
| 155 | 170 |
| 156 - (size_t)pageCount { | 171 - (size_t)pageCount { |
| 157 return [pages_ count]; | 172 return [pages_ count]; |
| 158 } | 173 } |
| 159 | 174 |
| 160 - (void)scrollToPage:(size_t)pageIndex { | 175 - (void)scrollToPage:(size_t)pageIndex { |
| 161 NSClipView* clipView = [[self gridScrollView] contentView]; | 176 NSClipView* clipView = [[self gridScrollView] contentView]; |
| 162 NSPoint newOrigin = [clipView bounds].origin; | 177 NSPoint newOrigin = [clipView bounds].origin; |
| 163 | 178 |
| 164 // Scrolling outside of this range is edge elasticity, which animates | 179 // Scrolling outside of this range is edge elasticity, which animates |
| 165 // automatically. | 180 // automatically. |
| 166 if ((pageIndex == 0 && (newOrigin.x <= 0)) || | 181 if ((pageIndex == 0 && (newOrigin.x <= 0)) || |
| 167 (pageIndex + 1 == [self pageCount] && | 182 (pageIndex + 1 == [self pageCount] && |
| 168 newOrigin.x >= pageIndex * kViewWidth)) { | 183 newOrigin.x >= pageIndex * kViewWidth)) { |
| 169 return; | 184 return; |
| 170 } | 185 } |
| 171 | 186 |
| 172 newOrigin.x = pageIndex * kViewWidth; | 187 newOrigin.x = pageIndex * kViewWidth; |
| 173 [NSAnimationContext beginGrouping]; | 188 [NSAnimationContext beginGrouping]; |
| 189 [[NSAnimationContext currentContext] setDuration:g_scroll_duration]; |
| 174 [[clipView animator] setBoundsOrigin:newOrigin]; | 190 [[clipView animator] setBoundsOrigin:newOrigin]; |
| 175 [NSAnimationContext endGrouping]; | 191 [NSAnimationContext endGrouping]; |
| 176 animatingScroll_ = YES; | 192 animatingScroll_ = YES; |
| 177 } | 193 } |
| 178 | 194 |
| 179 - (void)cancelScrollAnimation { | 195 - (void)cancelScrollAnimation { |
| 180 NSClipView* clipView = [[self gridScrollView] contentView]; | 196 NSClipView* clipView = [[self gridScrollView] contentView]; |
| 181 [NSAnimationContext beginGrouping]; | 197 [NSAnimationContext beginGrouping]; |
| 182 [[NSAnimationContext currentContext] setDuration:0]; | 198 [[NSAnimationContext currentContext] setDuration:0]; |
| 183 [[clipView animator] setBoundsOrigin:[clipView bounds].origin]; | 199 [[clipView animator] setBoundsOrigin:[clipView bounds].origin]; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 204 kLeftRightPadding + kViewWidth * pageIndex, 0, | 220 kLeftRightPadding + kViewWidth * pageIndex, 0, |
| 205 kViewWidth, kViewHeight); | 221 kViewWidth, kViewHeight); |
| 206 NSSize itemSize = NSMakeSize(kPreferredTileWidth, kPreferredTileHeight); | 222 NSSize itemSize = NSMakeSize(kPreferredTileWidth, kPreferredTileHeight); |
| 207 scoped_nsobject<NSCollectionView> itemCollectionView( | 223 scoped_nsobject<NSCollectionView> itemCollectionView( |
| 208 [[NSCollectionView alloc] initWithFrame:pageFrame]); | 224 [[NSCollectionView alloc] initWithFrame:pageFrame]); |
| 209 [itemCollectionView setMaxNumberOfRows:kFixedRows]; | 225 [itemCollectionView setMaxNumberOfRows:kFixedRows]; |
| 210 [itemCollectionView setMinItemSize:itemSize]; | 226 [itemCollectionView setMinItemSize:itemSize]; |
| 211 [itemCollectionView setMaxItemSize:itemSize]; | 227 [itemCollectionView setMaxItemSize:itemSize]; |
| 212 [itemCollectionView setSelectable:YES]; | 228 [itemCollectionView setSelectable:YES]; |
| 213 [itemCollectionView setFocusRingType:NSFocusRingTypeNone]; | 229 [itemCollectionView setFocusRingType:NSFocusRingTypeNone]; |
| 230 [itemCollectionView setBackgroundColors: |
| 231 [NSArray arrayWithObject:[NSColor clearColor]]]; |
| 214 | 232 |
| 215 scoped_nsobject<AppsGridViewItem> itemPrototype( | 233 scoped_nsobject<AppsGridViewItem> itemPrototype( |
| 216 [[AppsGridViewItem alloc] initWithSize:itemSize]); | 234 [[AppsGridViewItem alloc] initWithSize:itemSize]); |
| 217 [[itemPrototype button] setTarget:self]; | 235 [[itemPrototype button] setTarget:self]; |
| 218 [[itemPrototype button] setAction:@selector(onItemClicked:)]; | 236 [[itemPrototype button] setAction:@selector(onItemClicked:)]; |
| 219 | 237 |
| 220 [itemCollectionView setItemPrototype:itemPrototype]; | 238 [itemCollectionView setItemPrototype:itemPrototype]; |
| 221 return itemCollectionView.autorelease(); | 239 return itemCollectionView.autorelease(); |
| 222 } | 240 } |
| 223 | 241 |
| 224 - (void)loadAndSetView { | 242 - (void)loadAndSetView { |
| 225 scoped_nsobject<NSView> pagesContainer( | 243 scoped_nsobject<NSView> pagesContainer( |
| 226 [[NSView alloc] initWithFrame:NSZeroRect]); | 244 [[NSView alloc] initWithFrame:NSZeroRect]); |
| 227 | 245 |
| 228 NSRect scrollFrame = NSMakeRect(0, 0, kViewWidth, kViewHeight + kTopPadding); | 246 NSRect scrollFrame = NSMakeRect(0, 0, kViewWidth, kViewHeight + kTopPadding); |
| 229 scoped_nsobject<ScrollViewWithNoScrollbars> scrollView( | 247 scoped_nsobject<ScrollViewWithNoScrollbars> scrollView( |
| 230 [[ScrollViewWithNoScrollbars alloc] initWithFrame:scrollFrame]); | 248 [[ScrollViewWithNoScrollbars alloc] initWithFrame:scrollFrame]); |
| 231 [scrollView setBorderType:NSNoBorder]; | 249 [scrollView setBorderType:NSNoBorder]; |
| 232 [scrollView setLineScroll:kViewWidth]; | 250 [scrollView setLineScroll:kViewWidth]; |
| 233 [scrollView setPageScroll:kViewWidth]; | 251 [scrollView setPageScroll:kViewWidth]; |
| 234 [scrollView setDelegate:self]; | 252 [scrollView setDelegate:self]; |
| 235 [scrollView setDocumentView:pagesContainer]; | 253 [scrollView setDocumentView:pagesContainer]; |
| 254 [scrollView setDrawsBackground:NO]; |
| 255 |
| 256 [[NSNotificationCenter defaultCenter] |
| 257 addObserver:self |
| 258 selector:@selector(boundsDidChange:) |
| 259 name:NSViewBoundsDidChangeNotification |
| 260 object:[scrollView contentView]]; |
| 236 | 261 |
| 237 [self setView:scrollView]; | 262 [self setView:scrollView]; |
| 238 } | 263 } |
| 239 | 264 |
| 265 - (void)boundsDidChange:(NSNotification*)notification { |
| 266 if ([self nearestPageIndex] == visiblePage_) |
| 267 return; |
| 268 |
| 269 size_t oldPage = visiblePage_; |
| 270 visiblePage_ = [self nearestPageIndex]; |
| 271 |
| 272 // Clear any selection on the previous page (unless it has been removed). |
| 273 if (oldPage < [pages_ count]) { |
| 274 [[self collectionViewAtPageIndex:oldPage] |
| 275 setSelectionIndexes:[NSIndexSet indexSet]]; |
| 276 } |
| 277 [paginationObserver_ selectedPageChanged:oldPage |
| 278 newSelected:visiblePage_]; |
| 279 } |
| 280 |
| 240 - (void)onItemClicked:(id)sender { | 281 - (void)onItemClicked:(id)sender { |
| 241 for (size_t i = 0; i < [items_ count]; ++i) { | 282 for (size_t i = 0; i < [items_ count]; ++i) { |
| 242 AppsGridViewItem* item = [self itemAtIndex:i]; | 283 AppsGridViewItem* item = [self itemAtIndex:i]; |
| 243 if ([[item button] isEqual:sender]) | 284 if ([[item button] isEqual:sender]) |
| 244 delegate_->ActivateAppListItem([item model], 0); | 285 delegate_->ActivateAppListItem([item model], 0); |
| 245 } | 286 } |
| 246 } | 287 } |
| 247 | 288 |
| 248 - (AppsGridViewItem*)itemAtPageIndex:(size_t)pageIndex | 289 - (AppsGridViewItem*)itemAtPageIndex:(size_t)pageIndex |
| 249 indexInPage:(size_t)indexInPage { | 290 indexInPage:(size_t)indexInPage { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 306 currentPages - targetPages)]; | 347 currentPages - targetPages)]; |
| 307 } else { | 348 } else { |
| 308 // Pages need to be added. | 349 // Pages need to be added. |
| 309 for (size_t i = currentPages; i < targetPages; ++i) | 350 for (size_t i = currentPages; i < targetPages; ++i) |
| 310 [pages_ addObject:[self makePageForIndex:i]]; | 351 [pages_ addObject:[self makePageForIndex:i]]; |
| 311 } | 352 } |
| 312 | 353 |
| 313 [[self pagesContainerView] setSubviews:pages_]; | 354 [[self pagesContainerView] setSubviews:pages_]; |
| 314 NSSize pagesSize = NSMakeSize(kViewWidth * targetPages, kViewHeight); | 355 NSSize pagesSize = NSMakeSize(kViewWidth * targetPages, kViewHeight); |
| 315 [[self pagesContainerView] setFrameSize:pagesSize]; | 356 [[self pagesContainerView] setFrameSize:pagesSize]; |
| 357 [paginationObserver_ totalPagesChanged]; |
| 316 } | 358 } |
| 317 | 359 |
| 318 const size_t startPage = startItemIndex / kItemsPerPage; | 360 const size_t startPage = startItemIndex / kItemsPerPage; |
| 319 // All pages on or after |startPage| may need items added or removed. | 361 // All pages on or after |startPage| may need items added or removed. |
| 320 for (size_t pageIndex = startPage; pageIndex < targetPages; ++pageIndex) { | 362 for (size_t pageIndex = startPage; pageIndex < targetPages; ++pageIndex) { |
| 321 size_t startIndex = pageIndex * kItemsPerPage; | 363 size_t startIndex = pageIndex * kItemsPerPage; |
| 322 size_t length = kItemsPerPage; | 364 size_t length = kItemsPerPage; |
| 323 // Check if it's the last page, and it's not full. | 365 // Check if it's the last page, and it's not full. |
| 324 if (startIndex + length > [items_ count]) | 366 if (startIndex + length > [items_ count]) |
| 325 length = [items_ count] - startIndex; | 367 length = [items_ count] - startIndex; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 340 [items_ insertObject:[NSValue valueWithPointer:itemModel] | 382 [items_ insertObject:[NSValue valueWithPointer:itemModel] |
| 341 atIndex:i]; | 383 atIndex:i]; |
| 342 } | 384 } |
| 343 | 385 |
| 344 [self updatePages:start]; | 386 [self updatePages:start]; |
| 345 for (size_t i = start; i < start + count; ++i) | 387 for (size_t i = start; i < start + count; ++i) |
| 346 [[self itemAtIndex:i] setModel:model_->apps()->GetItemAt(i)]; | 388 [[self itemAtIndex:i] setModel:model_->apps()->GetItemAt(i)]; |
| 347 } | 389 } |
| 348 | 390 |
| 349 @end | 391 @end |
| OLD | NEW |