Home | History | Annotate | Download | only in views
      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 
     16 namespace views {
     17 
     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;
     21 
     22 class 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 
    126 MouseWatcherListener::~MouseWatcherListener() {
    127 }
    128 
    129 MouseWatcherHost::~MouseWatcherHost() {
    130 }
    131 
    132 MouseWatcher::MouseWatcher(MouseWatcherHost* host,
    133                            MouseWatcherListener* listener)
    134     : host_(host),
    135       listener_(listener),
    136       notify_on_exit_time_(base::TimeDelta::FromMilliseconds(
    137           kNotifyListenerTimeMs)) {
    138 }
    139 
    140 MouseWatcher::~MouseWatcher() {
    141 }
    142 
    143 void MouseWatcher::Start() {
    144   if (!is_observing())
    145     observer_.reset(new Observer(this));
    146 }
    147 
    148 void MouseWatcher::Stop() {
    149   observer_.reset(NULL);
    150 }
    151 
    152 void MouseWatcher::NotifyListener() {
    153   observer_.reset(NULL);
    154   listener_->MouseMovedOutOfHost();
    155 }
    156 
    157 }  // namespace views
    158