1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/views/mouse_watcher.h"
8 #include "base/compiler_specific.h"
9 #include "base/event_types.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "ui/events/event_constants.h"
13 #include "ui/events/event_utils.h"
14 #include "ui/gfx/screen.h"
18 // Amount of time between when the mouse moves outside the Host's zone and when
19 // the listener is notified.
20 const int kNotifyListenerTimeMs = 300;
22 class MouseWatcher::Observer : public base::MessageLoopForUI::Observer {
24 explicit Observer(MouseWatcher* mouse_watcher)
25 : mouse_watcher_(mouse_watcher),
26 notify_listener_factory_(this) {
27 base::MessageLoopForUI::current()->AddObserver(this);
31 base::MessageLoopForUI::current()->RemoveObserver(this);
34 // MessageLoop::Observer implementation:
36 virtual base::EventStatus WillProcessEvent(
37 const base::NativeEvent& event) OVERRIDE {
38 return base::EVENT_CONTINUE;
41 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
42 // We spy on three different Windows messages here to see if the mouse has
43 // moved out of the bounds of the current view. The messages are:
46 // For when the mouse moves from the view into the rest of the browser UI,
47 // i.e. within the bounds of the same windows HWND.
49 // For when the mouse moves out of the bounds of the view's HWND.
51 // For notification when the mouse leaves the _non-client_ area.
53 switch (event.message) {
55 HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_MOVE);
59 HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_EXIT);
63 #elif defined(USE_AURA)
64 virtual base::EventStatus WillProcessEvent(
65 const base::NativeEvent& event) OVERRIDE {
66 return base::EVENT_CONTINUE;
68 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
69 switch (ui::EventTypeFromNative(event)) {
70 case ui::ET_MOUSE_MOVED:
71 case ui::ET_MOUSE_DRAGGED:
72 // DRAGGED is a special case of MOVED. See events_win.cc/events_x.cc.
73 HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_MOVE);
75 case ui::ET_MOUSE_EXITED:
76 HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_EXIT);
85 MouseWatcherHost* host() const { return mouse_watcher_->host_.get(); }
87 // Called from the message loop observer when a mouse movement has occurred.
88 void HandleGlobalMouseMoveEvent(MouseWatcherHost::MouseEventType event_type) {
89 bool contained = host()->Contains(
90 // TODO(scottmg): Native is wrong http://crbug.com/133312
91 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(),
94 // Mouse moved outside the host's zone, start a timer to notify the
96 if (!notify_listener_factory_.HasWeakPtrs()) {
97 base::MessageLoop::current()->PostDelayedTask(
99 base::Bind(&Observer::NotifyListener,
100 notify_listener_factory_.GetWeakPtr()),
101 event_type == MouseWatcherHost::MOUSE_MOVE
102 ? base::TimeDelta::FromMilliseconds(kNotifyListenerTimeMs)
103 : mouse_watcher_->notify_on_exit_time_);
106 // Mouse moved quickly out of the host and then into it again, so cancel
108 notify_listener_factory_.InvalidateWeakPtrs();
112 void NotifyListener() {
113 mouse_watcher_->NotifyListener();
114 // WARNING: we've been deleted.
118 MouseWatcher* mouse_watcher_;
120 // A factory that is used to construct a delayed callback to the listener.
121 base::WeakPtrFactory<Observer> notify_listener_factory_;
123 DISALLOW_COPY_AND_ASSIGN(Observer);
126 MouseWatcherListener::~MouseWatcherListener() {
129 MouseWatcherHost::~MouseWatcherHost() {
132 MouseWatcher::MouseWatcher(MouseWatcherHost* host,
133 MouseWatcherListener* listener)
136 notify_on_exit_time_(base::TimeDelta::FromMilliseconds(
137 kNotifyListenerTimeMs)) {
140 MouseWatcher::~MouseWatcher() {
143 void MouseWatcher::Start() {
145 observer_.reset(new Observer(this));
148 void MouseWatcher::Stop() {
149 observer_.reset(NULL);
152 void MouseWatcher::NotifyListener() {
153 observer_.reset(NULL);
154 listener_->MouseMovedOutOfHost();