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. 4 5#include "ui/views/mouse_watcher.h" 6 7#include "base/bind.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/base/events/event_constants.h" 13#include "ui/base/events/event_utils.h" 14#include "ui/gfx/screen.h" 15 16namespace views { 17 18// Amount of time between when the mouse moves outside the Host's zone and when 19// the listener is notified. 20const int kNotifyListenerTimeMs = 300; 21 22class MouseWatcher::Observer : public base::MessageLoopForUI::Observer { 23 public: 24 explicit Observer(MouseWatcher* mouse_watcher) 25 : mouse_watcher_(mouse_watcher), 26 notify_listener_factory_(this) { 27 base::MessageLoopForUI::current()->AddObserver(this); 28 } 29 30 virtual ~Observer() { 31 base::MessageLoopForUI::current()->RemoveObserver(this); 32 } 33 34 // MessageLoop::Observer implementation: 35#if defined(OS_WIN) 36 virtual base::EventStatus WillProcessEvent( 37 const base::NativeEvent& event) OVERRIDE { 38 return base::EVENT_CONTINUE; 39 } 40 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: 44 // 45 // WM_MOUSEMOVE: 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. 48 // WM_MOUSELEAVE: 49 // For when the mouse moves out of the bounds of the view's HWND. 50 // WM_NCMOUSELEAVE: 51 // For notification when the mouse leaves the _non-client_ area. 52 // 53 switch (event.message) { 54 case WM_MOUSEMOVE: 55 HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_MOVE); 56 break; 57 case WM_MOUSELEAVE: 58 case WM_NCMOUSELEAVE: 59 HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_EXIT); 60 break; 61 } 62 } 63#elif defined(USE_AURA) 64 virtual base::EventStatus WillProcessEvent( 65 const base::NativeEvent& event) OVERRIDE { 66 return base::EVENT_CONTINUE; 67 } 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); 74 break; 75 case ui::ET_MOUSE_EXITED: 76 HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_EXIT); 77 break; 78 default: 79 break; 80 } 81 } 82#endif 83 84 private: 85 MouseWatcherHost* host() const { return mouse_watcher_->host_.get(); } 86 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(), 92 event_type); 93 if (!contained) { 94 // Mouse moved outside the host's zone, start a timer to notify the 95 // listener. 96 if (!notify_listener_factory_.HasWeakPtrs()) { 97 base::MessageLoop::current()->PostDelayedTask( 98 FROM_HERE, 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_); 104 } 105 } else { 106 // Mouse moved quickly out of the host and then into it again, so cancel 107 // the timer. 108 notify_listener_factory_.InvalidateWeakPtrs(); 109 } 110 } 111 112 void NotifyListener() { 113 mouse_watcher_->NotifyListener(); 114 // WARNING: we've been deleted. 115 } 116 117 private: 118 MouseWatcher* mouse_watcher_; 119 120 // A factory that is used to construct a delayed callback to the listener. 121 base::WeakPtrFactory<Observer> notify_listener_factory_; 122 123 DISALLOW_COPY_AND_ASSIGN(Observer); 124}; 125 126MouseWatcherListener::~MouseWatcherListener() { 127} 128 129MouseWatcherHost::~MouseWatcherHost() { 130} 131 132MouseWatcher::MouseWatcher(MouseWatcherHost* host, 133 MouseWatcherListener* listener) 134 : host_(host), 135 listener_(listener), 136 notify_on_exit_time_(base::TimeDelta::FromMilliseconds( 137 kNotifyListenerTimeMs)) { 138} 139 140MouseWatcher::~MouseWatcher() { 141} 142 143void MouseWatcher::Start() { 144 if (!is_observing()) 145 observer_.reset(new Observer(this)); 146} 147 148void MouseWatcher::Stop() { 149 observer_.reset(NULL); 150} 151 152void MouseWatcher::NotifyListener() { 153 observer_.reset(NULL); 154 listener_->MouseMovedOutOfHost(); 155} 156 157} // namespace views 158