Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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_view_item.h" |
| 6 | |
| 7 | #include "base/mac/foundation_util.h" |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 8 | #include "base/mac/mac_util.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 9 | #include "base/mac/scoped_nsobject.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 10 | #include "base/strings/sys_string_conversions.h" |
| 11 | #include "skia/ext/skia_utils_mac.h" |
| 12 | #include "ui/app_list/app_list_constants.h" |
| 13 | #include "ui/app_list/app_list_item_model.h" |
| 14 | #include "ui/app_list/app_list_item_model_observer.h" |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 15 | #import "ui/app_list/cocoa/apps_grid_controller.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 16 | #import "ui/base/cocoa/menu_controller.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 17 | #include "ui/base/resource/resource_bundle.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 18 | #include "ui/gfx/font.h" |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 19 | #include "ui/gfx/image/image_skia_operations.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 20 | #include "ui/gfx/image/image_skia_util_mac.h" |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 21 | #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 22 | |
| 23 | namespace { |
| 24 | |
| 25 | // Padding from the top of the tile to the top of the app icon. |
| 26 | const CGFloat kTileTopPadding = 10; |
| 27 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 28 | const CGFloat kIconSize = 48; |
| 29 | |
| 30 | const CGFloat kProgressBarHorizontalPadding = 8; |
| 31 | const CGFloat kProgressBarVerticalPadding = 13; |
| 32 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 33 | // On Mac, fonts of the same enum from ResourceBundle are larger. The smallest |
| 34 | // enum is already used, so it needs to be reduced further to match Windows. |
| 35 | const int kMacFontSizeDelta = -1; |
| 36 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 37 | } // namespace |
| 38 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 39 | @class AppsGridItemBackgroundView; |
| 40 | |
| 41 | @interface AppsGridViewItem () |
| 42 | |
| 43 | // Typed accessor for the root view. |
| 44 | - (AppsGridItemBackgroundView*)itemBackgroundView; |
| 45 | |
| 46 | // Bridged methods from app_list::AppListItemModelObserver: |
| 47 | // Update the title, correctly setting the color if the button is highlighted. |
| 48 | - (void)updateButtonTitle; |
| 49 | |
| 50 | // Update the button image after ensuring its dimensions are |kIconSize|. |
| 51 | - (void)updateButtonImage; |
| 52 | |
| 53 | // Ensure the page this item is on is the visible page in the grid. |
| 54 | - (void)ensureVisible; |
| 55 | |
| 56 | // Add or remove a progress bar from the view. |
| 57 | - (void)setItemIsInstalling:(BOOL)isInstalling; |
| 58 | |
| 59 | // Update the progress bar to represent |percent|, or make it indeterminate if |
| 60 | // |percent| is -1, when unpacking begins. |
| 61 | - (void)setPercentDownloaded:(int)percent; |
| 62 | |
| 63 | @end |
| 64 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 65 | namespace app_list { |
| 66 | |
| 67 | class ItemModelObserverBridge : public app_list::AppListItemModelObserver { |
| 68 | public: |
| 69 | ItemModelObserverBridge(AppsGridViewItem* parent, AppListItemModel* model); |
| 70 | virtual ~ItemModelObserverBridge(); |
| 71 | |
| 72 | AppListItemModel* model() { return model_; } |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 73 | NSMenu* GetContextMenu(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 74 | |
| 75 | virtual void ItemIconChanged() OVERRIDE; |
| 76 | virtual void ItemTitleChanged() OVERRIDE; |
| 77 | virtual void ItemHighlightedChanged() OVERRIDE; |
| 78 | virtual void ItemIsInstallingChanged() OVERRIDE; |
| 79 | virtual void ItemPercentDownloadedChanged() OVERRIDE; |
| 80 | |
| 81 | private: |
| 82 | AppsGridViewItem* parent_; // Weak. Owns us. |
| 83 | AppListItemModel* model_; // Weak. Owned by AppListModel::Apps. |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 84 | base::scoped_nsobject<MenuController> context_menu_controller_; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 85 | |
| 86 | DISALLOW_COPY_AND_ASSIGN(ItemModelObserverBridge); |
| 87 | }; |
| 88 | |
| 89 | ItemModelObserverBridge::ItemModelObserverBridge(AppsGridViewItem* parent, |
| 90 | AppListItemModel* model) |
| 91 | : parent_(parent), |
| 92 | model_(model) { |
| 93 | model_->AddObserver(this); |
| 94 | } |
| 95 | |
| 96 | ItemModelObserverBridge::~ItemModelObserverBridge() { |
| 97 | model_->RemoveObserver(this); |
| 98 | } |
| 99 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 100 | NSMenu* ItemModelObserverBridge::GetContextMenu() { |
| 101 | if (!context_menu_controller_) { |
| 102 | context_menu_controller_.reset( |
| 103 | [[MenuController alloc] initWithModel:model_->GetContextMenuModel() |
| 104 | useWithPopUpButtonCell:NO]); |
| 105 | } |
| 106 | return [context_menu_controller_ menu]; |
| 107 | } |
| 108 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 109 | void ItemModelObserverBridge::ItemIconChanged() { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 110 | [parent_ updateButtonImage]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | void ItemModelObserverBridge::ItemTitleChanged() { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 114 | [parent_ updateButtonTitle]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | void ItemModelObserverBridge::ItemHighlightedChanged() { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 118 | if (model_->highlighted()) |
| 119 | [parent_ ensureVisible]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | void ItemModelObserverBridge::ItemIsInstallingChanged() { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 123 | [parent_ setItemIsInstalling:model_->is_installing()]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 124 | } |
| 125 | |
| 126 | void ItemModelObserverBridge::ItemPercentDownloadedChanged() { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 127 | [parent_ setPercentDownloaded:model_->percent_downloaded()]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | } // namespace app_list |
| 131 | |
| 132 | // Container for an NSButton to allow proper alignment of the icon in the apps |
| 133 | // grid, and to draw with a highlight when selected. |
| 134 | @interface AppsGridItemBackgroundView : NSView { |
| 135 | @private |
| 136 | BOOL selected_; |
| 137 | } |
| 138 | |
| 139 | - (NSButton*)button; |
| 140 | |
| 141 | - (void)setSelected:(BOOL)flag; |
| 142 | |
| 143 | @end |
| 144 | |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 145 | @interface AppsGridItemButtonCell : NSButtonCell { |
| 146 | @private |
| 147 | BOOL hasShadow_; |
| 148 | } |
| 149 | |
| 150 | @property(assign, nonatomic) BOOL hasShadow; |
| 151 | |
| 152 | @end |
| 153 | |
| 154 | @interface AppsGridItemButton : NSButton; |
| 155 | @end |
| 156 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 157 | @implementation AppsGridItemBackgroundView |
| 158 | |
| 159 | - (NSButton*)button { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 160 | // These views are part of a prototype NSCollectionViewItem, copied with an |
| 161 | // NSCoder. Rather than encoding additional members, the following relies on |
| 162 | // the button always being the first item added to AppsGridItemBackgroundView. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 163 | return base::mac::ObjCCastStrict<NSButton>([[self subviews] objectAtIndex:0]); |
| 164 | } |
| 165 | |
| 166 | - (void)setSelected:(BOOL)flag { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 167 | DCHECK(selected_ != flag); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 168 | selected_ = flag; |
| 169 | [self setNeedsDisplay:YES]; |
| 170 | } |
| 171 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 172 | // Ignore all hit tests. The grid controller needs to be the owner of any drags. |
| 173 | - (NSView*)hitTest:(NSPoint)aPoint { |
| 174 | return nil; |
| 175 | } |
| 176 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 177 | - (void)drawRect:(NSRect)dirtyRect { |
| 178 | if (!selected_) |
| 179 | return; |
| 180 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 181 | [gfx::SkColorToSRGBNSColor(app_list::kSelectedColor) set]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 182 | NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver); |
| 183 | } |
| 184 | |
| 185 | - (void)mouseDown:(NSEvent*)theEvent { |
| 186 | [[[self button] cell] setHighlighted:YES]; |
| 187 | } |
| 188 | |
| 189 | - (void)mouseDragged:(NSEvent*)theEvent { |
| 190 | NSPoint pointInView = [self convertPoint:[theEvent locationInWindow] |
| 191 | fromView:nil]; |
| 192 | BOOL isInView = [self mouse:pointInView inRect:[self bounds]]; |
| 193 | [[[self button] cell] setHighlighted:isInView]; |
| 194 | } |
| 195 | |
| 196 | - (void)mouseUp:(NSEvent*)theEvent { |
| 197 | NSPoint pointInView = [self convertPoint:[theEvent locationInWindow] |
| 198 | fromView:nil]; |
| 199 | if (![self mouse:pointInView inRect:[self bounds]]) |
| 200 | return; |
| 201 | |
| 202 | [[self button] performClick:self]; |
| 203 | } |
| 204 | |
| 205 | @end |
| 206 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 207 | @implementation AppsGridViewItem |
| 208 | |
| 209 | - (id)initWithSize:(NSSize)tileSize { |
| 210 | if ((self = [super init])) { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 211 | base::scoped_nsobject<AppsGridItemButton> prototypeButton( |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 212 | [[AppsGridItemButton alloc] initWithFrame:NSMakeRect( |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 213 | 0, 0, tileSize.width, tileSize.height - kTileTopPadding)]); |
| 214 | |
| 215 | // This NSButton style always positions the icon at the very top of the |
| 216 | // button frame. AppsGridViewItem uses an enclosing view so that it is |
| 217 | // visually correct. |
| 218 | [prototypeButton setImagePosition:NSImageAbove]; |
| 219 | [prototypeButton setButtonType:NSMomentaryChangeButton]; |
| 220 | [prototypeButton setBordered:NO]; |
| 221 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 222 | base::scoped_nsobject<AppsGridItemBackgroundView> prototypeButtonBackground( |
| 223 | [[AppsGridItemBackgroundView alloc] |
| 224 | initWithFrame:NSMakeRect(0, 0, tileSize.width, tileSize.height)]); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 225 | [prototypeButtonBackground addSubview:prototypeButton]; |
| 226 | [self setView:prototypeButtonBackground]; |
| 227 | } |
| 228 | return self; |
| 229 | } |
| 230 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 231 | - (NSProgressIndicator*)progressIndicator { |
| 232 | return progressIndicator_; |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 233 | } |
| 234 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 235 | - (void)updateButtonTitle { |
| 236 | if (progressIndicator_) |
| 237 | return; |
| 238 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 239 | base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( |
| 240 | [[NSMutableParagraphStyle alloc] init]); |
| 241 | [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail]; |
| 242 | [paragraphStyle setAlignment:NSCenterTextAlignment]; |
| 243 | NSDictionary* titleAttributes = @{ |
| 244 | NSParagraphStyleAttributeName : paragraphStyle, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 245 | NSFontAttributeName : ui::ResourceBundle::GetSharedInstance() |
| 246 | .GetFont(app_list::kItemTextFontStyle) |
| 247 | .DeriveFont(kMacFontSizeDelta) |
| 248 | .GetNativeFont(), |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 249 | NSForegroundColorAttributeName : [self isSelected] ? |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 250 | gfx::SkColorToSRGBNSColor(app_list::kGridTitleHoverColor) : |
| 251 | gfx::SkColorToSRGBNSColor(app_list::kGridTitleColor) |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 252 | }; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 253 | NSString* buttonTitle = base::SysUTF8ToNSString([self model]->title()); |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 254 | base::scoped_nsobject<NSAttributedString> attributedTitle( |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 255 | [[NSAttributedString alloc] initWithString:buttonTitle |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 256 | attributes:titleAttributes]); |
| 257 | [[self button] setAttributedTitle:attributedTitle]; |
| 258 | } |
| 259 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 260 | - (void)updateButtonImage { |
| 261 | const gfx::Size iconSize = gfx::Size(kIconSize, kIconSize); |
| 262 | gfx::ImageSkia icon = [self model]->icon(); |
| 263 | if (icon.size() != iconSize) { |
| 264 | icon = gfx::ImageSkiaOperations::CreateResizedImage( |
| 265 | icon, skia::ImageOperations::RESIZE_BEST, iconSize); |
| 266 | } |
| 267 | NSImage* buttonImage = gfx::NSImageFromImageSkiaWithColorSpace( |
| 268 | icon, base::mac::GetSRGBColorSpace()); |
| 269 | [[self button] setImage:buttonImage]; |
| 270 | [[[self button] cell] setHasShadow:[self model]->has_shadow()]; |
| 271 | } |
| 272 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 273 | - (void)setModel:(app_list::AppListItemModel*)itemModel { |
| 274 | if (!itemModel) { |
| 275 | observerBridge_.reset(); |
| 276 | return; |
| 277 | } |
| 278 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 279 | observerBridge_.reset(new app_list::ItemModelObserverBridge(self, itemModel)); |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 280 | [self updateButtonTitle]; |
| 281 | [self updateButtonImage]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 282 | |
| 283 | if (trackingArea_.get()) |
| 284 | [[self view] removeTrackingArea:trackingArea_.get()]; |
| 285 | |
| 286 | trackingArea_.reset( |
| 287 | [[CrTrackingArea alloc] initWithRect:NSZeroRect |
| 288 | options:NSTrackingInVisibleRect | |
| 289 | NSTrackingMouseEnteredAndExited | |
| 290 | NSTrackingActiveInKeyWindow |
| 291 | owner:self |
| 292 | userInfo:nil]); |
| 293 | [[self view] addTrackingArea:trackingArea_.get()]; |
| 294 | } |
| 295 | |
| 296 | - (app_list::AppListItemModel*)model { |
| 297 | return observerBridge_->model(); |
| 298 | } |
| 299 | |
| 300 | - (NSButton*)button { |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 301 | return [[self itemBackgroundView] button]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 302 | } |
| 303 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 304 | - (NSMenu*)contextMenu { |
| 305 | [self setSelected:YES]; |
| 306 | return observerBridge_->GetContextMenu(); |
| 307 | } |
| 308 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 309 | - (NSBitmapImageRep*)dragRepresentationForRestore:(BOOL)isRestore { |
| 310 | NSButton* button = [self button]; |
| 311 | NSView* itemView = [self view]; |
| 312 | |
| 313 | // The snapshot is never drawn as if it was selected. Also remove the cell |
| 314 | // highlight on the button image, added when it was clicked. |
| 315 | [button setHidden:NO]; |
| 316 | [[button cell] setHighlighted:NO]; |
| 317 | [self setSelected:NO]; |
| 318 | [progressIndicator_ setHidden:YES]; |
| 319 | if (isRestore) |
| 320 | [self updateButtonTitle]; |
| 321 | else |
| 322 | [button setTitle:@""]; |
| 323 | |
| 324 | NSBitmapImageRep* imageRep = |
| 325 | [itemView bitmapImageRepForCachingDisplayInRect:[itemView visibleRect]]; |
| 326 | [itemView cacheDisplayInRect:[itemView visibleRect] |
| 327 | toBitmapImageRep:imageRep]; |
| 328 | |
| 329 | if (isRestore) { |
| 330 | [progressIndicator_ setHidden:NO]; |
| 331 | [self setSelected:YES]; |
| 332 | } |
| 333 | // Button is always hidden until the drag animation completes. |
| 334 | [button setHidden:YES]; |
| 335 | return imageRep; |
| 336 | } |
| 337 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 338 | - (void)onInitialModelBuilt { |
| 339 | if ([self model]->highlighted()) { |
| 340 | [self ensureVisible]; |
| 341 | if (![self model]->is_installing()) |
| 342 | [self setSelected:YES]; |
| 343 | } |
| 344 | } |
| 345 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 346 | - (void)ensureVisible { |
| 347 | NSCollectionView* collectionView = [self collectionView]; |
| 348 | AppsGridController* gridController = |
| 349 | base::mac::ObjCCastStrict<AppsGridController>([collectionView delegate]); |
| 350 | size_t pageIndex = [gridController pageIndexForCollectionView:collectionView]; |
| 351 | [gridController scrollToPage:pageIndex]; |
| 352 | } |
| 353 | |
| 354 | - (void)setItemIsInstalling:(BOOL)isInstalling { |
| 355 | if (!isInstalling == !progressIndicator_) |
| 356 | return; |
| 357 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 358 | [self ensureVisible]; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 359 | if (!isInstalling) { |
| 360 | [progressIndicator_ removeFromSuperview]; |
| 361 | progressIndicator_.reset(); |
| 362 | [self updateButtonTitle]; |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 363 | [self setSelected:YES]; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 364 | return; |
| 365 | } |
| 366 | |
| 367 | NSRect rect = NSMakeRect( |
| 368 | kProgressBarHorizontalPadding, |
| 369 | kProgressBarVerticalPadding, |
| 370 | NSWidth([[self view] bounds]) - 2 * kProgressBarHorizontalPadding, |
| 371 | NSProgressIndicatorPreferredAquaThickness); |
| 372 | [[self button] setTitle:@""]; |
| 373 | progressIndicator_.reset([[NSProgressIndicator alloc] initWithFrame:rect]); |
| 374 | [progressIndicator_ setIndeterminate:NO]; |
| 375 | [progressIndicator_ setControlSize:NSSmallControlSize]; |
| 376 | [[self view] addSubview:progressIndicator_]; |
| 377 | } |
| 378 | |
| 379 | - (void)setPercentDownloaded:(int)percent { |
| 380 | // In a corner case, items can be installing when they are first added. For |
| 381 | // those, the icon will start desaturated. Wait for a progress update before |
| 382 | // showing the progress bar. |
| 383 | [self setItemIsInstalling:YES]; |
| 384 | if (percent != -1) { |
| 385 | [progressIndicator_ setDoubleValue:percent]; |
| 386 | return; |
| 387 | } |
| 388 | |
| 389 | // Otherwise, fully downloaded and waiting for install to complete. |
| 390 | [progressIndicator_ setIndeterminate:YES]; |
| 391 | [progressIndicator_ startAnimation:self]; |
| 392 | } |
| 393 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 394 | - (AppsGridItemBackgroundView*)itemBackgroundView { |
| 395 | return base::mac::ObjCCastStrict<AppsGridItemBackgroundView>([self view]); |
| 396 | } |
| 397 | |
| 398 | - (void)mouseEntered:(NSEvent*)theEvent { |
| 399 | [self setSelected:YES]; |
| 400 | } |
| 401 | |
| 402 | - (void)mouseExited:(NSEvent*)theEvent { |
| 403 | [self setSelected:NO]; |
| 404 | } |
| 405 | |
| 406 | - (void)setSelected:(BOOL)flag { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 407 | if ([self isSelected] == flag) |
| 408 | return; |
| 409 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 410 | [[self itemBackgroundView] setSelected:flag]; |
| 411 | [super setSelected:flag]; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 412 | [self updateButtonTitle]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 413 | } |
| 414 | |
| 415 | @end |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 416 | |
| 417 | @implementation AppsGridItemButton |
| 418 | |
| 419 | + (Class)cellClass { |
| 420 | return [AppsGridItemButtonCell class]; |
| 421 | } |
| 422 | |
| 423 | @end |
| 424 | |
| 425 | @implementation AppsGridItemButtonCell |
| 426 | |
| 427 | @synthesize hasShadow = hasShadow_; |
| 428 | |
| 429 | - (void)drawImage:(NSImage*)image |
| 430 | withFrame:(NSRect)frame |
| 431 | inView:(NSView*)controlView { |
| 432 | if (!hasShadow_) { |
| 433 | [super drawImage:image |
| 434 | withFrame:frame |
| 435 | inView:controlView]; |
| 436 | return; |
| 437 | } |
| 438 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 439 | base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 440 | gfx::ScopedNSGraphicsContextSaveGState context; |
| 441 | [shadow setShadowOffset:NSMakeSize(0, -2)]; |
| 442 | [shadow setShadowBlurRadius:2.0]; |
| 443 | [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0 |
| 444 | alpha:0.14]]; |
| 445 | [shadow set]; |
| 446 | |
| 447 | [super drawImage:image |
| 448 | withFrame:frame |
| 449 | inView:controlView]; |
| 450 | } |
| 451 | |
| 452 | @end |