summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-11 17:31:17 +0000
committerjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-11 17:31:17 +0000
commit432b209d282ab13dbeec2f01e27d001d2417ed24 (patch)
tree13ab521d880033f176a4e6f8d1c03ecadfd1dd19 /chrome/browser
parent1a644d9469be7ed4f8a08e22e61f0fb990ae0537 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/gtk/tabs/tab_strip_gtk.cc107
-rw-r--r--chrome/browser/gtk/tabs/tab_strip_gtk.h29
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);
};