diff options
Diffstat (limited to 'chrome')
6 files changed, 123 insertions, 4 deletions
diff --git a/chrome/browser/ui/cocoa/download/download_item_controller.h b/chrome/browser/ui/cocoa/download/download_item_controller.h index dea7722..c41ff43 100644 --- a/chrome/browser/ui/cocoa/download/download_item_controller.h +++ b/chrome/browser/ui/cocoa/download/download_item_controller.h @@ -74,6 +74,9 @@ class DownloadShelfContextMenuMac; // contained in its parent. - (void)updateVisibility:(id)sender; +// Called after a download is opened. +- (void)downloadWasOpened; + // Asynchronous icon loading callback. - (void)setIcon:(NSImage*)icon; diff --git a/chrome/browser/ui/cocoa/download/download_item_controller.mm b/chrome/browser/ui/cocoa/download/download_item_controller.mm index 8d10b2a..be0be80 100644 --- a/chrome/browser/ui/cocoa/download/download_item_controller.mm +++ b/chrome/browser/ui/cocoa/download/download_item_controller.mm @@ -242,6 +242,10 @@ class DownloadShelfContextMenuMac : public DownloadShelfContextMenu { [view setHidden:(NSMaxX([view frame]) > NSWidth(containerFrame))]; } +- (void)downloadWasOpened { + [shelf_ downloadWasOpened:self]; +} + - (IBAction)handleButtonClick:(id)sender { NSEvent* event = [NSApp currentEvent]; if ([event modifierFlags] & NSCommandKeyMask) { diff --git a/chrome/browser/ui/cocoa/download/download_item_mac.h b/chrome/browser/ui/cocoa/download/download_item_mac.h index 4a4c7fe..6ad83a3 100644 --- a/chrome/browser/ui/cocoa/download/download_item_mac.h +++ b/chrome/browser/ui/cocoa/download/download_item_mac.h @@ -33,8 +33,8 @@ class DownloadItemMac : DownloadItem::Observer { // DownloadItem::Observer implementation virtual void OnDownloadUpdated(DownloadItem* download); + virtual void OnDownloadOpened(DownloadItem* download); virtual void OnDownloadFileCompleted(DownloadItem* download) { } - virtual void OnDownloadOpened(DownloadItem* download) { } BaseDownloadItemModel* download_model() { return download_model_.get(); } diff --git a/chrome/browser/ui/cocoa/download/download_item_mac.mm b/chrome/browser/ui/cocoa/download/download_item_mac.mm index d6737ef..f39cb96 100644 --- a/chrome/browser/ui/cocoa/download/download_item_mac.mm +++ b/chrome/browser/ui/cocoa/download/download_item_mac.mm @@ -64,6 +64,11 @@ void DownloadItemMac::OnDownloadUpdated(DownloadItem* download) { } } +void DownloadItemMac::OnDownloadOpened(DownloadItem* download) { + DCHECK_EQ(download, download_model_->download()); + [item_controller_ downloadWasOpened]; +} + void DownloadItemMac::LoadIcon() { IconManager* icon_manager = g_browser_process->icon_manager(); if (!icon_manager) { diff --git a/chrome/browser/ui/cocoa/download/download_shelf_controller.h b/chrome/browser/ui/cocoa/download/download_shelf_controller.h index e67fab9..c478439 100644 --- a/chrome/browser/ui/cocoa/download/download_shelf_controller.h +++ b/chrome/browser/ui/cocoa/download/download_shelf_controller.h @@ -52,6 +52,11 @@ class DownloadShelf; // out. CGFloat currentShelfHeight_; + // Used to autoclose the shelf when the mouse is moved off it. Is non-nil + // only when a subsequent mouseExited event can trigger autoclose or when a + // subsequent mouseEntered event will cancel autoclose. Is nil otherwise. + scoped_nsobject<NSTrackingArea> trackingArea_; + // The download items we have added to our shelf. scoped_nsobject<NSMutableArray> downloadItemControllers_; @@ -83,6 +88,9 @@ class DownloadShelf; // Remove a download, possibly via clearing browser data. - (void)remove:(DownloadItemController*)download; +// Called by individual item controllers when their downloads are opened. +- (void)downloadWasOpened:(DownloadItemController*)download; + // Notification that we are closing and should release our downloads. - (void)exiting; diff --git a/chrome/browser/ui/cocoa/download/download_shelf_controller.mm b/chrome/browser/ui/cocoa/download/download_shelf_controller.mm index 41b0b6a..4238b98 100644 --- a/chrome/browser/ui/cocoa/download/download_shelf_controller.mm +++ b/chrome/browser/ui/cocoa/download/download_shelf_controller.mm @@ -24,6 +24,24 @@ #include "grit/theme_resources.h" #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" +// Download shelf autoclose behavior: +// +// The download shelf autocloses if all of this is true: +// 1) An item on the shelf has just been opened. +// 2) All remaining items on the shelf have been opened in the past. +// 3) The mouse leaves the shelf and remains off the shelf for 5 seconds. +// +// If the mouse re-enters the shelf within the 5 second grace period, the +// autoclose is canceled. An autoclose can only be scheduled in response to a +// shelf item being opened or removed. If an item is opened and then the +// resulting autoclose is canceled, subsequent mouse exited events will NOT +// trigger an autoclose. +// +// If the shelf is manually closed while a download is still in progress, that +// download is marked as "opened" for these purposes. If the shelf is later +// reopened, these previously-in-progress download will not block autoclose, +// even if that download was never actually clicked on and opened. + namespace { // Max number of download views we'll contain. Any time a view is added and @@ -39,16 +57,24 @@ const NSTimeInterval kDownloadItemOpenDuration = 0.8; // Duration for download shelf closing animation, in seconds. const NSTimeInterval kDownloadShelfCloseDuration = 0.12; +// Amount of time between when the mouse is moved off the shelf and the shelf is +// autoclosed, in seconds. +const NSTimeInterval kAutoCloseDelaySeconds = 5; + } // namespace @interface DownloadShelfController(Private) - (void)showDownloadShelf:(BOOL)enable; - (void)layoutItems:(BOOL)skipFirst; - (void)closed; +- (BOOL)canAutoClose; - (void)updateTheme; - (void)themeDidChangeNotification:(NSNotification*)notification; - (void)viewFrameDidChange:(NSNotification*)notification; + +- (void)installTrackingArea; +- (void)cancelAutoCloseAndRemoveTrackingArea; @end @@ -100,6 +126,7 @@ const NSTimeInterval kDownloadShelfCloseDuration = 0.12; - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; + [self cancelAutoCloseAndRemoveTrackingArea]; // The controllers will unregister themselves as observers when they are // deallocated. No need to do that here. @@ -173,10 +200,19 @@ const NSTimeInterval kDownloadShelfCloseDuration = 0.12; [self showDownloadShelf:NO]; } +- (void)downloadWasOpened:(DownloadItemController*)item_controller { + // This should only be called on the main thead. + DCHECK([NSThread isMainThread]); + + if ([self canAutoClose]) + [self installTrackingArea]; +} + // We need to explicitly release our download controllers here since they need // to remove themselves as observers before the remaining shutdown happens. - (void)exiting { [[self animatableView] stopAnimation]; + [self cancelAutoCloseAndRemoveTrackingArea]; downloadItemControllers_.reset(); } @@ -216,6 +252,8 @@ const NSTimeInterval kDownloadShelfCloseDuration = 0.12; } - (void)hide:(id)sender { + [self cancelAutoCloseAndRemoveTrackingArea]; + // If |sender| isn't nil, then we're being closed from the UI by the user and // we need to tell our shelf implementation to close. Otherwise, we're being // closed programmatically by our shelf implementation. @@ -255,6 +293,8 @@ const NSTimeInterval kDownloadShelfCloseDuration = 0.12; - (void)addDownloadItem:(BaseDownloadItemModel*)model { DCHECK([NSThread isMainThread]); + [self cancelAutoCloseAndRemoveTrackingArea]; + // Insert new item at the left. scoped_nsobject<DownloadItemController> controller( [[DownloadItemController alloc] initWithModel:model shelf:self]); @@ -312,16 +352,75 @@ const NSTimeInterval kDownloadShelfCloseDuration = 0.12; while (i < [downloadItemControllers_ count]) { DownloadItemController* itemController = [downloadItemControllers_ objectAtIndex:i]; + DownloadItem* download = [itemController download]; bool isTransferDone = - [itemController download]->state() == DownloadItem::COMPLETE || - [itemController download]->state() == DownloadItem::CANCELLED; + download->state() == DownloadItem::COMPLETE || + download->state() == DownloadItem::CANCELLED; if (isTransferDone && - [itemController download]->safety_state() != DownloadItem::DANGEROUS) { + download->safety_state() != DownloadItem::DANGEROUS) { [self remove:itemController]; } else { + // Treat the item as opened when we close. This way if we get shown again + // the user need not open this item for the shelf to auto-close. + download->set_opened(true); ++i; } } } +- (void)mouseEntered:(NSEvent*)event { + // If the mouse re-enters the download shelf, cancel the auto-close. Further + // mouse exits should not trigger autoclose, so also remove the tracking area. + [self cancelAutoCloseAndRemoveTrackingArea]; +} + +- (void)mouseExited:(NSEvent*)event { + // Cancel any previous hide requests, just to be safe. + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(hide:) + object:self]; + + // Schedule an autoclose after a delay. If the mouse is moved back into the + // view, or if an item is added to the shelf, the timer will be canceled. + [self performSelector:@selector(hide:) + withObject:self + afterDelay:kAutoCloseDelaySeconds]; +} + +- (BOOL)canAutoClose { + for (NSUInteger i = 0; i < [downloadItemControllers_ count]; ++i) { + DownloadItemController* itemController = + [downloadItemControllers_ objectAtIndex:i]; + if (![itemController download]->opened()) + return NO; + } + return YES; +} + +- (void)installTrackingArea { + // Install the tracking area to listen for mouseExited messages and trigger + // the shelf autoclose. + if (trackingArea_.get()) + return; + + trackingArea_.reset([[NSTrackingArea alloc] + initWithRect:[[self view] bounds] + options:NSTrackingMouseEnteredAndExited | + NSTrackingActiveAlways + owner:self + userInfo:nil]); + [[self view] addTrackingArea:trackingArea_]; +} + +- (void)cancelAutoCloseAndRemoveTrackingArea { + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(hide:) + object:self]; + + if (trackingArea_.get()) { + [[self view] removeTrackingArea:trackingArea_]; + trackingArea_.reset(nil); + } +} + @end |