1 // Copyright (c) 2011 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 "views/mouse_watcher.h"
7 #include "base/compiler_specific.h"
8 #include "base/message_loop.h"
10 #include "ui/gfx/screen.h"
11 #include "views/view.h"
12 #include "views/widget/widget.h"
14 #if defined(USE_WAYLAND)
15 #include "ui/wayland/events/wayland_event.h"
20 // Amount of time between when the mouse moves outside the view's zone and when
21 // the listener is notified.
22 static const int kNotifyListenerTimeMs
= 300;
24 class MouseWatcher::Observer
: public MessageLoopForUI::Observer
{
26 explicit Observer(MouseWatcher
* mouse_watcher
)
27 : mouse_watcher_(mouse_watcher
),
28 ALLOW_THIS_IN_INITIALIZER_LIST(notify_listener_factory_(this)) {
29 MessageLoopForUI::current()->AddObserver(this);
33 MessageLoopForUI::current()->RemoveObserver(this);
36 // MessageLoop::Observer implementation:
38 void WillProcessMessage(const MSG
& msg
) {
41 void DidProcessMessage(const MSG
& msg
) {
42 // We spy on three different Windows messages here to see if the mouse has
43 // moved out of the bounds of the 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 (msg
.message
) {
55 HandleGlobalMouseMoveEvent(false);
59 HandleGlobalMouseMoveEvent(true);
63 #elif defined(USE_WAYLAND)
64 MessageLoopForUI::Observer::EventStatus
WillProcessEvent(
65 ui::WaylandEvent
* event
) {
66 switch (event
->type
) {
67 case ui::WAYLAND_MOTION
:
68 HandleGlobalMouseMoveEvent(false);
70 case ui::WAYLAND_POINTER_FOCUS
:
71 if (!event
->pointer_focus
.state
)
72 HandleGlobalMouseMoveEvent(true);
77 return EVENT_CONTINUE
;
80 void WillProcessEvent(GdkEvent
* event
) {
83 void DidProcessEvent(GdkEvent
* event
) {
84 switch (event
->type
) {
85 case GDK_MOTION_NOTIFY
:
86 HandleGlobalMouseMoveEvent(false);
88 case GDK_LEAVE_NOTIFY
:
89 HandleGlobalMouseMoveEvent(true);
98 View
* view() const { return mouse_watcher_
->host_
; }
100 // Returns whether or not the cursor is currently in the view's "zone" which
101 // is defined as a slightly larger region than the view.
102 bool IsCursorInViewZone() {
103 gfx::Rect bounds
= view()->GetLocalBounds();
104 gfx::Point
view_topleft(bounds
.origin());
105 View::ConvertPointToScreen(view(), &view_topleft
);
106 bounds
.set_origin(view_topleft
);
107 bounds
.SetRect(view_topleft
.x() - mouse_watcher_
->hot_zone_insets_
.left(),
108 view_topleft
.y() - mouse_watcher_
->hot_zone_insets_
.top(),
109 bounds
.width() + mouse_watcher_
->hot_zone_insets_
.width(),
110 bounds
.height() + mouse_watcher_
->hot_zone_insets_
.height());
112 gfx::Point cursor_point
= gfx::Screen::GetCursorScreenPoint();
114 return bounds
.Contains(cursor_point
.x(), cursor_point
.y());
117 // Returns true if the mouse is over the view's window.
118 bool IsMouseOverWindow() {
119 Widget
* widget
= view()->GetWidget();
123 return gfx::Screen::GetWindowAtCursorScreenPoint() ==
124 widget
->GetNativeWindow();
127 // Called from the message loop observer when a mouse movement has occurred.
128 void HandleGlobalMouseMoveEvent(bool check_window
) {
129 bool in_view
= IsCursorInViewZone();
130 if (!in_view
|| (check_window
&& !IsMouseOverWindow())) {
131 // Mouse moved outside the view's zone, start a timer to notify the
133 if (notify_listener_factory_
.empty()) {
134 MessageLoop::current()->PostDelayedTask(
136 notify_listener_factory_
.NewRunnableMethod(
137 &Observer::NotifyListener
),
138 !in_view
? kNotifyListenerTimeMs
:
139 mouse_watcher_
->notify_on_exit_time_ms_
);
142 // Mouse moved quickly out of the view and then into it again, so cancel
144 notify_listener_factory_
.RevokeAll();
148 void NotifyListener() {
149 mouse_watcher_
->NotifyListener();
150 // WARNING: we've been deleted.
154 MouseWatcher
* mouse_watcher_
;
156 // A factory that is used to construct a delayed callback to the listener.
157 ScopedRunnableMethodFactory
<Observer
> notify_listener_factory_
;
159 DISALLOW_COPY_AND_ASSIGN(Observer
);
162 MouseWatcherListener::~MouseWatcherListener() {
165 MouseWatcher::MouseWatcher(View
* host
,
166 MouseWatcherListener
* listener
,
167 const gfx::Insets
& hot_zone_insets
)
170 hot_zone_insets_(hot_zone_insets
),
171 notify_on_exit_time_ms_(kNotifyListenerTimeMs
) {
174 MouseWatcher::~MouseWatcher() {
177 void MouseWatcher::Start() {
179 observer_
.reset(new Observer(this));
182 void MouseWatcher::Stop() {
183 observer_
.reset(NULL
);
186 void MouseWatcher::NotifyListener() {
187 observer_
.reset(NULL
);
188 listener_
->MouseMovedOutOfView();