Index: chrome/browser/ui/gtk/download_shelf_gtk.cc |
diff --git a/chrome/browser/ui/gtk/download_shelf_gtk.cc b/chrome/browser/ui/gtk/download_shelf_gtk.cc |
index a964b1c7d4ce543c106dd8b2f64d915dadc305d4..baaf900ddb58849534a5845fce706e16fe0bc15a 100644 |
--- a/chrome/browser/ui/gtk/download_shelf_gtk.cc |
+++ b/chrome/browser/ui/gtk/download_shelf_gtk.cc |
@@ -19,6 +19,9 @@ |
#include "chrome/browser/ui/gtk/gtk_util.h" |
#include "chrome/common/notification_service.h" |
#include "gfx/gtk_util.h" |
+#include "gfx/insets.h" |
+#include "gfx/point.h" |
+#include "gfx/rect.h" |
#include "grit/generated_resources.h" |
#include "grit/theme_resources.h" |
#include "ui/base/l10n/l10n_util.h" |
@@ -45,12 +48,22 @@ const int kRightPadding = 10; |
// Speed of the shelf show/hide animation. |
const int kShelfAnimationDurationMs = 120; |
+// The time between when the user mouses out of the download shelf zone and |
+// when the shelf closes (when auto-close is enabled). |
+const int kAutoCloseDelayMs = 300; |
+ |
+// The area to the top of the shelf that is considered part of its "zone". |
+const int kShelfAuraSize = 40; |
+ |
} // namespace |
DownloadShelfGtk::DownloadShelfGtk(Browser* browser, GtkWidget* parent) |
: browser_(browser), |
is_showing_(false), |
- theme_provider_(GtkThemeProvider::GetFrom(browser->profile())) { |
+ theme_provider_(GtkThemeProvider::GetFrom(browser->profile())), |
+ close_on_mouse_out_(false), |
+ mouse_in_shelf_(false), |
+ auto_close_factory_(this) { |
// Logically, the shelf is a vbox that contains two children: a one pixel |
// tall event box, which serves as the top border, and an hbox, which holds |
// the download items and other shelf widgets (close button, show-all- |
@@ -149,6 +162,9 @@ DownloadShelfGtk::~DownloadShelfGtk() { |
shelf_.Destroy(); |
items_hbox_.Destroy(); |
+ |
+ // Make sure we're no longer an observer of the message loop. |
+ SetCloseOnMouseOut(false); |
} |
void DownloadShelfGtk::AddDownload(BaseDownloadItemModel* download_model_) { |
@@ -167,6 +183,7 @@ bool DownloadShelfGtk::IsClosing() const { |
void DownloadShelfGtk::Show() { |
slide_widget_->Open(); |
browser_->UpdateDownloadShelfVisibility(true); |
+ CancelAutoClose(); |
} |
void DownloadShelfGtk::Close() { |
@@ -175,6 +192,7 @@ void DownloadShelfGtk::Close() { |
gdk_window_raise(shelf_.get()->window); |
slide_widget_->Close(); |
browser_->UpdateDownloadShelfVisibility(false); |
+ SetCloseOnMouseOut(false); |
} |
Browser* DownloadShelfGtk::browser() const { |
@@ -192,6 +210,9 @@ void DownloadShelfGtk::Closed() { |
download->safety_state() != DownloadItem::DANGEROUS) { |
RemoveDownloadItem(download_items_[i]); |
} else { |
+ // We set all remaining items as "opened", so that the shelf will auto- |
+ // close in the future without the user clicking on them. |
+ download->set_opened(true); |
++i; |
} |
} |
@@ -246,6 +267,8 @@ void DownloadShelfGtk::RemoveDownloadItem(DownloadItemGtk* download_item) { |
if (download_items_.empty()) { |
slide_widget_->CloseWithoutAnimation(); |
browser_->UpdateDownloadShelfVisibility(false); |
+ } else { |
+ AutoCloseIfPossible(); |
} |
} |
@@ -267,3 +290,89 @@ void DownloadShelfGtk::OnButtonClick(GtkWidget* button) { |
browser_->ShowDownloadsTab(); |
} |
} |
+ |
+void DownloadShelfGtk::AutoCloseIfPossible() { |
+ for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin(); |
+ iter != download_items_.end(); ++iter) { |
+ if (!(*iter)->get_download()->opened()) |
+ return; |
+ } |
+ |
+ SetCloseOnMouseOut(true); |
+} |
+ |
+void DownloadShelfGtk::CancelAutoClose() { |
+ SetCloseOnMouseOut(false); |
+ auto_close_factory_.RevokeAll(); |
+} |
+ |
+void DownloadShelfGtk::ItemOpened() { |
+ AutoCloseIfPossible(); |
+} |
+ |
+void DownloadShelfGtk::SetCloseOnMouseOut(bool close) { |
+ if (close_on_mouse_out_ == close) |
+ return; |
+ |
+ close_on_mouse_out_ = close; |
+ mouse_in_shelf_ = close; |
+ if (close) |
+ MessageLoopForUI::current()->AddObserver(this); |
+ else |
+ MessageLoopForUI::current()->RemoveObserver(this); |
+} |
+ |
+void DownloadShelfGtk::WillProcessEvent(GdkEvent* event) { |
+} |
+ |
+void DownloadShelfGtk::DidProcessEvent(GdkEvent* event) { |
+ gfx::Point cursor_screen_coords; |
+ |
+ switch (event->type) { |
+ case GDK_MOTION_NOTIFY: |
+ cursor_screen_coords = |
+ gfx::Point(event->motion.x_root, event->motion.y_root); |
+ break; |
+ case GDK_LEAVE_NOTIFY: |
+ cursor_screen_coords = |
+ gfx::Point(event->crossing.x_root, event->crossing.y_root); |
+ break; |
+ default: |
+ return; |
+ } |
+ |
+ bool mouse_in_shelf = IsCursorInShelfZone(cursor_screen_coords); |
+ if (mouse_in_shelf == mouse_in_shelf_) |
+ return; |
+ mouse_in_shelf_ = mouse_in_shelf; |
+ |
+ if (mouse_in_shelf) |
+ MouseEnteredShelf(); |
+ else |
+ MouseLeftShelf(); |
+} |
+ |
+bool DownloadShelfGtk::IsCursorInShelfZone( |
+ const gfx::Point& cursor_screen_coords) { |
+ gfx::Rect bounds(gtk_util::GetWidgetScreenPosition(shelf_.get()), |
+ gfx::Size(shelf_.get()->allocation.width, |
+ shelf_.get()->allocation.height)); |
+ |
+ // Negative insets expand the rectangle. We only expand the top. |
+ bounds.Inset(gfx::Insets(-kShelfAuraSize, 0, 0, 0)); |
+ |
+ return bounds.Contains(cursor_screen_coords); |
+} |
+ |
+void DownloadShelfGtk::MouseLeftShelf() { |
+ DCHECK(close_on_mouse_out_); |
+ |
+ MessageLoop::current()->PostDelayedTask( |
+ FROM_HERE, |
+ auto_close_factory_.NewRunnableMethod(&DownloadShelfGtk::Close), |
+ kAutoCloseDelayMs); |
+} |
+ |
+void DownloadShelfGtk::MouseEnteredShelf() { |
+ auto_close_factory_.RevokeAll(); |
+} |