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

Side by Side Diff: chrome/browser/cocoa/extension_shelf_controller.mm

Issue 175025: Add a bare-bones extension shelf that displays extension items on OS X. (Closed)
Patch Set: Merge ToT, address more comments Created 11 years, 3 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 (c) 2009 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 "extension_shelf_controller.h"
6
7 #include "base/mac_util.h"
8 #include "chrome/browser/browser.h"
9 #include "chrome/browser/extensions/extension_shelf_model.h"
10 #include "skia/ext/skia_utils_mac.h"
11
12 namespace {
13
14 const int kExtensionShelfPaddingTop = 1;
15 const int kToolstripPadding = 2;
16
17 }
18
19 // This class manages the extensions ("toolstrips") on the shelf. It listens to
20 // events sent by the extension system, and acts as a bridge between that and
21 // the cocoa world.
22 class ExtensionShelfMac : public ExtensionShelfModelObserver {
23 public:
24 ExtensionShelfMac(Browser* browser, ExtensionShelfController* controller);
25 virtual ~ExtensionShelfMac();
26
27 // ExtensionShelfModelObserver
28 virtual void ToolstripInsertedAt(ExtensionHost* toolstrip, int index);
29 virtual void ToolstripRemovingAt(ExtensionHost* toolstrip, int index);
30 virtual void ToolstripMoved(ExtensionHost* toolstrip,
31 int from_index,
32 int to_index);
33 virtual void ToolstripChangedAt(ExtensionHost* toolstrip, int index);
34 virtual void ExtensionShelfEmpty();
35 virtual void ShelfModelReloaded();
36 virtual void ShelfModelDeleting();
37
38 // Determines what is our target height and sets it.
39 void AdjustHeight();
40
41 private:
42 class Toolstrip;
43
44 void Show();
45 void Hide();
46
47 // Create the contents of the extension shelf.
48 void Init(Profile* profile);
49
50 // Loads the background image into memory, or does nothing if already loaded.
51 void InitBackground();
52
53 // Re-inserts all toolstrips from the model. Must be called when the shelf
54 // contains no toolstrips.
55 void LoadFromModel();
56
57 void DeleteToolstrips();
58
59 Toolstrip* ToolstripAtIndex(int index);
60
61 ExtensionShelfController* controller_; // weak, owns us
62
63 Browser* browser_; // weak
64
65 // Lazily-initialized background for toolstrips.
66 scoped_ptr<SkBitmap> background_;
67
68 // The model representing the toolstrips on the shelf.
69 ExtensionShelfModel* model_; // weak
70
71 // Set of toolstrip views which are really on the shelf.
72 std::set<Toolstrip*> toolstrips_;
73
74 // Stores if we are currently layouting items.
75 bool is_adjusting_height_;
76
77 DISALLOW_COPY_AND_ASSIGN(ExtensionShelfMac);
78 };
79
80 // This class represents a single extension ("toolstrip") on the extension
81 // shelf.
82 class ExtensionShelfMac::Toolstrip {
83 public:
84 explicit Toolstrip(ExtensionHost* host)
85 : host_(host) {
86 DCHECK(host_->view());
87 Init();
88 }
89
90 // Inserts the native NSView belonging to this extension into the view that
91 // belongs to |controller|. Makes sure the controller is notified when the
92 // extension's |frame| changes.
93 void AddToolstripToController(ExtensionShelfController* controller);
94
95 // Removes the native NSView belonging to this extension from the view that
96 // belongs to |controller|. Removes |controller| as a frame size observer.
97 void RemoveToolstripFromController(ExtensionShelfController* controller);
98
99 // Sets the image that is used by the extension.
100 void SetBackground(const SkBitmap& background) {
101 host_->view()->SetBackground(background);
102 }
103
104 // Returns the native NSView belonging to this extension.
105 gfx::NativeView native_view() {
106 return host_->view()->native_view();
107 }
108
109 private:
110 void Init();
111
112 ExtensionHost* host_; // weak
113
114 const std::string extension_name_;
115
116 private:
117 DISALLOW_COPY_AND_ASSIGN(Toolstrip);
118 };
119
120 void ExtensionShelfMac::Toolstrip::AddToolstripToController(
121 ExtensionShelfController* controller) {
122 NSView* toolstrip_view = host_->view()->native_view();
123 [[controller view] addSubview:toolstrip_view];
124
125 [[NSNotificationCenter defaultCenter]
126 addObserver:controller
127 selector:@selector(updateVisibility:)
128 name:NSViewFrameDidChangeNotification
129 object:toolstrip_view];
130 }
131
132 void ExtensionShelfMac::Toolstrip::RemoveToolstripFromController(
133 ExtensionShelfController* controller) {
134 [host_->view()->native_view() removeFromSuperview];
135
136 [[NSNotificationCenter defaultCenter]
137 removeObserver:controller
138 name:NSViewFrameDidChangeNotification
139 object:host_->view()->native_view()];
140 }
141
142 void ExtensionShelfMac::Toolstrip::Init() {
143 host_->view()->set_is_toolstrip(true);
144 }
145
146 ExtensionShelfMac::ExtensionShelfMac(Browser* browser,
147 ExtensionShelfController* controller)
148 : controller_(controller),
149 browser_(browser),
150 model_(browser->extension_shelf_model()),
151 is_adjusting_height_(false) {
152 if (model_) // Can be NULL in tests.
153 Init(browser_->profile());
154 }
155
156 ExtensionShelfMac::~ExtensionShelfMac() {
157 DeleteToolstrips();
158 if (model_)
159 model_->RemoveObserver(this);
160 }
161
162 void ExtensionShelfMac::Show() {
163 [controller_ show:nil];
164 }
165
166 void ExtensionShelfMac::Hide() {
167 [controller_ hide:nil];
168 }
169
170 void ExtensionShelfMac::ToolstripInsertedAt(ExtensionHost* host,
171 int index) {
172 InitBackground();
173 Toolstrip* toolstrip = new Toolstrip(host);
174 toolstrip->SetBackground(*background_.get());
175 toolstrip->AddToolstripToController(controller_);
176 toolstrips_.insert(toolstrip);
177 model_->SetToolstripDataAt(index, toolstrip);
178
179 AdjustHeight();
180 }
181
182 void ExtensionShelfMac::ToolstripRemovingAt(ExtensionHost* host,
183 int index) {
184 Toolstrip* toolstrip = ToolstripAtIndex(index);
185 toolstrip->RemoveToolstripFromController(controller_);
186 toolstrips_.erase(toolstrip);
187 model_->SetToolstripDataAt(index, NULL);
188 delete toolstrip;
189
190 AdjustHeight();
191 }
192
193 void ExtensionShelfMac::ToolstripMoved(ExtensionHost* host,
194 int from_index,
195 int to_index) {
196 // TODO(thakis): Implement reordering toolstrips.
197 AdjustHeight();
198 }
199
200 void ExtensionShelfMac::ToolstripChangedAt(
201 ExtensionHost* toolstrip, int index) {
202 // TODO(thakis): Implement changing toolstrips.
203 AdjustHeight();
204 }
205
206 void ExtensionShelfMac::ExtensionShelfEmpty() {
207 AdjustHeight();
208 }
209
210 void ExtensionShelfMac::ShelfModelReloaded() {
211 DeleteToolstrips();
212 LoadFromModel();
213 }
214
215 void ExtensionShelfMac::ShelfModelDeleting() {
216 DeleteToolstrips();
217 model_->RemoveObserver(this);
218 model_ = NULL;
219 }
220
221 void ExtensionShelfMac::Init(Profile* profile) {
222 LoadFromModel();
223 model_->AddObserver(this);
224 }
225
226 void ExtensionShelfMac::InitBackground() {
227 if (background_.get())
228 return;
229
230 // If this is called while the shelf is invisible, shortly resize the shelf so
231 // that it can paint itself.
232 NSRect current_frame = [[controller_ view] frame];
233 if (current_frame.size.height < [controller_ height]) {
234 NSRect new_frame = current_frame;
235 new_frame.size.height = [controller_ height];
236 [[controller_ view] setFrame:new_frame];
237 }
238
239 // The background is tiled horizontally in the toolstrip. Hence, its width
240 // should not be too small so that tiling is fast, and not too large, so that
241 // not too much memory is needed -- but the exact width doesn't really matter.
242 const CGFloat kBackgroundTileWidth = 100;
243
244 // Paint shelf background into an SkBitmap. If we decide to keep the shelf, we
245 // need to do this for both the "main window" and "not main window" shadings.
246 NSRect background_rect = NSMakeRect(
247 0, 0,
248 kBackgroundTileWidth, [controller_ height] - kExtensionShelfPaddingTop);
249 NSBitmapImageRep* bitmap_rep = [[controller_ view]
250 bitmapImageRepForCachingDisplayInRect:background_rect];
251
252 [[controller_ view] cacheDisplayInRect:background_rect
253 toBitmapImageRep:bitmap_rep];
254 background_.reset(new SkBitmap(gfx::CGImageToSkBitmap([bitmap_rep CGImage])));
255
256 // Restore old frame.
257 [[controller_ view] setFrame:current_frame];
258 }
259
260 void ExtensionShelfMac::AdjustHeight() {
261 if (model_->empty() || toolstrips_.empty()) {
262 // It's possible that |model_| is not empty, but |toolstrips_| are empty
263 // when removing the last toolstrip.
264 DCHECK(toolstrips_.empty());
265 Hide();
266 return;
267 }
268
269 if (is_adjusting_height_)
270 return;
271 is_adjusting_height_ = true;
272
273 Show();
274
275 // Lay out items horizontally from left to right. This method's name is
276 // misleading, but matches linux and windows for now.
277 CGFloat x = 0;
278 for (std::set<Toolstrip*>::iterator iter = toolstrips_.begin();
279 iter != toolstrips_.end(); ++iter) {
280 NSView* view = (*iter)->native_view();
281 NSRect frame = [view frame];
282 frame.origin.x = x;
283 frame.origin.y = 0;
284 frame.size.height = [controller_ height] - kExtensionShelfPaddingTop;
285 [view setFrame:frame];
286 x += frame.size.width + kToolstripPadding;
287 }
288
289 is_adjusting_height_ = false;
290 }
291
292 void ExtensionShelfMac::LoadFromModel() {
293 DCHECK(toolstrips_.empty());
294 int count = model_->count();
295 for (int i = 0; i < count; ++i)
296 ToolstripInsertedAt(model_->ToolstripAt(i).host, i);
297 AdjustHeight();
298 }
299
300 void ExtensionShelfMac::DeleteToolstrips() {
301 for (std::set<Toolstrip*>::iterator iter = toolstrips_.begin();
302 iter != toolstrips_.end(); ++iter) {
303 (*iter)->RemoveToolstripFromController(controller_);
304 delete *iter;
305 }
306 toolstrips_.clear();
307 }
308
309 ExtensionShelfMac::Toolstrip* ExtensionShelfMac::ToolstripAtIndex(int index) {
310 return static_cast<Toolstrip*>(model_->ToolstripAt(index).data);
311 }
312
313
314 @implementation ExtensionShelfController
315
316 - (id)initWithBrowser:(Browser*)browser
317 resizeDelegate:(id<ViewResizer>)resizeDelegate {
318 if ((self = [super initWithNibName:@"ExtensionShelf"
319 bundle:mac_util::MainAppBundle()])) {
320 resizeDelegate_ = resizeDelegate;
321 browser_ = browser;
322 shelfHeight_ = [[self view] bounds].size.height;
323
324 NSRect frame = [[self view] frame];
325 frame.size.height = 0;
326 [[self view] setFrame:frame];
327 }
328 return self;
329 }
330
331 - (void)wasInsertedIntoWindow {
332 // The bridge_ calls cacheDisplayInRect:toBitmapImageRep:, which requires that
333 // the view is in a superview to work. Hence, create the bridge object no
334 // sooner.
335 DCHECK(bridge_.get() == NULL);
336 bridge_.reset(new ExtensionShelfMac(browser_, self));
337 }
338
339 - (IBAction)show:(id)sender {
340 [resizeDelegate_ resizeView:[self view] newHeight:shelfHeight_];
341 }
342
343 - (IBAction)hide:(id)sender {
344 [resizeDelegate_ resizeView:[self view] newHeight:0];
345 }
346
347 - (CGFloat)height {
348 return shelfHeight_;
349 }
350
351 - (void)updateVisibility:(id)sender {
352 if(bridge_.get())
353 bridge_->AdjustHeight();
354 }
355
356 @end
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/extension_shelf_controller.h ('k') | chrome/browser/cocoa/extension_shelf_controller_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698