diff options
author | jhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-11 17:31:17 +0000 |
---|---|---|
committer | jhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-11 17:31:17 +0000 |
commit | 432b209d282ab13dbeec2f01e27d001d2417ed24 (patch) | |
tree | 13ab521d880033f176a4e6f8d1c03ecadfd1dd19 /chrome/browser/gtk/tabs | |
parent | 1a644d9469be7ed4f8a08e22e61f0fb990ae0537 (diff) | |
download | chromium_src-432b209d282ab13dbeec2f01e27d001d2417ed24.zip chromium_src-432b209d282ab13dbeec2f01e27d001d2417ed24.tar.gz chromium_src-432b209d282ab13dbeec2f01e27d001d2417ed24.tar.bz2 |
Start the resize animation after a delay once the mouse has left the tabstrip zone.
BUG=13471
TEST=Open enough tabs in a browser window so that they must resize. Close a few tabs in the middle of the tabstrip. The tabs should not resize. Now move the mouse outside of the tabstrip zone (40 pixels below). The tabs should resize after a short delay.
Review URL: http://codereview.chromium.org/123005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18174 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/gtk/tabs')
-rw-r--r-- | chrome/browser/gtk/tabs/tab_strip_gtk.cc | 107 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_strip_gtk.h | 29 |
2 files changed, 115 insertions, 21 deletions
diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.cc b/chrome/browser/gtk/tabs/tab_strip_gtk.cc index ab6e88c..9265ace 100644 --- a/chrome/browser/gtk/tabs/tab_strip_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_strip_gtk.cc @@ -34,6 +34,14 @@ const int kAnimateToBoundsDurationMs = 150; const int kNewTabButtonHOffset = -5; const int kNewTabButtonVOffset = 5; +// The delay between when the mouse leaves the tabstrip and the resize animation +// is started. +const int kResizeTabsTimeMs = 300; + +// The range outside of the tabstrip where the pointer must enter/leave to +// start/stop the resize animation. +const int kTabStripAnimationVSlop = 40; + const int kHorizontalMoveThreshold = 16; // pixels // The horizontal offset from one tab to the next, @@ -151,8 +159,6 @@ class TabStripGtk::TabAnimation : public AnimationDelegate { virtual void AnimationEnded(const Animation* animation) { tabstrip_->FinishAnimation(this, layout_on_completion_); - // TODO(jhawkins): Remove this once each tab is its own widget. - SimulateMouseMotion(); // This object is destroyed now, so we can't do anything else after this. } @@ -203,23 +209,6 @@ class TabStripGtk::TabAnimation : public AnimationDelegate { double end_unselected_width_; private: - // When the animation completes, we send the Container a message to simulate - // a mouse moved event at the current mouse position. This tickles the Tab - // the mouse is currently over to show the "hot" state of the close button, or - // resets the hover index if it's now stale. - void SimulateMouseMotion() { - // Get default display and screen. - GdkDisplay* display = gdk_display_get_default(); - GdkScreen* screen = gdk_display_get_default_screen(display); - - // Get cursor position. - int x, y; - gdk_display_get_pointer(display, NULL, &x, &y, NULL); - - // Reset cursor position. - gdk_display_warp_pointer(display, screen, x, y); - } - // True if a complete re-layout is required upon completion of the animation. // Subclasses set this if they don't perform a complete layout // themselves and canceling the animation may leave the strip in an @@ -457,7 +446,9 @@ TabStripGtk::TabStripGtk(TabStripModel* model) current_selected_width_(TabGtk::GetStandardSize().width()), available_width_for_tabs_(-1), resize_layout_scheduled_(false), - model_(model) { + model_(model), + resize_layout_factory_(this), + added_as_message_loop_observer_(false) { } TabStripGtk::~TabStripGtk() { @@ -766,6 +757,10 @@ void TabStripGtk::CloseTab(TabGtk* tab) { // the TabStrip). available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab); resize_layout_scheduled_ = true; + // We hook into the message loop in order to receive mouse move events when + // the mouse is outside of the tabstrip. We unhook once the resize layout + // animation is started. + AddMessageLoopObserver(); model_->CloseTabContentsAt(tab_index); } } @@ -840,6 +835,24 @@ bool TabStripGtk::HasAvailableDragActions() const { return model_->delegate()->GetDragActions() != 0; } +/////////////////////////////////////////////////////////////////////////////// +// TabStripGtk, MessageLoop::Observer implementation: + +void TabStripGtk::WillProcessEvent(GdkEvent* event) { + // Nothing to do. +} + +void TabStripGtk::DidProcessEvent(GdkEvent* event) { + switch (event->type) { + case GDK_MOTION_NOTIFY: + case GDK_LEAVE_NOTIFY: + HandleGlobalMouseMoveEvent(); + break; + default: + break; + } +} + //////////////////////////////////////////////////////////////////////////////// // TabStripGtk, private: @@ -881,6 +894,24 @@ void TabStripGtk::RemoveTabAt(int index) { } } +void TabStripGtk::HandleGlobalMouseMoveEvent() { + if (!IsCursorInTabStripZone()) { + // Mouse moved outside the tab slop zone, start a timer to do a resize + // layout after a short while... + if (resize_layout_factory_.empty()) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + resize_layout_factory_.NewRunnableMethod( + &TabStripGtk::ResizeLayoutTabs), + kResizeTabsTimeMs); + } + } else { + // Mouse moved quickly out of the tab strip and then into it again, so + // cancel the timer so that the strip doesn't move when the mouse moves + // back over it. + resize_layout_factory_.RevokeAll(); + } +} + void TabStripGtk::GenerateIdealBounds() { int tab_count = GetTabCount(); double unselected, selected; @@ -995,6 +1026,12 @@ void TabStripGtk::GetDesiredTabWidths(int tab_count, } void TabStripGtk::ResizeLayoutTabs() { + resize_layout_factory_.RevokeAll(); + + // It is critically important that this is unhooked here, otherwise we will + // keep spying on messages forever. + RemoveMessageLoopObserver(); + available_width_for_tabs_ = -1; double unselected, selected; GetDesiredTabWidths(GetTabCount(), &unselected, &selected); @@ -1007,6 +1044,36 @@ void TabStripGtk::ResizeLayoutTabs() { StartResizeLayoutAnimation(); } +bool TabStripGtk::IsCursorInTabStripZone() const { + gfx::Rect bds = bounds(); + gfx::Point tabstrip_topleft(bds.origin()); + gtk_util::ConvertWidgetPointToScreen(tabstrip_.get(), &tabstrip_topleft); + bds.set_origin(tabstrip_topleft); + bds.set_height(bds.height() + kTabStripAnimationVSlop); + + GdkScreen* screen = gdk_screen_get_default(); + GdkDisplay* display = gdk_screen_get_display(screen); + gint x, y; + gdk_display_get_pointer(display, NULL, &x, &y, NULL); + gfx::Point cursor_point(x, y); + + return bds.Contains(cursor_point); +} + +void TabStripGtk::AddMessageLoopObserver() { + if (!added_as_message_loop_observer_) { + MessageLoopForUI::current()->AddObserver(this); + added_as_message_loop_observer_ = true; + } +} + +void TabStripGtk::RemoveMessageLoopObserver() { + if (added_as_message_loop_observer_) { + MessageLoopForUI::current()->RemoveObserver(this); + added_as_message_loop_observer_ = false; + } +} + gfx::Rect TabStripGtk::GetDropBounds(int drop_index, bool drop_before, bool* is_beneath) { diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.h b/chrome/browser/gtk/tabs/tab_strip_gtk.h index 7c21073..3f49712 100644 --- a/chrome/browser/gtk/tabs/tab_strip_gtk.h +++ b/chrome/browser/gtk/tabs/tab_strip_gtk.h @@ -10,6 +10,8 @@ #include "base/basictypes.h" #include "base/gfx/rect.h" +#include "base/task.h" +#include "base/message_loop.h" #include "chrome/browser/gtk/menu_gtk.h" #include "chrome/browser/gtk/tabs/tab_gtk.h" #include "chrome/browser/tabs/tab_strip_model.h" @@ -20,7 +22,8 @@ class DraggedTabControllerGtk; class TabStripGtk : public TabStripModelObserver, public TabGtk::TabDelegate, - public MenuGtk::Delegate { + public MenuGtk::Delegate, + public MessageLoopForUI::Observer { public: class TabAnimation; @@ -107,6 +110,10 @@ class TabStripGtk : public TabStripModelObserver, virtual bool EndDrag(bool canceled); virtual bool HasAvailableDragActions() const; + // MessageLoop::Observer implementation: + virtual void WillProcessEvent(GdkEvent* event); + virtual void DidProcessEvent(GdkEvent* event); + private: friend class DraggedTabControllerGtk; friend class InsertTabAnimation; @@ -231,6 +238,15 @@ class TabStripGtk : public TabStripModelObserver, // Perform an animated resize-relayout of the TabStrip immediately. void ResizeLayoutTabs(); + // Returns whether or not the cursor is currently in the "tab strip zone" + // which is defined as the region above the TabStrip and a bit below it. + bool IsCursorInTabStripZone() const; + + // Ensure that the message loop observer used for event spying is added and + // removed appropriately so we can tell when to resize layout the tab strip. + void AddMessageLoopObserver(); + void RemoveMessageLoopObserver(); + // Calculates the available width for tabs, assuming a Tab is to be closed. int GetAvailableWidthForTabs(TabGtk* last_tab) const; @@ -242,6 +258,10 @@ class TabStripGtk : public TabStripModelObserver, // Cleans up the tab from the TabStrip at the specified |index|. void RemoveTabAt(int index); + // Called from the message loop observer when a mouse movement has occurred + // anywhere over our containing window. + void HandleGlobalMouseMoveEvent(); + // Generates the ideal bounds of the TabStrip when all Tabs have finished // animating to their desired position/bounds. This is used by the standard // Layout method and other callers like the DraggedTabController that need @@ -360,6 +380,13 @@ class TabStripGtk : public TabStripModelObserver, // The context menu. scoped_ptr<MenuGtk> context_menu_; + // A factory that is used to construct a delayed callback to the + // ResizeLayoutTabsNow method. + ScopedRunnableMethodFactory<TabStripGtk> resize_layout_factory_; + + // True if the tabstrip has already been added as a MessageLoop observer. + bool added_as_message_loop_observer_; + DISALLOW_COPY_AND_ASSIGN(TabStripGtk); }; |