Index: mojo/services/view_manager/gesture_manager.cc |
diff --git a/mojo/services/view_manager/gesture_manager.cc b/mojo/services/view_manager/gesture_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1b312bd6b0e4d9c1567df0aff3b6f20fdaa4e26d |
--- /dev/null |
+++ b/mojo/services/view_manager/gesture_manager.cc |
@@ -0,0 +1,701 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "mojo/services/view_manager/gesture_manager.h" |
+ |
+#include <algorithm> |
+ |
+#include "mojo/services/view_manager/gesture_manager_delegate.h" |
+#include "mojo/services/view_manager/server_view.h" |
+#include "mojo/services/view_manager/view_coordinate_conversions.h" |
+#include "mojo/services/view_manager/view_locator.h" |
+#include "third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom.h" |
+#include "third_party/mojo_services/src/view_manager/public/cpp/keys.h" |
+#include "ui/gfx/geometry/point_f.h" |
+ |
+namespace view_manager { |
+ |
+using Views = std::vector<const ServerView*>; |
+ |
+namespace { |
+ |
+GestureManager::GestureAndConnectionId MakeGestureAndConnectionId( |
+ const ServerView* view, |
+ uint32_t gesture_id) { |
+ return (static_cast<GestureManager::GestureAndConnectionId>( |
+ view->id().connection_id) |
+ << 32) | |
+ gesture_id; |
+} |
+ |
+// Returns the views (deepest first) that should receive touch events. This only |
+// returns one view per connection. If multiple views from the same connection |
+// are interested in touch events the shallowest view is returned. |
+Views GetTouchTargets(const ServerView* deepest) { |
+ Views result; |
+ const ServerView* view = deepest; |
+ while (view) { |
+ if (view->properties().count(mojo::kViewManagerKeyWantsTouchEvents)) { |
+ if (!result.empty() && |
+ result.back()->id().connection_id == view->id().connection_id) { |
+ result.pop_back(); |
+ } |
+ result.push_back(view); |
+ } |
+ view = view->parent(); |
+ } |
+ // TODO(sky): I'm doing this until things are converted. Seems as though we |
+ // shouldn't do this long term. |
+ if (result.empty()) |
+ result.push_back(deepest); |
+ return result; |
+} |
+ |
+mojo::EventPtr CloneEventForView(const mojo::Event& event, |
+ const ServerView* view) { |
+ mojo::EventPtr result(event.Clone()); |
+ const gfx::PointF location(event.pointer_data->x, event.pointer_data->y); |
+ const gfx::PointF target_location( |
+ ConvertPointFBetweenViews(view->GetRoot(), view, location)); |
+ result->pointer_data->x = target_location.x(); |
+ result->pointer_data->y = target_location.y(); |
+ return result.Pass(); |
+} |
+ |
+} // namespace |
+ |
+// GestureStateChange ---------------------------------------------------------- |
+ |
+GestureStateChange::GestureStateChange() |
+ : chosen_gesture(GestureManager::kInvalidGestureId) { |
+} |
+ |
+GestureStateChange::~GestureStateChange() { |
+} |
+ |
+// ViewIterator ---------------------------------------------------------------- |
+ |
+// Used to iterate over a set of views. |
+class ViewIterator { |
+ public: |
+ explicit ViewIterator(const Views& views) |
+ : views_(views), current_(views_.begin()) {} |
+ |
+ // Advances to the next view. Returns true if there are no more views (at |
+ // the end). |
+ bool advance() { return ++current_ != views_.end(); } |
+ |
+ bool at_end() const { return current_ == views_.end(); } |
+ |
+ bool empty() const { return views_.empty(); } |
+ |
+ const ServerView* current() const { return *current_; } |
+ |
+ void reset_to_beginning() { current_ = views_.begin(); } |
+ |
+ void remove(const ServerView* view) { |
+ Views::iterator iter = std::find(views_.begin(), views_.end(), view); |
+ DCHECK(iter != views_.end()); |
+ if (iter == current_) { |
+ current_ = views_.erase(current_); |
+ } else if (!at_end()) { |
+ size_t index = current_ - views_.begin(); |
+ if (current_ > iter) |
+ index--; |
+ views_.erase(iter); |
+ current_ = views_.begin() + index; |
+ } else { |
+ views_.erase(iter); |
+ current_ = views_.end(); |
+ } |
+ } |
+ |
+ bool contains(const ServerView* view) const { |
+ return std::find(views_.begin(), views_.end(), view) != views_.end(); |
+ } |
+ |
+ private: |
+ Views views_; |
+ Views::iterator current_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ViewIterator); |
+}; |
+ |
+// PointerAndView -------------------------------------------------------------- |
+ |
+struct GestureManager::PointerAndView { |
+ PointerAndView(); |
+ PointerAndView(Pointer* pointer, const ServerView* view); |
+ |
+ // Compares two PointerAndView instances based on pointer id, then view id. |
+ // This is really only interesting for unit tests so that they get a known |
+ // order of events. |
+ bool operator<(const PointerAndView& other) const; |
+ |
+ Pointer* pointer; |
+ const ServerView* view; |
+}; |
+ |
+// Gesture --------------------------------------------------------------------- |
+ |
+// Gesture maintains the set of pointers and views it is attached to. |
+class GestureManager::Gesture { |
+ public: |
+ enum State { STATE_INITIAL, STATE_CANCELED, STATE_CHOSEN }; |
+ |
+ explicit Gesture(uint32_t id); |
+ ~Gesture(); |
+ |
+ uint32_t id() const { return id_; } |
+ |
+ void Attach(Pointer* pointer, const ServerView* view); |
+ void Detach(Pointer* pointer, const ServerView* view); |
+ |
+ void set_state(State state) { state_ = state; } |
+ State state() const { return state_; } |
+ |
+ const std::set<PointerAndView>& pointers_and_views() const { |
+ return pointers_and_views_; |
+ } |
+ |
+ private: |
+ const uint32_t id_; |
+ State state_; |
+ std::set<PointerAndView> pointers_and_views_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Gesture); |
+}; |
+ |
+GestureManager::Gesture::Gesture(uint32_t id) : id_(id), state_(STATE_INITIAL) { |
+} |
+ |
+GestureManager::Gesture::~Gesture() { |
+} |
+ |
+void GestureManager::Gesture::Attach(Pointer* pointer, const ServerView* view) { |
+ pointers_and_views_.insert(PointerAndView(pointer, view)); |
+} |
+ |
+void GestureManager::Gesture::Detach(Pointer* pointer, const ServerView* view) { |
+ pointers_and_views_.erase(PointerAndView(pointer, view)); |
+} |
+ |
+// Pointer --------------------------------------------------------------------- |
+ |
+// Pointer manages the state associated with a particular pointer from the time |
+// of the POINTER_DOWN to the time of the POINTER_UP (or POINTER_CANCEL). This |
+// state includes a mapping from view to the set of gestures the view is |
+// interested in. It also manages choosing gestures at the appropriate point as |
+// well as which view to dispatch to and the events to dispatch. |
+// See description in GestureManager for more. |
+class GestureManager::Pointer { |
+ public: |
+ Pointer(GestureManager* gesture_manager, |
+ int32_t pointer_id, |
+ const mojo::Event& event, |
+ const Views& views); |
+ ~Pointer(); |
+ |
+ int32_t pointer_id() const { return pointer_id_; } |
+ bool was_chosen_or_canceled() const { return was_chosen_or_canceled_; } |
+ |
+ // Sets the set of gestures for this pointer. |
+ void SetGestures(const ServerView* view, |
+ uint32_t chosen_gesture_id, |
+ const std::set<uint32_t>& possible_gesture_ids, |
+ const std::set<uint32_t>& canceled_ids); |
+ |
+ // Called when a Gesture we contain has been canceled. |
+ void GestureCanceled(Gesture* gesture); |
+ |
+ // Called when a Gesture we contain has been chosen. |
+ void GestureChosen(Gesture* gesture, const ServerView* view); |
+ |
+ // Process a move or up event. This may delay processing if we're waiting for |
+ // previous results. |
+ void ProcessEvent(const mojo::Event& event); |
+ |
+ private: |
+ // Corresponds to the type of event we're dispatching. |
+ enum Phase { |
+ // We're dispatching the initial down. |
+ PHASE_DOWN, |
+ |
+ // We're dispatching a move. |
+ PHASE_MOVE, |
+ }; |
+ |
+ // Sends the event for the current phase to the delegate. |
+ void ForwardCurrentEvent(); |
+ |
+ // Moves |pending_event_| to |current_event_| and notifies the delegate. |
+ void MovePendingToCurrentAndProcess(); |
+ |
+ // If |was_chosen_or_canceled_| is false and there is only one possible |
+ // gesture and it is in the initial state, choose it. Otherwise do nothing. |
+ void ChooseGestureIfPossible(); |
+ |
+ bool ScheduleDeleteIfNecessary(); |
+ |
+ GestureManager* gesture_manager_; |
+ const int32_t pointer_id_; |
+ Phase phase_; |
+ |
+ // Used to iterate over the set of views that potentially have gestures. |
+ ViewIterator view_iter_; |
+ |
+ // Maps from the view to the set of possible gestures for the view. |
+ std::map<const ServerView*, std::set<Gesture*>> view_to_gestures_; |
+ |
+ Gesture* chosen_gesture_; |
+ |
+ bool was_chosen_or_canceled_; |
+ |
+ // The event we're processing. When initially created this is the supplied |
+ // down event. When in PHASE_MOVE this is a move event. |
+ mojo::EventPtr current_event_; |
+ |
+ // Incoming events (move or up) are added here while while waiting for |
+ // responses. |
+ mojo::EventPtr pending_event_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Pointer); |
+}; |
+ |
+GestureManager::Pointer::Pointer(GestureManager* gesture_manager, |
+ int32_t pointer_id, |
+ const mojo::Event& event, |
+ const Views& views) |
+ : gesture_manager_(gesture_manager), |
+ pointer_id_(pointer_id), |
+ phase_(PHASE_DOWN), |
+ view_iter_(views), |
+ chosen_gesture_(nullptr), |
+ was_chosen_or_canceled_(false), |
+ current_event_(event.Clone()) { |
+ ForwardCurrentEvent(); |
+} |
+ |
+GestureManager::Pointer::~Pointer() { |
+ for (auto& pair : view_to_gestures_) { |
+ for (Gesture* gesture : pair.second) |
+ gesture_manager_->DetachGesture(gesture, this, pair.first); |
+ } |
+} |
+ |
+void GestureManager::Pointer::SetGestures( |
+ const ServerView* view, |
+ uint32_t chosen_gesture_id, |
+ const std::set<uint32_t>& possible_gesture_ids, |
+ const std::set<uint32_t>& canceled_gesture_ids) { |
+ if (!view_iter_.contains(view)) { |
+ // We don't know about this view. |
+ return; |
+ } |
+ |
+ // True if this is the view we're waiting for a response from. |
+ const bool was_waiting_on = |
+ (!was_chosen_or_canceled_ && |
+ (!view_iter_.at_end() && view_iter_.current() == view)); |
+ |
+ if (possible_gesture_ids.empty()) { |
+ // The view no longer wants to be notified. |
+ for (Gesture* gesture : view_to_gestures_[view]) |
+ gesture_manager_->DetachGesture(gesture, this, view); |
+ view_to_gestures_.erase(view); |
+ view_iter_.remove(view); |
+ if (view_iter_.empty()) { |
+ gesture_manager_->PointerHasNoGestures(this); |
+ // WARNING: we've been deleted. |
+ return; |
+ } |
+ } else { |
+ if (was_waiting_on) |
+ view_iter_.advance(); |
+ |
+ Gesture* to_choose = nullptr; |
+ std::set<Gesture*> gestures; |
+ for (auto gesture_id : possible_gesture_ids) { |
+ Gesture* gesture = gesture_manager_->GetGesture(view, gesture_id); |
+ gesture_manager_->AttachGesture(gesture, this, view); |
+ gestures.insert(gesture); |
+ if (gesture->state() == Gesture::STATE_CHOSEN && |
+ !was_chosen_or_canceled_) { |
+ to_choose = gesture; |
+ } |
+ } |
+ |
+ // Give preference to the supplied |chosen_gesture_id|. |
+ if (!was_chosen_or_canceled_ && chosen_gesture_id != kInvalidGestureId) { |
+ Gesture* gesture = gesture_manager_->GetGesture(view, chosen_gesture_id); |
+ if (gesture->state() != Gesture::STATE_CANCELED) |
+ to_choose = gesture; |
+ |
+ DCHECK(possible_gesture_ids.count(gesture->id())); |
+ gesture_manager_->AttachGesture(gesture, this, view); |
+ } |
+ |
+ // Tell GestureManager of any Gestures we're no longer associated with. |
+ std::set<Gesture*> removed_gestures; |
+ std::set_difference( |
+ view_to_gestures_[view].begin(), view_to_gestures_[view].end(), |
+ gestures.begin(), gestures.end(), |
+ std::inserter(removed_gestures, removed_gestures.begin())); |
+ view_to_gestures_[view].swap(gestures); |
+ for (Gesture* gesture : removed_gestures) |
+ gesture_manager_->DetachGesture(gesture, this, view); |
+ |
+ if (chosen_gesture_ && removed_gestures.count(chosen_gesture_)) |
+ chosen_gesture_ = nullptr; |
+ |
+ if (to_choose) { |
+ gesture_manager_->ChooseGesture(to_choose, this, view); |
+ } else { |
+ // Choosing a gesture implicitly cancels all other gestures. If we didn't |
+ // choose a gesture we need to update the state of any newly added |
+ // gestures. |
+ for (Gesture* gesture : gestures) { |
+ if (gesture != chosen_gesture_ && |
+ (was_chosen_or_canceled_ || |
+ canceled_gesture_ids.count(gesture->id()))) { |
+ gesture_manager_->CancelGesture(gesture, this, view); |
+ } |
+ } |
+ } |
+ } |
+ |
+ if (was_waiting_on && !was_chosen_or_canceled_) { |
+ if (view_iter_.at_end()) { |
+ if (ScheduleDeleteIfNecessary()) |
+ return; |
+ // If we're got all the responses, check if there is only one valid |
+ // gesture. |
+ ChooseGestureIfPossible(); |
+ if (!was_chosen_or_canceled_) { |
+ // There is more than one valid gesture and none chosen. Continue |
+ // synchronous dispatch of move events. |
+ phase_ = PHASE_MOVE; |
+ MovePendingToCurrentAndProcess(); |
+ } |
+ } else { |
+ ForwardCurrentEvent(); |
+ } |
+ } else if (!was_chosen_or_canceled_ && phase_ != PHASE_DOWN) { |
+ // We weren't waiting on this view but we're in the move phase. The set of |
+ // gestures may have changed such that we only have one valid gesture. Check |
+ // for that. |
+ ChooseGestureIfPossible(); |
+ } |
+} |
+ |
+void GestureManager::Pointer::GestureCanceled(Gesture* gesture) { |
+ if (was_chosen_or_canceled_ && gesture == chosen_gesture_) { |
+ chosen_gesture_ = nullptr; |
+ // No need to cancel other gestures as they are already canceled by virtue |
+ // of us having been chosen. |
+ } else if (!was_chosen_or_canceled_ && phase_ == PHASE_MOVE) { |
+ ChooseGestureIfPossible(); |
+ } |
+} |
+ |
+void GestureManager::Pointer::GestureChosen(Gesture* gesture, |
+ const ServerView* view) { |
+ DCHECK(!was_chosen_or_canceled_); |
+ was_chosen_or_canceled_ = true; |
+ chosen_gesture_ = gesture; |
+ for (auto& pair : view_to_gestures_) { |
+ for (Gesture* g : pair.second) { |
+ if (g != gesture) |
+ gesture_manager_->CancelGesture(g, this, pair.first); |
+ } |
+ } |
+ |
+ while (!view_iter_.at_end()) { |
+ ForwardCurrentEvent(); |
+ view_iter_.advance(); |
+ } |
+ if (ScheduleDeleteIfNecessary()) |
+ return; |
+ phase_ = PHASE_MOVE; |
+ MovePendingToCurrentAndProcess(); |
+} |
+ |
+void GestureManager::Pointer::ProcessEvent(const mojo::Event& event) { |
+ // |event| is either a move or up. In either case it has the new coordinates |
+ // and is safe to replace the existing one with. |
+ pending_event_ = event.Clone(); |
+ if (was_chosen_or_canceled_) { |
+ MovePendingToCurrentAndProcess(); |
+ } else if (view_iter_.at_end()) { |
+ view_iter_.reset_to_beginning(); |
+ MovePendingToCurrentAndProcess(); |
+ } |
+ // The else case is we are waiting on a response from a view before we |
+ // continue dispatching. When we get the response for the last view in the |
+ // stack we'll move pending to current and start dispatching it. |
+} |
+ |
+void GestureManager::Pointer::ForwardCurrentEvent() { |
+ DCHECK(!view_iter_.at_end()); |
+ const ServerView* view = view_iter_.current(); |
+ gesture_manager_->delegate_->ProcessEvent( |
+ view, CloneEventForView(*current_event_, view), was_chosen_or_canceled_); |
+} |
+ |
+void GestureManager::Pointer::MovePendingToCurrentAndProcess() { |
+ if (!pending_event_.get()) { |
+ current_event_ = nullptr; |
+ return; |
+ } |
+ current_event_ = pending_event_.Pass(); |
+ view_iter_.reset_to_beginning(); |
+ ForwardCurrentEvent(); |
+ if (was_chosen_or_canceled_) { |
+ while (view_iter_.advance()) |
+ ForwardCurrentEvent(); |
+ if (ScheduleDeleteIfNecessary()) |
+ return; |
+ current_event_ = nullptr; |
+ } |
+} |
+ |
+void GestureManager::Pointer::ChooseGestureIfPossible() { |
+ if (was_chosen_or_canceled_) |
+ return; |
+ |
+ Gesture* gesture_to_choose = nullptr; |
+ const ServerView* view = nullptr; |
+ for (auto& pair : view_to_gestures_) { |
+ for (Gesture* gesture : pair.second) { |
+ if (gesture->state() == Gesture::STATE_INITIAL) { |
+ if (gesture_to_choose) |
+ return; |
+ view = pair.first; |
+ gesture_to_choose = gesture; |
+ } |
+ } |
+ } |
+ if (view) |
+ gesture_manager_->ChooseGesture(gesture_to_choose, this, view); |
+} |
+ |
+bool GestureManager::Pointer::ScheduleDeleteIfNecessary() { |
+ if (current_event_ && |
+ (current_event_->action == mojo::EVENT_TYPE_POINTER_UP || |
+ current_event_->action == mojo::EVENT_TYPE_POINTER_CANCEL)) { |
+ gesture_manager_->ScheduleDelete(this); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+// ScheduledDeleteProcessor --------------------------------------------------- |
+ |
+class GestureManager::ScheduledDeleteProcessor { |
+ public: |
+ explicit ScheduledDeleteProcessor(GestureManager* manager) |
+ : manager_(manager) {} |
+ |
+ ~ScheduledDeleteProcessor() { manager_->pointers_to_delete_.clear(); } |
+ |
+ private: |
+ GestureManager* manager_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ScheduledDeleteProcessor); |
+}; |
+ |
+// PointerAndView -------------------------------------------------------------- |
+ |
+GestureManager::PointerAndView::PointerAndView() |
+ : pointer(nullptr), view(nullptr) { |
+} |
+ |
+GestureManager::PointerAndView::PointerAndView(Pointer* pointer, |
+ const ServerView* view) |
+ : pointer(pointer), view(view) { |
+} |
+ |
+bool GestureManager::PointerAndView::operator<( |
+ const PointerAndView& other) const { |
+ if (other.pointer->pointer_id() == pointer->pointer_id()) |
+ return view->id().connection_id < other.view->id().connection_id; |
+ return pointer->pointer_id() < other.pointer->pointer_id(); |
+} |
+ |
+// GestureManager -------------------------------------------------------------- |
+ |
+// static |
+const uint32_t GestureManager::kInvalidGestureId = 0u; |
+ |
+GestureManager::GestureManager(GestureManagerDelegate* delegate, |
+ const ServerView* root) |
+ : delegate_(delegate), root_view_(root) { |
+} |
+ |
+GestureManager::~GestureManager() { |
+ // Explicitly delete the pointers first as this may result in calling back to |
+ // us to cleanup and delete gestures. |
+ active_pointers_.clear(); |
+} |
+ |
+bool GestureManager::ProcessEvent(const mojo::Event& event) { |
+ if (!event.pointer_data) |
+ return false; |
+ |
+ ScheduledDeleteProcessor delete_processor(this); |
+ const gfx::Point location(static_cast<int>(event.pointer_data->x), |
+ static_cast<int>(event.pointer_data->y)); |
+ switch (event.action) { |
+ case mojo::EVENT_TYPE_POINTER_DOWN: { |
+ if (GetPointerById(event.pointer_data->pointer_id)) { |
+ DVLOG(1) << "received more than one down for a pointer without a " |
+ << "corresponding up, id=" << event.pointer_data->pointer_id; |
+ NOTREACHED(); |
+ return true; |
+ } |
+ |
+ const ServerView* deepest = FindDeepestVisibleView(root_view_, location); |
+ Views targets(GetTouchTargets(deepest)); |
+ if (targets.empty()) |
+ return true; |
+ |
+ scoped_ptr<Pointer> pointer( |
+ new Pointer(this, event.pointer_data->pointer_id, event, targets)); |
+ active_pointers_.push_back(pointer.Pass()); |
+ return true; |
+ } |
+ |
+ case mojo::EVENT_TYPE_POINTER_CANCEL: |
+ case mojo::EVENT_TYPE_POINTER_MOVE: |
+ case mojo::EVENT_TYPE_POINTER_UP: { |
+ Pointer* pointer = GetPointerById(event.pointer_data->pointer_id); |
+ // We delete a pointer when it has no gestures, so it's possible to get |
+ // here with no gestures. Additionally there is no need to explicitly |
+ // delete |pointer| as it'll tell us when it's ready to be deleted. |
+ if (pointer) |
+ pointer->ProcessEvent(event); |
+ return true; |
+ } |
+ |
+ default: |
+ break; |
+ } |
+ return false; |
+} |
+ |
+scoped_ptr<ChangeMap> GestureManager::SetGestures( |
+ const ServerView* view, |
+ int32_t pointer_id, |
+ uint32_t chosen_gesture_id, |
+ const std::set<uint32_t>& possible_gesture_ids, |
+ const std::set<uint32_t>& canceled_gesture_ids) { |
+ // TODO(sky): caller should validate ids and make sure possible contains |
+ // canceled and chosen. |
+ DCHECK(!canceled_gesture_ids.count(kInvalidGestureId)); |
+ DCHECK(!possible_gesture_ids.count(kInvalidGestureId)); |
+ DCHECK(chosen_gesture_id == kInvalidGestureId || |
+ possible_gesture_ids.count(chosen_gesture_id)); |
+ DCHECK(chosen_gesture_id == kInvalidGestureId || |
+ !canceled_gesture_ids.count(chosen_gesture_id)); |
+ ScheduledDeleteProcessor delete_processor(this); |
+ Pointer* pointer = GetPointerById(pointer_id); |
+ current_change_.reset(new ChangeMap); |
+ if (pointer) { |
+ pointer->SetGestures(view, chosen_gesture_id, possible_gesture_ids, |
+ canceled_gesture_ids); |
+ } |
+ return current_change_.Pass(); |
+} |
+ |
+GestureManager::Pointer* GestureManager::GetPointerById(int32_t pointer_id) { |
+ for (Pointer* pointer : active_pointers_) { |
+ if (pointer->pointer_id() == pointer_id) |
+ return pointer; |
+ } |
+ return nullptr; |
+} |
+ |
+void GestureManager::PointerHasNoGestures(Pointer* pointer) { |
+ auto iter = |
+ std::find(active_pointers_.begin(), active_pointers_.end(), pointer); |
+ CHECK(iter != active_pointers_.end()); |
+ active_pointers_.erase(iter); |
+} |
+ |
+GestureManager::Gesture* GestureManager::GetGesture(const ServerView* view, |
+ uint32_t gesture_id) { |
+ GestureAndConnectionId gesture_and_connection_id = |
+ MakeGestureAndConnectionId(view, gesture_id); |
+ Gesture* gesture = gesture_map_[gesture_and_connection_id]; |
+ if (!gesture) { |
+ gesture = new Gesture(gesture_id); |
+ gesture_map_[gesture_and_connection_id] = gesture; |
+ } |
+ return gesture; |
+} |
+ |
+void GestureManager::AttachGesture(Gesture* gesture, |
+ Pointer* pointer, |
+ const ServerView* view) { |
+ gesture->Attach(pointer, view); |
+} |
+ |
+void GestureManager::DetachGesture(Gesture* gesture, |
+ Pointer* pointer, |
+ const ServerView* view) { |
+ gesture->Detach(pointer, view); |
+ if (gesture->pointers_and_views().empty()) { |
+ gesture_map_.erase(MakeGestureAndConnectionId(view, gesture->id())); |
+ delete gesture; |
+ } |
+} |
+ |
+void GestureManager::CancelGesture(Gesture* gesture, |
+ Pointer* pointer, |
+ const ServerView* view) { |
+ if (gesture->state() == Gesture::STATE_CANCELED) |
+ return; |
+ |
+ gesture->set_state(Gesture::STATE_CANCELED); |
+ for (auto& pointer_and_view : gesture->pointers_and_views()) { |
+ (*current_change_)[pointer_and_view.view].canceled_gestures.insert( |
+ gesture->id()); |
+ if (pointer_and_view.pointer != pointer) |
+ pointer_and_view.pointer->GestureCanceled(gesture); |
+ } |
+} |
+ |
+void GestureManager::ChooseGesture(Gesture* gesture, |
+ Pointer* pointer, |
+ const ServerView* view) { |
+ if (gesture->state() == Gesture::STATE_CHOSEN) { |
+ // This happens when |pointer| is supplied a gesture that is already |
+ // chosen. |
+ DCHECK((*current_change_)[view].chosen_gesture == kInvalidGestureId || |
+ (*current_change_)[view].chosen_gesture == gesture->id()); |
+ (*current_change_)[view].chosen_gesture = gesture->id(); |
+ pointer->GestureChosen(gesture, view); |
+ } else { |
+ gesture->set_state(Gesture::STATE_CHOSEN); |
+ for (auto& pointer_and_view : gesture->pointers_and_views()) { |
+ DCHECK((*current_change_)[pointer_and_view.view].chosen_gesture == |
+ kInvalidGestureId || |
+ (*current_change_)[pointer_and_view.view].chosen_gesture == |
+ gesture->id()); |
+ (*current_change_)[pointer_and_view.view].chosen_gesture = gesture->id(); |
+ pointer_and_view.pointer->GestureChosen(gesture, view); |
+ } |
+ } |
+} |
+ |
+void GestureManager::ScheduleDelete(Pointer* pointer) { |
+ auto iter = |
+ std::find(active_pointers_.begin(), active_pointers_.end(), pointer); |
+ if (iter != active_pointers_.end()) { |
+ active_pointers_.weak_erase(iter); |
+ pointers_to_delete_.push_back(pointer); |
+ } |
+} |
+ |
+} // namespace view_manager |