diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-17 21:18:20 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-17 21:18:20 +0000 |
commit | f2c9df50f8acaa7818758f4fdb0b95ab41ee2706 (patch) | |
tree | f0fcea13abda28604065094e1cfe375e62ff9237 /views | |
parent | 41ef4508ee9980a2945514ba2f913d1bf64ee326 (diff) | |
download | chromium_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.cc | 153 | ||||
-rw-r--r-- | views/mouse_watcher.h | 75 | ||||
-rw-r--r-- | views/views.gyp | 2 |
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', |