summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-17 21:18:20 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-17 21:18:20 +0000
commitf2c9df50f8acaa7818758f4fdb0b95ab41ee2706 (patch)
treef0fcea13abda28604065094e1cfe375e62ff9237 /views
parent41ef4508ee9980a2945514ba2f913d1bf64ee326 (diff)
downloadchromium_src-f2c9df50f8acaa7818758f4fdb0b95ab41ee2706.zip
chromium_src-f2c9df50f8acaa7818758f4fdb0b95ab41ee2706.tar.gz
chromium_src-f2c9df50f8acaa7818758f4fdb0b95ab41ee2706.tar.bz2
Refactors mouse watching out of TabStrip into a standalone class.
BUG=27797 TEST=none Review URL: http://codereview.chromium.org/3140008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56424 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r--views/mouse_watcher.cc153
-rw-r--r--views/mouse_watcher.h75
-rw-r--r--views/views.gyp2
3 files changed, 230 insertions, 0 deletions
diff --git a/views/mouse_watcher.cc b/views/mouse_watcher.cc
new file mode 100644
index 0000000..dfe8d60
--- /dev/null
+++ b/views/mouse_watcher.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/mouse_watcher.h"
+
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "views/screen.h"
+#include "views/view.h"
+
+namespace views {
+
+// Amount of time between when the mouse moves outside the view's zone and when
+// the listener is notified.
+static const int kNotifyListenerTimeMs = 300;
+
+class MouseWatcher::Observer : public MessageLoopForUI::Observer {
+ public:
+ explicit Observer(MouseWatcher* mouse_watcher)
+ : mouse_watcher_(mouse_watcher),
+ ALLOW_THIS_IN_INITIALIZER_LIST(notify_listener_factory_(this)) {
+ MessageLoopForUI::current()->AddObserver(this);
+ }
+
+ ~Observer() {
+ MessageLoopForUI::current()->RemoveObserver(this);
+ }
+
+ // MessageLoop::Observer implementation:
+#if defined(OS_WIN)
+ void WillProcessMessage(const MSG& msg) {
+ }
+
+ void DidProcessMessage(const MSG& msg) {
+ // We spy on three different Windows messages here to see if the mouse has
+ // moved out of the bounds of the view. The messages are:
+ //
+ // WM_MOUSEMOVE:
+ // For when the mouse moves from the view into the rest of the browser UI,
+ // i.e. within the bounds of the same windows HWND.
+ // WM_MOUSELEAVE:
+ // For when the mouse moves out of the bounds of the view's HWND.
+ // WM_NCMOUSELEAVE:
+ // For notification when the mouse leaves the _non-client_ area.
+ //
+ switch (msg.message) {
+ case WM_MOUSEMOVE:
+ case WM_MOUSELEAVE:
+ case WM_NCMOUSELEAVE:
+ HandleGlobalMouseMoveEvent();
+ break;
+ }
+}
+#else
+ void WillProcessEvent(GdkEvent* event) {
+ }
+
+ void DidProcessEvent(GdkEvent* event) {
+ switch (event->type) {
+ case GDK_MOTION_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ HandleGlobalMouseMoveEvent();
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+
+ private:
+ views::View* view() { return mouse_watcher_->host_; }
+
+ // Returns whether or not the cursor is currently in the view's "zone" which
+ // is defined as a slightly larger region than the view.
+ bool IsCursorInViewZone() {
+ gfx::Rect bounds = view()->GetLocalBounds(true);
+ gfx::Point view_topleft(bounds.origin());
+ views::View::ConvertPointToScreen(view(), &view_topleft);
+ bounds.set_origin(view_topleft);
+ bounds.SetRect(view_topleft.x() - mouse_watcher_->hot_zone_insets_.left(),
+ view_topleft.y() - mouse_watcher_->hot_zone_insets_.top(),
+ bounds.width() + mouse_watcher_->hot_zone_insets_.width(),
+ bounds.height() + mouse_watcher_->hot_zone_insets_.height());
+
+ gfx::Point cursor_point = views::Screen::GetCursorScreenPoint();
+
+ return bounds.Contains(cursor_point.x(), cursor_point.y());
+ }
+
+ // Called from the message loop observer when a mouse movement has occurred.
+ void HandleGlobalMouseMoveEvent() {
+ if (!IsCursorInViewZone()) {
+ // Mouse moved outside the view's zone, start a timer to notify the
+ // listener.
+ if (notify_listener_factory_.empty()) {
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ notify_listener_factory_.NewRunnableMethod(
+ &Observer::NotifyListener),
+ kNotifyListenerTimeMs);
+ }
+ } else {
+ // Mouse moved quickly out of the view and then into it again, so cancel
+ // the timer.
+ notify_listener_factory_.RevokeAll();
+ }
+ }
+
+ void NotifyListener() {
+ mouse_watcher_->NotifyListener();
+ // WARNING: we've been deleted.
+ }
+
+ private:
+ MouseWatcher* mouse_watcher_;
+
+ // A factory that is used to construct a delayed callback to the listener.
+ ScopedRunnableMethodFactory<Observer> notify_listener_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Observer);
+};
+
+MouseWatcherListener::~MouseWatcherListener() {
+}
+
+MouseWatcher::MouseWatcher(views::View* host,
+ MouseWatcherListener* listener,
+ const gfx::Insets& hot_zone_insets)
+ : host_(host),
+ listener_(listener),
+ hot_zone_insets_(hot_zone_insets) {
+}
+
+MouseWatcher::~MouseWatcher() {
+}
+
+void MouseWatcher::Start() {
+ if (!is_observing())
+ observer_.reset(new Observer(this));
+}
+
+void MouseWatcher::Stop() {
+ observer_.reset(NULL);
+}
+
+void MouseWatcher::NotifyListener() {
+ observer_.reset(NULL);
+ listener_->MouseMovedOutOfView();
+}
+
+} // namespace views
diff --git a/views/mouse_watcher.h b/views/mouse_watcher.h
new file mode 100644
index 0000000..7037487
--- /dev/null
+++ b/views/mouse_watcher.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VIEWS_MOUSE_WATCHER_H_
+#define VIEWS_MOUSE_WATCHER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "gfx/insets.h"
+
+namespace views {
+
+class View;
+
+// MouseWatcherListener is notified when the mouse moves outside the view.
+class MouseWatcherListener {
+ public:
+ virtual void MouseMovedOutOfView() = 0;
+
+ protected:
+ virtual ~MouseWatcherListener();
+};
+
+// MouseWatcher is used to watch mouse movement and notify its listener when the
+// mouse moves outside the bounds of a view.
+class MouseWatcher {
+ public:
+ // Creates a new MouseWatcher. |hot_zone_insets| is added to the bounds of
+ // the view to determine the active zone. For example, if
+ // |hot_zone_insets.bottom()| is 10, then the listener is not notified if
+ // the y coordinate is between the origin of the view and height of the view
+ // plus 10.
+ MouseWatcher(views::View* host,
+ MouseWatcherListener* listener,
+ const gfx::Insets& hot_zone_insets);
+ ~MouseWatcher();
+
+ // Starts watching mouse movements. When the mouse moves outside the bounds of
+ // the view the listener is notified. |Start| may be invoked any number of
+ // times. If the mouse moves outside the bounds of the view the listener is
+ // notified and watcher stops watching events.
+ void Start();
+
+ // Stops watching mouse events.
+ void Stop();
+
+ private:
+ class Observer;
+
+ // Are we currently observing events?
+ bool is_observing() const { return observer_.get() != NULL; }
+
+ // Notifies the listener and stops watching events.
+ void NotifyListener();
+
+ // View we're listening for events over.
+ views::View* host_;
+
+ // Our listener.
+ MouseWatcherListener* listener_;
+
+ // Insets added to the bounds of the view.
+ const gfx::Insets hot_zone_insets_;
+
+ // Does the actual work of listening for mouse events.
+ scoped_ptr<Observer> observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(MouseWatcher);
+};
+
+} // namespace views
+
+#endif // VIEWS_MOUSE_WATCHER_H_
diff --git a/views/views.gyp b/views/views.gyp
index 2e137da..130e351 100644
--- a/views/views.gyp
+++ b/views/views.gyp
@@ -250,6 +250,8 @@
'grid_layout.h',
'layout_manager.cc',
'layout_manager.h',
+ 'mouse_watcher.cc',
+ 'mouse_watcher.h',
'painter.cc',
'painter.h',
'repeat_controller.cc',