]> git.tdb.fi Git - libs/gui.git/blobdiff - source/input/gesturedetector.cpp
Put feature control macros before any headers
[libs/gui.git] / source / input / gesturedetector.cpp
index aecfa81bb3b7ab88fb4602956f6ca11368a74634..08232cf8f0fecf023e98fe43b452d2bf9135ee0e 100644 (file)
@@ -1,6 +1,7 @@
+#define _USE_MATH_DEFINES
+#include "gesturedetector.h"
 #include <cmath>
 #include <sigc++/bind_return.h>
-#include "gesturedetector.h"
 #include "touchscreen.h"
 
 using namespace std;
@@ -9,10 +10,8 @@ namespace Msp {
 namespace Input {
 
 GestureDetector::GestureDetector(Touchscreen &ts):
-       touchscreen(ts),
-       current_gesture(GESTURE_NONE),
-       active_points(0),
-       invalid_gesture(false)
+       Device(UNSPECIFIED),
+       touchscreen(ts)
 {
        name = "Gesture";
 
@@ -26,10 +25,18 @@ GestureDetector::GestureDetector(Touchscreen &ts):
 
 string GestureDetector::get_button_name(unsigned btn) const
 {
-       if(btn==GESTURE_DRAG)
+       if(btn==GESTURE_TAP)
+               return "Tap";
+       else if(btn==GESTURE_TAP_2)
+               return "Two-finger tap";
+       else if(btn==GESTURE_TAP_3)
+               return "Three-finger tap";
+       else if(btn==GESTURE_DRAG)
                return "Drag";
        else if(btn==GESTURE_DRAG_2)
                return "Two-finger drag";
+       else if(btn==GESTURE_DRAG_3)
+               return "Three-finger drag";
        else if(btn==GESTURE_PINCH)
                return "Pinch";
        else if(btn==GESTURE_ROTATE)
@@ -45,7 +52,9 @@ string GestureDetector::get_axis_name(unsigned axis) const
        else if(axis==1)
                return "Y";
        else if(axis==2)
-               return "Progress";
+               return "Delta X";
+       else if(axis==3)
+               return "Delta Y";
        else
                return Device::get_axis_name(axis);
 }
@@ -62,6 +71,16 @@ void GestureDetector::touch_down(unsigned btn)
        p.x = 0;
        p.y = 0;
        p.threshold_exceeded = false;
+
+       if(current_gesture==GESTURE_NONE && !invalid_gesture)
+       {
+               if(btn==0)
+                       pending_tap = GESTURE_TAP;
+               else if(btn==1)
+                       pending_tap = GESTURE_TAP_2;
+               else if(btn==2)
+                       pending_tap = GESTURE_TAP_3;
+       }
 }
 
 void GestureDetector::touch_up(unsigned btn)
@@ -74,7 +93,7 @@ void GestureDetector::touch_up(unsigned btn)
        p.y += p.down_y;
        p.down = false;
 
-       if(active_points&(1<<btn))
+       if(btn<gesture_points(current_gesture))
                end_gesture();
 
        if(current_gesture==GESTURE_NONE)
@@ -83,6 +102,15 @@ void GestureDetector::touch_up(unsigned btn)
                invalid_gesture = false;
                for(unsigned i=0; (i<MAX_POINTS && !invalid_gesture); ++i)
                        invalid_gesture = points[i].down;
+
+               if(!invalid_gesture && pending_tap!=GESTURE_NONE)
+               {
+                       set_gesture_location(gesture_points(pending_tap));
+                       set_button_state(pending_tap, true, true);
+                       set_button_state(pending_tap, false, true);
+
+                       pending_tap = GESTURE_NONE;
+               }
        }
 }
 
@@ -102,11 +130,14 @@ void GestureDetector::touch_move(unsigned axis, float value, float)
        if(p.down)
        {
                if(p.x*p.x/threshold_x_sq+p.y*p.y/threshold_y_sq>=1)
+               {
                        p.threshold_exceeded = true;
+                       pending_tap = GESTURE_NONE;
+               }
 
                if(current_gesture==GESTURE_NONE && !invalid_gesture)
                        start_gesture();
-               else if(active_points&(1<<i))
+               else if(i<gesture_points(current_gesture))
                        update_progress();
        }
 }
@@ -117,16 +148,31 @@ void GestureDetector::start_gesture()
        if(!p.down)
                return;
 
-       /* At least one point needs to have moved more than the threshold to start
-       the gesture. */
-       bool threshold_exceeded = false;
-       for(unsigned i=0; (i<MAX_POINTS && !threshold_exceeded); ++i)
-               threshold_exceeded = (points[i].down && points[i].threshold_exceeded);
+       /* All held points need to have moved more than the threshold to start the
+       gesture. */
+       bool threshold_exceeded = true;
+       for(unsigned i=0; (i<MAX_POINTS && threshold_exceeded); ++i)
+               if(points[i].down && !points[i].threshold_exceeded)
+                       threshold_exceeded = false;
        if(!threshold_exceeded)
                return;
 
        invalid_gesture = false;
-       if(points[1].down)
+       if(points[2].down)
+       {
+               bool same_direction = true;
+               for(unsigned i=0; (same_direction && i<2); ++i)
+                       for(unsigned j=i+1; (same_direction && j<3); ++j)
+                       {
+                               TouchPoint &pi = points[i];
+                               TouchPoint &pj = points[j];
+                               same_direction = ((pi.x*pj.x+pi.y*pj.y)>2*abs(pi.x*pj.y-pi.y*pj.x));
+                       }
+
+               if(same_direction)
+                       current_gesture = GESTURE_DRAG_3;
+       }
+       else if(points[1].down)
        {
                TouchPoint &p2 = points[1];
                float ddx = p.down_x-p2.down_x;
@@ -146,47 +192,55 @@ void GestureDetector::start_gesture()
                else if((p.x*p2.x+p.y*p2.y)>2*abs(p.x*p2.y-p.y*p2.x))
                        // If both points moved in the same direction, it's a two-finger drag.
                        current_gesture = GESTURE_DRAG_2;
-
-               if(current_gesture!=GESTURE_NONE)
-               {
-                       active_points = 3;
-                       set_axis_value(0, (p.down_x+p2.down_x)/2, true);
-                       set_axis_value(1, (p.down_y+p2.down_y)/2, true);
-               }
        }
        else
-       {
                current_gesture = GESTURE_DRAG;
-               active_points = 1;
-               set_axis_value(0, p.down_x, true);
-               set_axis_value(1, p.down_y, true);
-       }
-
-       update_progress();
 
        if(current_gesture!=GESTURE_NONE)
+       {
+               set_gesture_location(gesture_points(current_gesture));
+               update_progress();
                set_button_state(current_gesture, true, true);
+       }
        else
                invalid_gesture = true;
 }
 
-void GestureDetector::update_progress()
+void GestureDetector::set_gesture_location(unsigned n_points)
 {
-       TouchPoint &p = points[0];
-
-       if(current_gesture==GESTURE_DRAG)
+       float x = 0;
+       float y = 0;
+       for(unsigned i=0; i<n_points; ++i)
        {
-               set_axis_value(2, p.x, true);
-               set_axis_value(3, p.y, true);
+               x += points[i].down_x;
+               y += points[i].down_y;
        }
-       else if(current_gesture==GESTURE_DRAG_2)
+
+       set_axis_value(0, x/n_points, true);
+       set_axis_value(1, y/n_points, true);
+}
+
+void GestureDetector::set_gesture_delta(unsigned n_points)
+{
+       float x = 0;
+       float y = 0;
+       for(unsigned i=0; i<n_points; ++i)
        {
-               TouchPoint &p2 = points[1];
-               set_axis_value(2, (p.x+p2.x)/2, true);
-               set_axis_value(3, (p.y+p2.y)/2, true);
+               x += points[i].x;
+               y += points[i].y;
        }
+
+       set_axis_value(2, x/n_points, true);
+       set_axis_value(3, y/n_points, true);
+}
+
+void GestureDetector::update_progress()
+{
+       if(current_gesture>=GESTURE_DRAG && current_gesture<=GESTURE_DRAG_3)
+               set_gesture_delta(gesture_points(current_gesture));
        else if(current_gesture==GESTURE_PINCH || current_gesture==GESTURE_ROTATE)
        {
+               TouchPoint &p = points[0];
                TouchPoint &p2 = points[1];
                /* Pinch progress is the ratio between the current distance of the points
                and their distance when they were pressed. */
@@ -212,7 +266,7 @@ void GestureDetector::end_gesture()
        set_button_state(current_gesture, false, true);
        set_axis_value(2, 0, false);
        current_gesture = GESTURE_NONE;
-       active_points = 0;
+       pending_tap = GESTURE_NONE;
 }
 
 void GestureDetector::window_resized(unsigned w, unsigned h)
@@ -227,8 +281,27 @@ GestureDetector::TouchPoint::TouchPoint():
        down_x(0),
        down_y(0),
        x(0),
-       y(0)
+       y(0),
+       threshold_exceeded(false)
 { }
 
+
+unsigned gesture_points(Gesture gesture)
+{
+       switch(gesture)
+       {
+       case GESTURE_NONE: return 0;
+       case GESTURE_TAP: return 1;
+       case GESTURE_TAP_2: return 2;
+       case GESTURE_TAP_3: return 3;
+       case GESTURE_DRAG: return 1;
+       case GESTURE_DRAG_2: return 2;
+       case GESTURE_DRAG_3: return 3;
+       case GESTURE_PINCH: return 2;
+       case GESTURE_ROTATE: return 2;
+       default: throw invalid_argument("gesture_points");
+       }
+}
+
 } // namespace Input
 } // namespace Msp