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

Side by Side Diff: chrome/browser/ui/cocoa/download/download_shelf_controller.mm

Issue 3400027: [Mac] Makes the download shelf auto-close after the user opens all downloads... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years 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 | Annotate | Revision Log
« no previous file with comments | « chrome/browser/ui/cocoa/download/download_shelf_controller.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 "chrome/browser/ui/cocoa/download/download_shelf_controller.h" 5 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h"
6 6
7 #include "app/l10n_util.h" 7 #include "app/l10n_util.h"
8 #include "app/resource_bundle.h" 8 #include "app/resource_bundle.h"
9 #include "base/mac_util.h" 9 #include "base/mac_util.h"
10 #include "base/sys_string_conversions.h" 10 #include "base/sys_string_conversions.h"
11 #include "chrome/browser/download/download_item.h" 11 #include "chrome/browser/download/download_item.h"
12 #include "chrome/browser/download/download_manager.h" 12 #include "chrome/browser/download/download_manager.h"
13 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/themes/browser_theme_provider.h" 14 #include "chrome/browser/themes/browser_theme_provider.h"
15 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/browser.h"
16 #import "chrome/browser/ui/cocoa/animatable_view.h" 16 #import "chrome/browser/ui/cocoa/animatable_view.h"
17 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h" 17 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
18 #import "chrome/browser/ui/cocoa/browser_window_controller.h" 18 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
19 #include "chrome/browser/ui/cocoa/download/download_item_controller.h" 19 #include "chrome/browser/ui/cocoa/download/download_item_controller.h"
20 #include "chrome/browser/ui/cocoa/download/download_shelf_mac.h" 20 #include "chrome/browser/ui/cocoa/download/download_shelf_mac.h"
21 #import "chrome/browser/ui/cocoa/download/download_shelf_view.h" 21 #import "chrome/browser/ui/cocoa/download/download_shelf_view.h"
22 #import "chrome/browser/ui/cocoa/hyperlink_button_cell.h" 22 #import "chrome/browser/ui/cocoa/hyperlink_button_cell.h"
23 #include "grit/generated_resources.h" 23 #include "grit/generated_resources.h"
24 #include "grit/theme_resources.h" 24 #include "grit/theme_resources.h"
25 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" 25 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h"
26 26
27 // Download shelf autoclose behavior:
28 //
29 // The download shelf autocloses if all of this is true:
30 // 1) An item on the shelf has just been opened.
31 // 2) All remaining items on the shelf have been opened in the past.
32 // 3) The mouse leaves the shelf and remains off the shelf for 5 seconds.
33 //
34 // If the mouse re-enters the shelf within the 5 second grace period, the
35 // autoclose is canceled. An autoclose can only be scheduled in response to a
36 // shelf item being opened or removed. If an item is opened and then the
37 // resulting autoclose is canceled, subsequent mouse exited events will NOT
38 // trigger an autoclose.
39 //
40 // If the shelf is manually closed while a download is still in progress, that
41 // download is marked as "opened" for these purposes. If the shelf is later
42 // reopened, these previously-in-progress download will not block autoclose,
43 // even if that download was never actually clicked on and opened.
44
27 namespace { 45 namespace {
28 46
29 // Max number of download views we'll contain. Any time a view is added and 47 // Max number of download views we'll contain. Any time a view is added and
30 // we already have this many download views, one is removed. 48 // we already have this many download views, one is removed.
31 const size_t kMaxDownloadItemCount = 16; 49 const size_t kMaxDownloadItemCount = 16;
32 50
33 // Horizontal padding between two download items. 51 // Horizontal padding between two download items.
34 const int kDownloadItemPadding = 0; 52 const int kDownloadItemPadding = 0;
35 53
36 // Duration for the open-new-leftmost-item animation, in seconds. 54 // Duration for the open-new-leftmost-item animation, in seconds.
37 const NSTimeInterval kDownloadItemOpenDuration = 0.8; 55 const NSTimeInterval kDownloadItemOpenDuration = 0.8;
38 56
39 // Duration for download shelf closing animation, in seconds. 57 // Duration for download shelf closing animation, in seconds.
40 const NSTimeInterval kDownloadShelfCloseDuration = 0.12; 58 const NSTimeInterval kDownloadShelfCloseDuration = 0.12;
41 59
60 // Amount of time between when the mouse is moved off the shelf and the shelf is
61 // autoclosed, in seconds.
62 const NSTimeInterval kAutoCloseDelaySeconds = 5;
63
42 } // namespace 64 } // namespace
43 65
44 @interface DownloadShelfController(Private) 66 @interface DownloadShelfController(Private)
45 - (void)showDownloadShelf:(BOOL)enable; 67 - (void)showDownloadShelf:(BOOL)enable;
46 - (void)layoutItems:(BOOL)skipFirst; 68 - (void)layoutItems:(BOOL)skipFirst;
47 - (void)closed; 69 - (void)closed;
70 - (BOOL)canAutoClose;
48 71
49 - (void)updateTheme; 72 - (void)updateTheme;
50 - (void)themeDidChangeNotification:(NSNotification*)notification; 73 - (void)themeDidChangeNotification:(NSNotification*)notification;
51 - (void)viewFrameDidChange:(NSNotification*)notification; 74 - (void)viewFrameDidChange:(NSNotification*)notification;
75
76 - (void)installTrackingArea;
77 - (void)cancelAutoCloseAndRemoveTrackingArea;
52 @end 78 @end
53 79
54 80
55 @implementation DownloadShelfController 81 @implementation DownloadShelfController
56 82
57 - (id)initWithBrowser:(Browser*)browser 83 - (id)initWithBrowser:(Browser*)browser
58 resizeDelegate:(id<ViewResizer>)resizeDelegate { 84 resizeDelegate:(id<ViewResizer>)resizeDelegate {
59 if ((self = [super initWithNibName:@"DownloadShelf" 85 if ((self = [super initWithNibName:@"DownloadShelf"
60 bundle:mac_util::MainAppBundle()])) { 86 bundle:mac_util::MainAppBundle()])) {
61 resizeDelegate_ = resizeDelegate; 87 resizeDelegate_ = resizeDelegate;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 object:[self view]]; 119 object:[self view]];
94 120
95 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 121 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
96 NSImage* favicon = rb.GetNativeImageNamed(IDR_DOWNLOADS_FAVICON); 122 NSImage* favicon = rb.GetNativeImageNamed(IDR_DOWNLOADS_FAVICON);
97 DCHECK(favicon); 123 DCHECK(favicon);
98 [image_ setImage:favicon]; 124 [image_ setImage:favicon];
99 } 125 }
100 126
101 - (void)dealloc { 127 - (void)dealloc {
102 [[NSNotificationCenter defaultCenter] removeObserver:self]; 128 [[NSNotificationCenter defaultCenter] removeObserver:self];
129 [self cancelAutoCloseAndRemoveTrackingArea];
103 130
104 // The controllers will unregister themselves as observers when they are 131 // The controllers will unregister themselves as observers when they are
105 // deallocated. No need to do that here. 132 // deallocated. No need to do that here.
106 [super dealloc]; 133 [super dealloc];
107 } 134 }
108 135
109 // Called after the current theme has changed. 136 // Called after the current theme has changed.
110 - (void)themeDidChangeNotification:(NSNotification*)notification { 137 - (void)themeDidChangeNotification:(NSNotification*)notification {
111 [self updateTheme]; 138 [self updateTheme];
112 } 139 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 193
167 [downloadItemControllers_ removeObject:download]; 194 [downloadItemControllers_ removeObject:download];
168 195
169 [self layoutItems]; 196 [self layoutItems];
170 197
171 // Check to see if we have any downloads remaining and if not, hide the shelf. 198 // Check to see if we have any downloads remaining and if not, hide the shelf.
172 if (![downloadItemControllers_ count]) 199 if (![downloadItemControllers_ count])
173 [self showDownloadShelf:NO]; 200 [self showDownloadShelf:NO];
174 } 201 }
175 202
203 - (void)downloadWasOpened:(DownloadItemController*)item_controller {
204 // This should only be called on the main thead.
205 DCHECK([NSThread isMainThread]);
206
207 if ([self canAutoClose])
208 [self installTrackingArea];
209 }
210
176 // We need to explicitly release our download controllers here since they need 211 // We need to explicitly release our download controllers here since they need
177 // to remove themselves as observers before the remaining shutdown happens. 212 // to remove themselves as observers before the remaining shutdown happens.
178 - (void)exiting { 213 - (void)exiting {
179 [[self animatableView] stopAnimation]; 214 [[self animatableView] stopAnimation];
215 [self cancelAutoCloseAndRemoveTrackingArea];
180 downloadItemControllers_.reset(); 216 downloadItemControllers_.reset();
181 } 217 }
182 218
183 // Show or hide the bar based on the value of |enable|. Handles animating the 219 // Show or hide the bar based on the value of |enable|. Handles animating the
184 // resize of the content view. 220 // resize of the content view.
185 - (void)showDownloadShelf:(BOOL)enable { 221 - (void)showDownloadShelf:(BOOL)enable {
186 if ([self isVisible] == enable) 222 if ([self isVisible] == enable)
187 return; 223 return;
188 224
189 if ([[self view] window]) 225 if ([[self view] window])
(...skipping 19 matching lines...) Expand all
209 245
210 - (BOOL)isVisible { 246 - (BOOL)isVisible {
211 return barIsVisible_; 247 return barIsVisible_;
212 } 248 }
213 249
214 - (void)show:(id)sender { 250 - (void)show:(id)sender {
215 [self showDownloadShelf:YES]; 251 [self showDownloadShelf:YES];
216 } 252 }
217 253
218 - (void)hide:(id)sender { 254 - (void)hide:(id)sender {
255 [self cancelAutoCloseAndRemoveTrackingArea];
256
219 // If |sender| isn't nil, then we're being closed from the UI by the user and 257 // If |sender| isn't nil, then we're being closed from the UI by the user and
220 // we need to tell our shelf implementation to close. Otherwise, we're being 258 // we need to tell our shelf implementation to close. Otherwise, we're being
221 // closed programmatically by our shelf implementation. 259 // closed programmatically by our shelf implementation.
222 if (sender) 260 if (sender)
223 bridge_->Close(); 261 bridge_->Close();
224 else 262 else
225 [self showDownloadShelf:NO]; 263 [self showDownloadShelf:NO];
226 } 264 }
227 265
228 - (void)animationDidEnd:(NSAnimation*)animation { 266 - (void)animationDidEnd:(NSAnimation*)animation {
(...skipping 19 matching lines...) Expand all
248 skipFirst = NO; 286 skipFirst = NO;
249 } 287 }
250 } 288 }
251 289
252 - (void)layoutItems { 290 - (void)layoutItems {
253 [self layoutItems:NO]; 291 [self layoutItems:NO];
254 } 292 }
255 293
256 - (void)addDownloadItem:(BaseDownloadItemModel*)model { 294 - (void)addDownloadItem:(BaseDownloadItemModel*)model {
257 DCHECK([NSThread isMainThread]); 295 DCHECK([NSThread isMainThread]);
296 [self cancelAutoCloseAndRemoveTrackingArea];
297
258 // Insert new item at the left. 298 // Insert new item at the left.
259 scoped_nsobject<DownloadItemController> controller( 299 scoped_nsobject<DownloadItemController> controller(
260 [[DownloadItemController alloc] initWithModel:model shelf:self]); 300 [[DownloadItemController alloc] initWithModel:model shelf:self]);
261 301
262 // Adding at index 0 in NSMutableArrays is O(1). 302 // Adding at index 0 in NSMutableArrays is O(1).
263 [downloadItemControllers_ insertObject:controller.get() atIndex:0]; 303 [downloadItemControllers_ insertObject:controller.get() atIndex:0];
264 304
265 [itemContainerView_ addSubview:[controller view]]; 305 [itemContainerView_ addSubview:[controller view]];
266 306
267 // The controller is in charge of removing itself as an observer in its 307 // The controller is in charge of removing itself as an observer in its
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 // laying out the items, so that the longer animation duration we set up above 345 // laying out the items, so that the longer animation duration we set up above
306 // is not overwritten. 346 // is not overwritten.
307 [self layoutItems:YES]; 347 [self layoutItems:YES];
308 } 348 }
309 349
310 - (void)closed { 350 - (void)closed {
311 NSUInteger i = 0; 351 NSUInteger i = 0;
312 while (i < [downloadItemControllers_ count]) { 352 while (i < [downloadItemControllers_ count]) {
313 DownloadItemController* itemController = 353 DownloadItemController* itemController =
314 [downloadItemControllers_ objectAtIndex:i]; 354 [downloadItemControllers_ objectAtIndex:i];
355 DownloadItem* download = [itemController download];
315 bool isTransferDone = 356 bool isTransferDone =
316 [itemController download]->state() == DownloadItem::COMPLETE || 357 download->state() == DownloadItem::COMPLETE ||
317 [itemController download]->state() == DownloadItem::CANCELLED; 358 download->state() == DownloadItem::CANCELLED;
318 if (isTransferDone && 359 if (isTransferDone &&
319 [itemController download]->safety_state() != DownloadItem::DANGEROUS) { 360 download->safety_state() != DownloadItem::DANGEROUS) {
320 [self remove:itemController]; 361 [self remove:itemController];
321 } else { 362 } else {
363 // Treat the item as opened when we close. This way if we get shown again
364 // the user need not open this item for the shelf to auto-close.
365 download->set_opened(true);
322 ++i; 366 ++i;
323 } 367 }
324 } 368 }
325 } 369 }
326 370
371 - (void)mouseEntered:(NSEvent*)event {
372 // If the mouse re-enters the download shelf, cancel the auto-close. Further
373 // mouse exits should not trigger autoclose, so also remove the tracking area.
374 [self cancelAutoCloseAndRemoveTrackingArea];
375 }
376
377 - (void)mouseExited:(NSEvent*)event {
378 // Cancel any previous hide requests, just to be safe.
379 [NSObject cancelPreviousPerformRequestsWithTarget:self
380 selector:@selector(hide:)
381 object:self];
382
383 // Schedule an autoclose after a delay. If the mouse is moved back into the
384 // view, or if an item is added to the shelf, the timer will be canceled.
385 [self performSelector:@selector(hide:)
386 withObject:self
387 afterDelay:kAutoCloseDelaySeconds];
388 }
389
390 - (BOOL)canAutoClose {
391 for (NSUInteger i = 0; i < [downloadItemControllers_ count]; ++i) {
392 DownloadItemController* itemController =
393 [downloadItemControllers_ objectAtIndex:i];
394 if (![itemController download]->opened())
395 return NO;
396 }
397 return YES;
398 }
399
400 - (void)installTrackingArea {
401 // Install the tracking area to listen for mouseExited messages and trigger
402 // the shelf autoclose.
403 if (trackingArea_.get())
404 return;
405
406 trackingArea_.reset([[NSTrackingArea alloc]
407 initWithRect:[[self view] bounds]
408 options:NSTrackingMouseEnteredAndExited |
409 NSTrackingActiveAlways
410 owner:self
411 userInfo:nil]);
412 [[self view] addTrackingArea:trackingArea_];
413 }
414
415 - (void)cancelAutoCloseAndRemoveTrackingArea {
416 [NSObject cancelPreviousPerformRequestsWithTarget:self
417 selector:@selector(hide:)
418 object:self];
419
420 if (trackingArea_.get()) {
421 [[self view] removeTrackingArea:trackingArea_];
422 trackingArea_.reset(nil);
423 }
424 }
425
327 @end 426 @end
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/download/download_shelf_controller.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698