apps_grid_view_item.mm revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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" 8#include "base/memory/scoped_nsobject.h" 9#include "base/strings/sys_string_conversions.h" 10#include "skia/ext/skia_utils_mac.h" 11#include "ui/app_list/app_list_constants.h" 12#include "ui/app_list/app_list_item_model.h" 13#include "ui/app_list/app_list_item_model_observer.h" 14#include "ui/base/resource/resource_bundle.h" 15#include "ui/gfx/image/image_skia_util_mac.h" 16#include "ui/gfx/font.h" 17 18namespace { 19 20// Padding from the top of the tile to the top of the app icon. 21const CGFloat kTileTopPadding = 10; 22 23} // namespace 24 25namespace app_list { 26 27class ItemModelObserverBridge : public app_list::AppListItemModelObserver { 28 public: 29 ItemModelObserverBridge(AppsGridViewItem* parent, AppListItemModel* model); 30 virtual ~ItemModelObserverBridge(); 31 32 AppListItemModel* model() { return model_; } 33 34 virtual void ItemIconChanged() OVERRIDE; 35 virtual void ItemTitleChanged() OVERRIDE; 36 virtual void ItemHighlightedChanged() OVERRIDE; 37 virtual void ItemIsInstallingChanged() OVERRIDE; 38 virtual void ItemPercentDownloadedChanged() OVERRIDE; 39 40 private: 41 AppsGridViewItem* parent_; // Weak. Owns us. 42 AppListItemModel* model_; // Weak. Owned by AppListModel::Apps. 43 44 DISALLOW_COPY_AND_ASSIGN(ItemModelObserverBridge); 45}; 46 47ItemModelObserverBridge::ItemModelObserverBridge(AppsGridViewItem* parent, 48 AppListItemModel* model) 49 : parent_(parent), 50 model_(model) { 51 model_->AddObserver(this); 52} 53 54ItemModelObserverBridge::~ItemModelObserverBridge() { 55 model_->RemoveObserver(this); 56} 57 58void ItemModelObserverBridge::ItemIconChanged() { 59 [[parent_ button] setImage:gfx::NSImageFromImageSkia(model_->icon())]; 60} 61 62void ItemModelObserverBridge::ItemTitleChanged() { 63 [[parent_ button] setTitle:base::SysUTF8ToNSString(model_->title())]; 64} 65 66void ItemModelObserverBridge::ItemHighlightedChanged() { 67 //TODO(tapted): Ensure the item view is visible (requires pagination). 68} 69 70void ItemModelObserverBridge::ItemIsInstallingChanged() { 71 //TODO(tapted): Hide the title while itemModel->is_installing(). 72} 73 74void ItemModelObserverBridge::ItemPercentDownloadedChanged() { 75 //TODO(tapted): Update install progress bar for this item. 76} 77 78} // namespace app_list 79 80// Container for an NSButton to allow proper alignment of the icon in the apps 81// grid, and to draw with a highlight when selected. 82@interface AppsGridItemBackgroundView : NSView { 83 @private 84 BOOL selected_; 85} 86 87- (NSButton*)button; 88 89- (void)setSelected:(BOOL)flag; 90 91@end 92 93@implementation AppsGridItemBackgroundView 94 95- (NSButton*)button { 96 return base::mac::ObjCCastStrict<NSButton>([[self subviews] objectAtIndex:0]); 97} 98 99- (void)setSelected:(BOOL)flag { 100 if (selected_ == flag) 101 return; 102 103 selected_ = flag; 104 [self setNeedsDisplay:YES]; 105} 106 107// Ignore all hit tests. The grid controller needs to be the owner of any drags. 108- (NSView*)hitTest:(NSPoint)aPoint { 109 return nil; 110} 111 112- (void)drawRect:(NSRect)dirtyRect { 113 if (!selected_) 114 return; 115 116 [gfx::SkColorToCalibratedNSColor(app_list::kHoverAndPushedColor) set]; 117 NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver); 118} 119 120- (void)mouseDown:(NSEvent*)theEvent { 121 [[[self button] cell] setHighlighted:YES]; 122} 123 124- (void)mouseDragged:(NSEvent*)theEvent { 125 NSPoint pointInView = [self convertPoint:[theEvent locationInWindow] 126 fromView:nil]; 127 BOOL isInView = [self mouse:pointInView inRect:[self bounds]]; 128 [[[self button] cell] setHighlighted:isInView]; 129} 130 131- (void)mouseUp:(NSEvent*)theEvent { 132 NSPoint pointInView = [self convertPoint:[theEvent locationInWindow] 133 fromView:nil]; 134 if (![self mouse:pointInView inRect:[self bounds]]) 135 return; 136 137 [[self button] performClick:self]; 138} 139 140@end 141 142@interface AppsGridViewItem () 143 144- (AppsGridItemBackgroundView*)itemBackgroundView; 145 146@end 147 148@implementation AppsGridViewItem 149 150- (id)initWithSize:(NSSize)tileSize { 151 if ((self = [super init])) { 152 scoped_nsobject<NSButton> prototypeButton( 153 [[NSButton alloc] initWithFrame:NSMakeRect( 154 0, 0, tileSize.width, tileSize.height - kTileTopPadding)]); 155 156 // This NSButton style always positions the icon at the very top of the 157 // button frame. AppsGridViewItem uses an enclosing view so that it is 158 // visually correct. 159 [prototypeButton setImagePosition:NSImageAbove]; 160 [prototypeButton setButtonType:NSMomentaryChangeButton]; 161 [prototypeButton setBordered:NO]; 162 163 [[prototypeButton cell] 164 setFont:ui::ResourceBundle::GetSharedInstance().GetFont( 165 app_list::kItemTextFontStyle).GetNativeFont()]; 166 167 scoped_nsobject<AppsGridItemBackgroundView> prototypeButtonBackground( 168 [[AppsGridItemBackgroundView alloc] initWithFrame:NSMakeRect( 169 0, 0, tileSize.width, tileSize.height)]); 170 [prototypeButtonBackground addSubview:prototypeButton]; 171 [self setView:prototypeButtonBackground]; 172 } 173 return self; 174} 175 176- (void)setModel:(app_list::AppListItemModel*)itemModel { 177 if (!itemModel) { 178 observerBridge_.reset(); 179 return; 180 } 181 182 NSButton* button = [self button]; 183 [button setTitle:base::SysUTF8ToNSString(itemModel->title())]; 184 [button setImage:gfx::NSImageFromImageSkia(itemModel->icon())]; 185 observerBridge_.reset(new app_list::ItemModelObserverBridge(self, itemModel)); 186 187 if (trackingArea_.get()) 188 [[self view] removeTrackingArea:trackingArea_.get()]; 189 190 trackingArea_.reset( 191 [[CrTrackingArea alloc] initWithRect:NSZeroRect 192 options:NSTrackingInVisibleRect | 193 NSTrackingMouseEnteredAndExited | 194 NSTrackingActiveInKeyWindow 195 owner:self 196 userInfo:nil]); 197 [[self view] addTrackingArea:trackingArea_.get()]; 198} 199 200- (app_list::AppListItemModel*)model { 201 return observerBridge_->model(); 202} 203 204- (NSButton*)button { 205 DCHECK_EQ(1u, [[[self view] subviews] count]); 206 return base::mac::ObjCCastStrict<NSButton>( 207 [[[self view] subviews] objectAtIndex:0]); 208} 209 210- (AppsGridItemBackgroundView*)itemBackgroundView { 211 return base::mac::ObjCCastStrict<AppsGridItemBackgroundView>([self view]); 212} 213 214- (void)mouseEntered:(NSEvent*)theEvent { 215 [self setSelected:YES]; 216} 217 218- (void)mouseExited:(NSEvent*)theEvent { 219 [self setSelected:NO]; 220} 221 222- (void)setSelected:(BOOL)flag { 223 [[self itemBackgroundView] setSelected:flag]; 224 [super setSelected:flag]; 225} 226 227@end 228