apps_grid_view_item.mm revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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- (void)drawRect:(NSRect)dirtyRect {
108  if (!selected_)
109    return;
110
111  [gfx::SkColorToCalibratedNSColor(app_list::kHoverAndPushedColor) set];
112  NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
113}
114
115- (void)mouseDown:(NSEvent*)theEvent {
116  [[[self button] cell] setHighlighted:YES];
117}
118
119- (void)mouseDragged:(NSEvent*)theEvent {
120  NSPoint pointInView = [self convertPoint:[theEvent locationInWindow]
121                                  fromView:nil];
122  BOOL isInView = [self mouse:pointInView inRect:[self bounds]];
123  [[[self button] cell] setHighlighted:isInView];
124}
125
126- (void)mouseUp:(NSEvent*)theEvent {
127  NSPoint pointInView = [self convertPoint:[theEvent locationInWindow]
128                                  fromView:nil];
129  if (![self mouse:pointInView inRect:[self bounds]])
130    return;
131
132  [[self button] performClick:self];
133}
134
135@end
136
137@interface AppsGridViewItem ()
138
139- (AppsGridItemBackgroundView*)itemBackgroundView;
140
141@end
142
143@implementation AppsGridViewItem
144
145- (id)initWithSize:(NSSize)tileSize {
146  if ((self = [super init])) {
147    scoped_nsobject<NSButton> prototypeButton(
148        [[NSButton alloc] initWithFrame:NSMakeRect(
149            0, 0, tileSize.width, tileSize.height - kTileTopPadding)]);
150
151    // This NSButton style always positions the icon at the very top of the
152    // button frame. AppsGridViewItem uses an enclosing view so that it is
153    // visually correct.
154    [prototypeButton setImagePosition:NSImageAbove];
155    [prototypeButton setButtonType:NSMomentaryChangeButton];
156    [prototypeButton setBordered:NO];
157
158    [[prototypeButton cell]
159        setFont:ui::ResourceBundle::GetSharedInstance().GetFont(
160            app_list::kItemTextFontStyle).GetNativeFont()];
161
162    scoped_nsobject<AppsGridItemBackgroundView> prototypeButtonBackground(
163        [[AppsGridItemBackgroundView alloc] initWithFrame:NSMakeRect(
164            0, 0, tileSize.width, tileSize.height)]);
165    [prototypeButtonBackground addSubview:prototypeButton];
166    [self setView:prototypeButtonBackground];
167  }
168  return self;
169}
170
171- (void)setModel:(app_list::AppListItemModel*)itemModel {
172  if (!itemModel) {
173    observerBridge_.reset();
174    return;
175  }
176
177  NSButton* button = [self button];
178  [button setTitle:base::SysUTF8ToNSString(itemModel->title())];
179  [button setImage:gfx::NSImageFromImageSkia(itemModel->icon())];
180  observerBridge_.reset(new app_list::ItemModelObserverBridge(self, itemModel));
181
182  if (trackingArea_.get())
183    [[self view] removeTrackingArea:trackingArea_.get()];
184
185  trackingArea_.reset(
186      [[CrTrackingArea alloc] initWithRect:NSZeroRect
187                                   options:NSTrackingInVisibleRect |
188                                           NSTrackingMouseEnteredAndExited |
189                                           NSTrackingActiveInKeyWindow
190                                     owner:self
191                                  userInfo:nil]);
192  [[self view] addTrackingArea:trackingArea_.get()];
193}
194
195- (app_list::AppListItemModel*)model {
196  return observerBridge_->model();
197}
198
199- (NSButton*)button {
200  DCHECK_EQ(1u, [[[self view] subviews] count]);
201  return base::mac::ObjCCastStrict<NSButton>(
202      [[[self view] subviews] objectAtIndex:0]);
203}
204
205- (AppsGridItemBackgroundView*)itemBackgroundView {
206  return base::mac::ObjCCastStrict<AppsGridItemBackgroundView>([self view]);
207}
208
209- (void)mouseEntered:(NSEvent*)theEvent {
210  [self setSelected:YES];
211}
212
213- (void)mouseExited:(NSEvent*)theEvent {
214  [self setSelected:NO];
215}
216
217- (void)setSelected:(BOOL)flag {
218  [[self itemBackgroundView] setSelected:flag];
219  [super setSelected:flag];
220}
221
222@end
223