Merge branch 'gesture-events'
Integrate GSoC 2017 work by Prashant Kumar implementing support for gesture events. Closes https://github.com/wxWidgets/wxWidgets/pull/551 Closes https://github.com/wxWidgets/wxWidgets/pull/565
This commit is contained in:
@@ -227,6 +227,80 @@ static GList* gs_sizeRevalidateList;
|
||||
static bool gs_inSizeAllocate;
|
||||
#endif
|
||||
|
||||
#if GTK_CHECK_VERSION(3,14,0)
|
||||
#define wxGTK_HAS_GESTURES_SUPPORT
|
||||
#endif
|
||||
|
||||
#ifdef wxGTK_HAS_GESTURES_SUPPORT
|
||||
|
||||
#include "wx/hashmap.h"
|
||||
#include "wx/private/extfield.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Per-window data for gestures support.
|
||||
class wxWindowGesturesData
|
||||
{
|
||||
public:
|
||||
// This class has rather unusual "resurrectable" semantics: it is
|
||||
// initialized by the ctor as usual, but may then be uninitialized by
|
||||
// calling Free() and re-initialized again by calling Reinit().
|
||||
wxWindowGesturesData(wxWindow* win, GtkWidget *widget, int eventsMask)
|
||||
{
|
||||
Reinit(win, widget, eventsMask);
|
||||
}
|
||||
|
||||
~wxWindowGesturesData()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
void Reinit(wxWindow* win, GtkWidget *widget, int eventsMask);
|
||||
void Free();
|
||||
|
||||
unsigned int m_touchCount;
|
||||
unsigned int m_lastTouchTime;
|
||||
int m_gestureState;
|
||||
int m_allowedGestures;
|
||||
int m_activeGestures;
|
||||
wxPoint m_lastTouchPoint;
|
||||
GdkEventSequence* m_touchSequence;
|
||||
|
||||
GtkGesture* m_vertical_pan_gesture;
|
||||
GtkGesture* m_horizontal_pan_gesture;
|
||||
GtkGesture* m_zoom_gesture;
|
||||
GtkGesture* m_rotate_gesture;
|
||||
GtkGesture* m_long_press_gesture;
|
||||
};
|
||||
|
||||
WX_DECLARE_HASH_MAP(wxWindow*, wxWindowGesturesData*,
|
||||
wxPointerHash, wxPointerEqual,
|
||||
wxWindowGesturesMap);
|
||||
|
||||
typedef wxExternalField<wxWindow,
|
||||
wxWindowGesturesData,
|
||||
wxWindowGesturesMap> wxWindowGestures;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// This is true when the gesture has just started (currently used for pan gesture only)
|
||||
static bool gs_gestureStart = false;
|
||||
|
||||
// Last offset for the pan gesture, this is used to calculate deltas for pan gesture event
|
||||
static double gs_lastOffset = 0;
|
||||
|
||||
// Last scale provided by GTK
|
||||
static gdouble gs_lastScale = 1.0;
|
||||
|
||||
// This is used to set the angle when rotate gesture ends.
|
||||
static gdouble gs_lastAngle = 0;
|
||||
|
||||
// Last Zoom/Rotate gesture point
|
||||
static wxPoint gs_lastGesturePoint;
|
||||
|
||||
#endif // wxGTK_HAS_GESTURES_SUPPORT
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// debug
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -2624,6 +2698,10 @@ wxWindowGTK::~wxWindowGTK()
|
||||
gs_sizeRevalidateList = g_list_remove_all(gs_sizeRevalidateList, this);
|
||||
#endif
|
||||
|
||||
#ifdef wxGTK_HAS_GESTURES_SUPPORT
|
||||
wxWindowGestures::EraseForObject(this);
|
||||
#endif // wxGTK_HAS_GESTURES_SUPPORT
|
||||
|
||||
if (m_widget)
|
||||
{
|
||||
// Note that gtk_widget_destroy() does not destroy the widget, it just
|
||||
@@ -2825,6 +2903,676 @@ static gboolean source_dispatch(GSource*, GSourceFunc, void*)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef wxGTK_HAS_GESTURES_SUPPORT
|
||||
|
||||
// Currently used for Press and Tap gesture only
|
||||
enum GestureStates
|
||||
{
|
||||
begin = 1,
|
||||
update,
|
||||
end
|
||||
};
|
||||
|
||||
enum TrackedGestures
|
||||
{
|
||||
two_finger_tap = 0x0001,
|
||||
press_and_tap = 0x0002,
|
||||
horizontal_pan = 0x0004,
|
||||
vertical_pan = 0x0008
|
||||
};
|
||||
|
||||
static void
|
||||
pan_gesture_begin_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* WXUNUSED(win))
|
||||
{
|
||||
gs_gestureStart = true;
|
||||
|
||||
// Set it to 0, as this will be used to calculate the deltas for new pan gesture
|
||||
gs_lastOffset = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindowGTK* win)
|
||||
{
|
||||
wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
|
||||
if ( !data )
|
||||
return;
|
||||
|
||||
// Do not process horizontal pan, if there was no "pan" signal for it.
|
||||
if ( !(data->m_allowedGestures & horizontal_pan) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gdouble x, y;
|
||||
|
||||
if ( !gtk_gesture_get_point(gesture, sequence, &x, &y) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
data->m_allowedGestures &= ~horizontal_pan;
|
||||
|
||||
wxPanGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
event.SetGestureEnd();
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
vertical_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindowGTK* win)
|
||||
{
|
||||
wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
|
||||
if ( !data )
|
||||
return;
|
||||
|
||||
// Do not process vertical pan, if there was no "pan" signal for it.
|
||||
if ( !(data->m_allowedGestures & vertical_pan) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gdouble x, y;
|
||||
|
||||
if ( !gtk_gesture_get_point(gesture, sequence, &x, &y) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
data->m_allowedGestures &= ~vertical_pan;
|
||||
|
||||
wxPanGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
event.SetGestureEnd();
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
pan_gesture_callback(GtkGesture* gesture, GtkPanDirection direction, gdouble offset, wxWindowGTK* win)
|
||||
{
|
||||
// The function that retrieves the GdkEventSequence (which will further be used to get the gesture point)
|
||||
// should be called only when the gestrure is active
|
||||
if ( !gtk_gesture_is_active(gesture) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GdkEventSequence* sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture));
|
||||
|
||||
gdouble x, y;
|
||||
|
||||
if ( !gtk_gesture_get_point(gesture, sequence, &x, &y) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wxPanGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
|
||||
wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
|
||||
if ( !data )
|
||||
return;
|
||||
|
||||
// This is the difference between this and the last pan gesture event in the current sequence
|
||||
int delta = wxRound(offset - gs_lastOffset);
|
||||
|
||||
switch ( direction )
|
||||
{
|
||||
case GTK_PAN_DIRECTION_UP:
|
||||
data->m_allowedGestures |= vertical_pan;
|
||||
event.SetDelta(wxPoint(0, -delta));
|
||||
break;
|
||||
|
||||
case GTK_PAN_DIRECTION_DOWN:
|
||||
data->m_allowedGestures |= vertical_pan;
|
||||
event.SetDelta(wxPoint(0, delta));
|
||||
break;
|
||||
|
||||
case GTK_PAN_DIRECTION_RIGHT:
|
||||
data->m_allowedGestures |= horizontal_pan;
|
||||
event.SetDelta(wxPoint(delta, 0));
|
||||
break;
|
||||
|
||||
case GTK_PAN_DIRECTION_LEFT:
|
||||
data->m_allowedGestures |= horizontal_pan;
|
||||
event.SetDelta(wxPoint(-delta, 0));
|
||||
break;
|
||||
}
|
||||
|
||||
// Update gs_lastOffset
|
||||
gs_lastOffset = offset;
|
||||
|
||||
if ( gs_gestureStart )
|
||||
{
|
||||
event.SetGestureStart();
|
||||
gs_gestureStart = false;
|
||||
}
|
||||
|
||||
// Cancel press and tap gesture if it is not active during "pan" signal.
|
||||
if( !(data->m_activeGestures & press_and_tap) )
|
||||
{
|
||||
data->m_allowedGestures &= ~press_and_tap;
|
||||
}
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
zoom_gesture_callback(GtkGesture* gesture, gdouble scale, wxWindowGTK* win)
|
||||
{
|
||||
gdouble x, y;
|
||||
|
||||
if ( !gtk_gesture_get_bounding_box_center(gesture, &x, &y) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wxZoomGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
event.SetZoomFactor(scale);
|
||||
|
||||
wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
|
||||
if ( !data )
|
||||
return;
|
||||
|
||||
// Cancel "Two FInger Tap Event" if scale has changed
|
||||
if ( wxRound(scale * 1000) != wxRound(gs_lastScale * 1000) )
|
||||
{
|
||||
data->m_allowedGestures &= ~two_finger_tap;
|
||||
}
|
||||
|
||||
gs_lastScale = scale;
|
||||
|
||||
// Save this point because the point obtained through gtk_gesture_get_bounding_box_center()
|
||||
// in the "end" signal is not a zoom center
|
||||
gs_lastGesturePoint = wxPoint(wxRound(x), wxRound(y));
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
zoom_gesture_begin_callback(GtkGesture* gesture, GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* win)
|
||||
{
|
||||
gdouble x, y;
|
||||
|
||||
if ( !gtk_gesture_get_bounding_box_center(gesture, &x, &y) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gs_lastScale = 1.0;
|
||||
|
||||
wxZoomGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
event.SetGestureStart();
|
||||
|
||||
// Save this point because the point obtained through gtk_gesture_get_bounding_box_center()
|
||||
// in the "end" signal is not a zoom center
|
||||
gs_lastGesturePoint = wxPoint(wxRound(x), wxRound(y));
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
zoom_gesture_end_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* win)
|
||||
{
|
||||
wxZoomGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(gs_lastGesturePoint);
|
||||
event.SetGestureEnd();
|
||||
event.SetZoomFactor(gs_lastScale);
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
rotate_gesture_begin_callback(GtkGesture* gesture, GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* win)
|
||||
{
|
||||
gdouble x, y;
|
||||
|
||||
if ( !gtk_gesture_get_bounding_box_center(gesture, &x, &y) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wxRotateGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
event.SetGestureStart();
|
||||
|
||||
// Save this point because the point obtained through gtk_gesture_get_bounding_box_center()
|
||||
// in the "end" signal is not a rotation center
|
||||
gs_lastGesturePoint = wxPoint(wxRound(x), wxRound(y));
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
rotate_gesture_callback(GtkGesture* gesture, gdouble WXUNUSED(angle_delta), gdouble angle, wxWindowGTK* win)
|
||||
{
|
||||
gdouble x, y;
|
||||
|
||||
if ( !gtk_gesture_get_bounding_box_center(gesture, &x, &y) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wxRotateGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
|
||||
event.SetRotationAngle(angle);
|
||||
|
||||
// Save the angle to set it when the gesture ends.
|
||||
gs_lastAngle = angle;
|
||||
|
||||
// Save this point because the point obtained through gtk_gesture_get_bounding_box_center()
|
||||
// in the "end" signal is not a rotation center
|
||||
gs_lastGesturePoint = wxPoint(wxRound(x), wxRound(y));
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
rotate_gesture_end_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* win)
|
||||
{
|
||||
wxRotateGestureEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(gs_lastGesturePoint);
|
||||
event.SetGestureEnd();
|
||||
event.SetRotationAngle(gs_lastAngle);
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
long_press_gesture_callback(GtkGesture* WXUNUSED(gesture), gdouble x, gdouble y, wxWindowGTK* win)
|
||||
{
|
||||
wxLongPressEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
event.SetGestureStart();
|
||||
event.SetGestureEnd();
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
wxEmitTwoFingerTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win)
|
||||
{
|
||||
wxTwoFingerTapEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
|
||||
wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
|
||||
if ( !data )
|
||||
return;
|
||||
|
||||
double lastX = data->m_lastTouchPoint.x;
|
||||
double lastY = data->m_lastTouchPoint.y;
|
||||
|
||||
// Calculate smaller of x coordinate between 2 touches
|
||||
double left = lastX <= gdk_event->x ? lastX : gdk_event->x;
|
||||
|
||||
// Calculate smaller of y coordinate between 2 touches
|
||||
double up = lastY <= gdk_event->y ? lastY : gdk_event->y;
|
||||
|
||||
// Calculate gesture point .i.e center of the box formed by two touches
|
||||
double x = left + abs(lastX - gdk_event->x)/2;
|
||||
double y = up + abs(lastY - gdk_event->y)/2;
|
||||
|
||||
event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
|
||||
event.SetGestureStart();
|
||||
event.SetGestureEnd();
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win)
|
||||
{
|
||||
wxPressAndTapEvent event(win->GetId());
|
||||
|
||||
event.SetEventObject(win);
|
||||
|
||||
wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
|
||||
if ( !data )
|
||||
return;
|
||||
|
||||
switch ( data->m_gestureState )
|
||||
{
|
||||
case begin:
|
||||
event.SetGestureStart();
|
||||
break;
|
||||
|
||||
case update:
|
||||
// Update touch point as the touch corresponding to "press" is moving
|
||||
if ( data->m_touchSequence == gdk_event->sequence )
|
||||
{
|
||||
data->m_lastTouchPoint.x = gdk_event->x;
|
||||
data->m_lastTouchPoint.y = gdk_event->y;
|
||||
}
|
||||
break;
|
||||
|
||||
case end:
|
||||
event.SetGestureEnd();
|
||||
break;
|
||||
}
|
||||
|
||||
event.SetPosition(data->m_lastTouchPoint);
|
||||
|
||||
win->GTKProcessEvent(event);
|
||||
}
|
||||
|
||||
static void
|
||||
touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGTK* win)
|
||||
{
|
||||
wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
|
||||
if ( !data )
|
||||
return;
|
||||
|
||||
switch ( gdk_event->type )
|
||||
{
|
||||
case GDK_TOUCH_BEGIN:
|
||||
data->m_touchCount++;
|
||||
|
||||
data->m_allowedGestures &= ~two_finger_tap;
|
||||
|
||||
if ( data->m_touchCount == 1 )
|
||||
{
|
||||
data->m_lastTouchTime = gdk_event->time;
|
||||
data->m_lastTouchPoint.x = gdk_event->x;
|
||||
data->m_lastTouchPoint.y = gdk_event->y;
|
||||
|
||||
// Save the sequence which identifies touch corresponding to "press"
|
||||
data->m_touchSequence = gdk_event->sequence;
|
||||
|
||||
// "Press and Tap Event" may occur in future
|
||||
data->m_allowedGestures |= press_and_tap;
|
||||
}
|
||||
|
||||
// Check if two fingers are placed together .i.e difference between their time stamps is <= 200 milliseconds
|
||||
else if ( data->m_touchCount == 2 && gdk_event->time - data->m_lastTouchTime <= wxTwoFingerTimeInterval )
|
||||
{
|
||||
// "Two Finger Tap Event" may be possible in the future
|
||||
data->m_allowedGestures |= two_finger_tap;
|
||||
|
||||
// Cancel "Press and Tap Event"
|
||||
data->m_allowedGestures &= ~press_and_tap;
|
||||
}
|
||||
break;
|
||||
|
||||
case GDK_TOUCH_UPDATE:
|
||||
// If press and tap gesture is active and touch corresponding to that gesture is moving
|
||||
if ( (data->m_activeGestures & press_and_tap) && gdk_event->sequence == data->m_touchSequence )
|
||||
{
|
||||
data->m_gestureState = update;
|
||||
wxEmitPressAndTapEvent(gdk_event, win);
|
||||
}
|
||||
break;
|
||||
|
||||
case GDK_TOUCH_END:
|
||||
case GDK_TOUCH_CANCEL:
|
||||
data->m_touchCount--;
|
||||
|
||||
if ( data->m_touchCount == 1 )
|
||||
{
|
||||
data->m_lastTouchTime = gdk_event->time;
|
||||
|
||||
// If the touch corresponding to "press" is present and "tap" is produced by some ather touch
|
||||
if ( (data->m_allowedGestures & press_and_tap) && gdk_event->sequence != data->m_touchSequence )
|
||||
{
|
||||
// Press and Tap gesture becomes active now
|
||||
if ( !(data->m_activeGestures & press_and_tap) )
|
||||
{
|
||||
data->m_gestureState = begin;
|
||||
data->m_activeGestures |= press_and_tap;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
data->m_gestureState = update;
|
||||
}
|
||||
|
||||
wxEmitPressAndTapEvent(gdk_event, win);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if "Two Finger Tap Event" is possible and both the fingers have been lifted up together
|
||||
else if ( (data->m_allowedGestures & two_finger_tap) && !data->m_touchCount
|
||||
&& gdk_event->time - data->m_lastTouchTime <= wxTwoFingerTimeInterval )
|
||||
{
|
||||
// Process Two Finger Tap Event
|
||||
wxEmitTwoFingerTapEvent(gdk_event, win);
|
||||
}
|
||||
|
||||
// If the gesture was active and the touch corresponding to "press" is no longer on the screen
|
||||
if ( (data->m_activeGestures & press_and_tap) && gdk_event->sequence == data->m_touchSequence )
|
||||
{
|
||||
data->m_gestureState = end;
|
||||
|
||||
data->m_activeGestures &= ~press_and_tap;
|
||||
|
||||
data->m_allowedGestures &= ~press_and_tap;
|
||||
|
||||
wxEmitPressAndTapEvent(gdk_event, win);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void wxWindowGesturesData::Reinit(wxWindowGTK* win,
|
||||
GtkWidget *widget,
|
||||
int eventsMask)
|
||||
{
|
||||
m_touchCount = 0;
|
||||
m_lastTouchTime = 0;
|
||||
m_gestureState = 0;
|
||||
m_allowedGestures = 0;
|
||||
m_activeGestures = 0;
|
||||
m_touchSequence = NULL;
|
||||
|
||||
if ( eventsMask & wxTOUCH_VERTICAL_PAN_GESTURE )
|
||||
{
|
||||
eventsMask &= ~wxTOUCH_VERTICAL_PAN_GESTURE;
|
||||
|
||||
m_vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL);
|
||||
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_vertical_pan_gesture), GTK_PHASE_TARGET);
|
||||
|
||||
g_signal_connect (m_vertical_pan_gesture, "begin",
|
||||
G_CALLBACK(pan_gesture_begin_callback), win);
|
||||
g_signal_connect (m_vertical_pan_gesture, "pan",
|
||||
G_CALLBACK(pan_gesture_callback), win);
|
||||
g_signal_connect (m_vertical_pan_gesture, "end",
|
||||
G_CALLBACK(vertical_pan_gesture_end_callback), win);
|
||||
g_signal_connect (m_vertical_pan_gesture, "cancel",
|
||||
G_CALLBACK(vertical_pan_gesture_end_callback), win);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vertical_pan_gesture = NULL;
|
||||
}
|
||||
|
||||
if ( eventsMask & wxTOUCH_HORIZONTAL_PAN_GESTURE )
|
||||
{
|
||||
eventsMask &= ~wxTOUCH_HORIZONTAL_PAN_GESTURE;
|
||||
|
||||
m_horizontal_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
// Pan signals are also generated in case of "left mouse down + mouse move". This can be disabled by
|
||||
// calling gtk_gesture_single_set_touch_only(GTK_GESTURE_SINGLE(m_horizontal_pan_gesture), TRUE) and
|
||||
// gtk_gesture_single_set_touch_only(GTK_GESTURE_SINGLE(verticaal_pan_gesture), TRUE) which will allow
|
||||
// pan signals only for Touch events.
|
||||
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_horizontal_pan_gesture), GTK_PHASE_TARGET);
|
||||
|
||||
g_signal_connect (m_horizontal_pan_gesture, "begin",
|
||||
G_CALLBACK(pan_gesture_begin_callback), win);
|
||||
g_signal_connect (m_horizontal_pan_gesture, "pan",
|
||||
G_CALLBACK(pan_gesture_callback), win);
|
||||
g_signal_connect (m_horizontal_pan_gesture, "end",
|
||||
G_CALLBACK(horizontal_pan_gesture_end_callback), win);
|
||||
g_signal_connect (m_horizontal_pan_gesture, "cancel",
|
||||
G_CALLBACK(horizontal_pan_gesture_end_callback), win);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_horizontal_pan_gesture = NULL;
|
||||
}
|
||||
|
||||
if ( eventsMask & wxTOUCH_ZOOM_GESTURE )
|
||||
{
|
||||
eventsMask &= ~wxTOUCH_ZOOM_GESTURE;
|
||||
|
||||
m_zoom_gesture = gtk_gesture_zoom_new(widget);
|
||||
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_zoom_gesture), GTK_PHASE_TARGET);
|
||||
|
||||
g_signal_connect (m_zoom_gesture, "begin",
|
||||
G_CALLBACK(zoom_gesture_begin_callback), win);
|
||||
g_signal_connect (m_zoom_gesture, "scale-changed",
|
||||
G_CALLBACK(zoom_gesture_callback), win);
|
||||
g_signal_connect (m_zoom_gesture, "end",
|
||||
G_CALLBACK(zoom_gesture_end_callback), win);
|
||||
g_signal_connect (m_zoom_gesture, "cancel",
|
||||
G_CALLBACK(zoom_gesture_end_callback), win);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_zoom_gesture = NULL;
|
||||
}
|
||||
|
||||
if ( eventsMask & wxTOUCH_ROTATE_GESTURE )
|
||||
{
|
||||
eventsMask &= ~wxTOUCH_ROTATE_GESTURE;
|
||||
|
||||
m_rotate_gesture = gtk_gesture_rotate_new(widget);
|
||||
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_rotate_gesture), GTK_PHASE_TARGET);
|
||||
|
||||
g_signal_connect (m_rotate_gesture, "begin",
|
||||
G_CALLBACK(rotate_gesture_begin_callback), win);
|
||||
g_signal_connect (m_rotate_gesture, "angle-changed",
|
||||
G_CALLBACK(rotate_gesture_callback), win);
|
||||
g_signal_connect (m_rotate_gesture, "end",
|
||||
G_CALLBACK(rotate_gesture_end_callback), win);
|
||||
g_signal_connect (m_rotate_gesture, "cancel",
|
||||
G_CALLBACK(rotate_gesture_end_callback), win);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rotate_gesture = NULL;
|
||||
}
|
||||
|
||||
if ( eventsMask & wxTOUCH_PRESS_GESTURES )
|
||||
{
|
||||
eventsMask &= ~wxTOUCH_PRESS_GESTURES;
|
||||
|
||||
m_long_press_gesture = gtk_gesture_long_press_new(widget);
|
||||
|
||||
// "pressed" signal is also generated when left mouse is down for some minimum duration of time.
|
||||
// This can be disable by calling gtk_gesture_single_set_touch_only(GTK_GESTURE_SINGLE(m_long_press_gesture), TRUE)
|
||||
// which will allow "pressed" signal only for Touch events.
|
||||
|
||||
gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_long_press_gesture), GTK_PHASE_TARGET);
|
||||
|
||||
g_signal_connect (m_long_press_gesture, "pressed",
|
||||
G_CALLBACK(long_press_gesture_callback), win);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_long_press_gesture = NULL;
|
||||
}
|
||||
|
||||
wxASSERT_MSG( eventsMask == 0, "Unknown touch event mask bit specified" );
|
||||
|
||||
gtk_widget_add_events(widget, GDK_TOUCHPAD_GESTURE_MASK);
|
||||
|
||||
g_signal_connect (widget, "touch-event",
|
||||
G_CALLBACK(touch_callback), win);
|
||||
}
|
||||
|
||||
void wxWindowGesturesData::Free()
|
||||
{
|
||||
g_clear_object(&m_vertical_pan_gesture);
|
||||
g_clear_object(&m_horizontal_pan_gesture);
|
||||
g_clear_object(&m_zoom_gesture);
|
||||
g_clear_object(&m_rotate_gesture);
|
||||
g_clear_object(&m_long_press_gesture);
|
||||
|
||||
// We don't current remove GDK_TOUCHPAD_GESTURE_MASK as this can't be done
|
||||
// for a window as long as it's realized, and this might still be the case
|
||||
// if we're called from EnableTouchEvents(wxTOUCH_NONE) and not from the
|
||||
// dtor, but it shouldn't really be a problem.
|
||||
}
|
||||
|
||||
#endif // wxGTK_HAS_GESTURES_SUPPORT
|
||||
|
||||
// This method must be always defined for GTK+ 3 as it's declared in the
|
||||
// header, where we can't (easily) test for wxGTK_HAS_GESTURES_SUPPORT.
|
||||
#ifdef __WXGTK3__
|
||||
|
||||
bool wxWindowGTK::EnableTouchEvents(int eventsMask)
|
||||
{
|
||||
#ifdef wxGTK_HAS_GESTURES_SUPPORT
|
||||
// Check if gestures support is also available during run-time.
|
||||
if ( gtk_check_version(3, 14, 0) == NULL )
|
||||
{
|
||||
wxWindowGesturesData* const dataOld = wxWindowGestures::FromObject(this);
|
||||
|
||||
if ( eventsMask == wxTOUCH_NONE )
|
||||
{
|
||||
// Reset the gestures data used by this object, but don't destroy
|
||||
// it, as we could be called from an event handler, in which case
|
||||
// this object could be still used after the event handler returns.
|
||||
if ( dataOld )
|
||||
dataOld->Free();
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkWidget* const widget = GetConnectWidget();
|
||||
|
||||
if ( dataOld )
|
||||
{
|
||||
dataOld->Reinit(this, widget, eventsMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxWindowGesturesData* const
|
||||
dataNew = new wxWindowGesturesData(this, widget, eventsMask);
|
||||
wxWindowGestures::StoreForObject(this, dataNew);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // wxGTK_HAS_GESTURES_SUPPORT
|
||||
|
||||
return wxWindowBase::EnableTouchEvents(eventsMask);
|
||||
}
|
||||
|
||||
#endif // __WXGTK3__
|
||||
|
||||
void wxWindowGTK::ConnectWidget( GtkWidget *widget )
|
||||
{
|
||||
static bool isSourceAttached;
|
||||
|
Reference in New Issue
Block a user