blob: 93354e9583bcbc64d801880455971acb63427700 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_LIB_UI_INPUT_GESTURE_DETECTOR_H_
#define SRC_LIB_UI_INPUT_GESTURE_DETECTOR_H_
#include <fuchsia/ui/input/cpp/fidl.h>
#include <map>
#include "src/lib/fxl/memory/weak_ptr.h"
#include "src/lib/ui/input/gesture.h"
#include "src/ui/lib/glm_workaround/glm_workaround.h"
namespace input {
// A basic higher-level gesture classifier. This gesture detector classifies
// gestures along two dimensions: "tap type" (number of touch points, mouse
// buttons, or stylus usage) and tap vs. drag. See |Interaction| for details.
//
// Gestures are handled independently for each input device.
class GestureDetector {
public:
// This default tends to be reasonable if input units are in pixels.
static constexpr float kDefaultDragThreshold = 8;
// Tap type, in terms of number of pointers used, or mouse button.
//
// Roughly, 1 can be considered the primary interaction mode (one finger,
// primary mouse button, or stylus touch), 2 can be considered secondary (two
// fingers, secondary mouse button, or stylus button/inverted touch), and so
// on.
//
// TODO(fxbug.dev/17287): time-based taps
using TapType = int32_t;
// Represents a single gesture interaction. This is a higher level event-based
// abstraction of |Gesture| that adds tap classification (provided by
// |GestureDetector|). Subclasses override methods (and the destructor) to
// handle events. The default implementation does nothing.
//
// Broadly, this handles two classes of gestures: taps and (multi)drags. Taps
// are defined as the addition of touch points and their subsequent removal
// without appreciable movement from any of the pointers. Multidrags cover
// all other cases.
// TODO(fxbug.dev/17287): This may need redefinition if time-based taps are
// supported at this level.
//
// In the interest of reducing input latency, a multi-pointer tap is
// considered "committed" as soon as any pointer is released. However, it can
// subsequently evolve into a multidrag if any remaining pointer is moved
// past the drag threshold or any new pointers are added.
//
// The |Interaction| is destroyed once the interaction ends, when all
// pointers are removed.
// TODO(fxbug.dev/17287): This may happen after a timeout once time-based taps are
// supported.
class Interaction {
public:
virtual ~Interaction();
// Called when the first pointer comes down.
virtual void OnTapBegin(const glm::vec2& coordinate, TapType tap_type);
// Called when the type of tap has changed, either due to more mouse buttons
// or more touch points. As a tap evolves, |tap_type| can increase but not
// decrease, as any release signifies the end of the tap.
virtual void OnTapUpdate(TapType tap_type);
// Called when any pointer involved in a tap comes up. At this point, the
// tap is considered "committed". However, it may subsequently evolve into a
// multidrag if any remaining pointer is moved past the drag threshold or
// any new pointers are added.
//
// TODO(fxbug.dev/18121, fxbug.dev/17287): This definition may change significantly in the face
// of pluggable tap classification.
virtual void OnTapCommit();
// Called for a multipoint drag gesture. Unlike in a tap gesture, |tap_type|
// here may decrease as fingers are removed or as buttons are released. Once
// this is called, this interaction is no longer considered a tap, and no
// subsequent tap-related methods will be called.
virtual void OnMultidrag(TapType tap_type, const Gesture::Delta& delta);
};
// Constructs |Interaction| subclass instances that will represent
// interactions detected by a |GestureDetector|.
class Delegate {
public:
virtual ~Delegate();
// Factory function for |Interaction| implementations. |gesture| is the
// underlying gesture and will outlive the interaction.
virtual std::unique_ptr<Interaction> BeginInteraction(const Gesture* gesture) = 0;
};
// If any touch point deviates |drag_threshold| from its origin, the input is
// considered a multidrag.
GestureDetector(Delegate* delegate, float drag_threshold = kDefaultDragThreshold);
void OnPointerEvent(fuchsia::ui::input::PointerEvent event);
// Clears all tracked devices and interactions from this detector.
void Reset() { devices_.clear(); }
private:
using DeviceId = uint32_t;
struct DevicePointerState {
DevicePointerState();
Gesture gesture;
std::unique_ptr<Interaction> interaction;
std::map<Gesture::PointerId, glm::vec2> origins;
// While an interaction can be classified as a tap, this tracks the tap
// type, > 0, nondecreasing. Once a tap is committed, this becomes negative.
// If this interaction becomes a multidrag, this is set to 0. Only positive
// tap types are ever exposed to the client.
//
// Example sequences:
// 1 -> 2 -> -2: two-finger tap
// 1 -> 0: one-finger drag
// 1 -> 2 -> -2 -> 0: two-finger tap, committed (one released), subsequently
// dragged
TapType tap_type;
Gesture::Delta pending_delta;
// The delegate implementation may choose to reset or destroy the |GestureDetector| in any of
// its methods, so any time we call out to the delegate, we should guard subsequent execution
// with a weak pointer to the state.
fxl::WeakPtr<DevicePointerState> GetWeakPtr();
private:
fxl::WeakPtrFactory<DevicePointerState> weak_ptr_factory_;
};
Delegate* const delegate_;
std::map<DeviceId, DevicePointerState> devices_;
float drag_threshold_squared_;
};
} // namespace input
#endif // SRC_LIB_UI_INPUT_GESTURE_DETECTOR_H_