Home | History | Annotate | Download | only in views
      1 // Copyright 2014 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/aura/env.h"
     13 #include "ui/aura/window.h"
     14 #include "ui/events/event.h"
     15 #include "ui/events/event_constants.h"
     16 #include "ui/events/event_handler.h"
     17 #include "ui/events/event_utils.h"
     18 #include "ui/gfx/screen.h"
     19 
     20 namespace views {
     21 
     22 // Amount of time between when the mouse moves outside the Host's zone and when
     23 // the listener is notified.
     24 const int kNotifyListenerTimeMs = 300;
     25 
     26 class MouseWatcher::Observer : public ui::EventHandler {
     27  public:
     28   explicit Observer(MouseWatcher* mouse_watcher)
     29       : mouse_watcher_(mouse_watcher),
     30         notify_listener_factory_(this) {
     31     aura::Env::GetInstance()->AddPreTargetHandler(this);
     32   }
     33 
     34   virtual ~Observer() {
     35     aura::Env::GetInstance()->RemovePreTargetHandler(this);
     36   }
     37 
     38   // ui::EventHandler implementation:
     39   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
     40     switch (event->type()) {
     41       case ui::ET_MOUSE_MOVED:
     42       case ui::ET_MOUSE_DRAGGED:
     43         HandleMouseEvent(MouseWatcherHost::MOUSE_MOVE);
     44         break;
     45       case ui::ET_MOUSE_EXITED:
     46         HandleMouseEvent(MouseWatcherHost::MOUSE_EXIT);
     47         break;
     48       default:
     49         break;
     50     }
     51   }
     52 
     53  private:
     54   MouseWatcherHost* host() const { return mouse_watcher_->host_.get(); }
     55 
     56   // Called when a mouse event we're interested is seen.
     57   void HandleMouseEvent(MouseWatcherHost::MouseEventType event_type) {
     58     // It's safe to use last_mouse_location() here as this function is invoked
     59     // during event dispatching.
     60     if (!host()->Contains(aura::Env::GetInstance()->last_mouse_location(),
     61                           event_type)) {
     62       // Mouse moved outside the host's zone, start a timer to notify the
     63       // listener.
     64       if (!notify_listener_factory_.HasWeakPtrs()) {
     65         base::MessageLoop::current()->PostDelayedTask(
     66             FROM_HERE,
     67             base::Bind(&Observer::NotifyListener,
     68                        notify_listener_factory_.GetWeakPtr()),
     69             event_type == MouseWatcherHost::MOUSE_MOVE
     70                 ? base::TimeDelta::FromMilliseconds(kNotifyListenerTimeMs)
     71                 : mouse_watcher_->notify_on_exit_time_);
     72       }
     73     } else {
     74       // Mouse moved quickly out of the host and then into it again, so cancel
     75       // the timer.
     76       notify_listener_factory_.InvalidateWeakPtrs();
     77     }
     78   }
     79 
     80   void NotifyListener() {
     81     mouse_watcher_->NotifyListener();
     82     // WARNING: we've been deleted.
     83   }
     84 
     85  private:
     86   MouseWatcher* mouse_watcher_;
     87 
     88   // A factory that is used to construct a delayed callback to the listener.
     89   base::WeakPtrFactory<Observer> notify_listener_factory_;
     90 
     91   DISALLOW_COPY_AND_ASSIGN(Observer);
     92 };
     93 
     94 MouseWatcherListener::~MouseWatcherListener() {
     95 }
     96 
     97 MouseWatcherHost::~MouseWatcherHost() {
     98 }
     99 
    100 MouseWatcher::MouseWatcher(MouseWatcherHost* host,
    101                            MouseWatcherListener* listener)
    102     : host_(host),
    103       listener_(listener),
    104       notify_on_exit_time_(base::TimeDelta::FromMilliseconds(
    105           kNotifyListenerTimeMs)) {
    106 }
    107 
    108 MouseWatcher::~MouseWatcher() {
    109 }
    110 
    111 void MouseWatcher::Start() {
    112   if (!is_observing())
    113     observer_.reset(new Observer(this));
    114 }
    115 
    116 void MouseWatcher::Stop() {
    117   observer_.reset(NULL);
    118 }
    119 
    120 void MouseWatcher::NotifyListener() {
    121   observer_.reset(NULL);
    122   listener_->MouseMovedOutOfHost();
    123 }
    124 
    125 }  // namespace views
    126