diff --git a/include/wx/window.h b/include/wx/window.h index 27a9c856cb..38d70ab7a8 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -136,8 +136,15 @@ enum wxShowEffect // Values for EnableTouchEvents() mask. enum { - wxTOUCH_NONE = 0x0000, - wxTOUCH_ALL_GESTURES = 0x00ff + wxTOUCH_NONE = 0x0000, + wxTOUCH_VERTICAL_PAN_GESTURE = 0x0001, + wxTOUCH_HORIZONTAL_PAN_GESTURE = 0x0002, + wxTOUCH_PAN_GESTURES = wxTOUCH_VERTICAL_PAN_GESTURE | + wxTOUCH_HORIZONTAL_PAN_GESTURE, + wxTOUCH_ZOOM_GESTURE = 0x0004, + wxTOUCH_ROTATE_GESTURE = 0x0008, + wxTOUCH_PRESS_GESTURES = 0x0010, + wxTOUCH_ALL_GESTURES = 0x001f }; // flags for SendSizeEvent() diff --git a/interface/wx/window.h b/interface/wx/window.h index b0712fb378..672a6f94e9 100644 --- a/interface/wx/window.h +++ b/interface/wx/window.h @@ -52,6 +52,68 @@ enum wxShowEffect }; +/** + Values for wxWindow::EnableTouchEvents() mask. + + The values other than ::wxTOUCH_NONE and ::wxTOUCH_ALL_GESTURES can be + combined together to request enabling events for the specified gestures and + for them only. + + @since 3.1.1 + */ +enum +{ + /** + Don't generate any touch events. + */ + wxTOUCH_NONE, + + /** + Generate wxPanGestureEvent for vertical pans. + + Note that under macOS horizontal pan events are also enabled when this + flag is specified. + */ + wxTOUCH_VERTICAL_PAN_GESTURE, + + /** + Generate wxPanGestureEvent for horizontal pans. + + Note that under macOS vertical pan events are also enabled when this + flag is specified. + */ + wxTOUCH_HORIZONTAL_PAN_GESTURE, + + /** + Generate wxPanGestureEvent for any pans. + + This is just a convenient combination of wxTOUCH_VERTICAL_PAN_GESTURE + and wxTOUCH_HORIZONTAL_PAN_GESTURE. + */ + wxTOUCH_PAN_GESTURES, + + /** + Generate wxZoomGestureEvent. + */ + wxTOUCH_ZOOM_GESTURE, + + /** + Generate wxRotateGestureEvent. + */ + wxTOUCH_ROTATE_GESTURE, + + /** + Generate events for press or tap gestures such as wxTwoFingerTapEvent, + wxLongPressEvent and wxPressAndTapEvent. + */ + wxTOUCH_PRESS_GESTURES, + + /** + Enable all supported gesture events. + */ + wxTOUCH_ALL_GESTURES +}; + /** flags for SendSizeEvent() */ @@ -3427,6 +3489,15 @@ public: /** Request generation of touch events for this window. + Each call to this function supersedes the previous ones, i.e. if you + want to receive events for both zoom and rotate gestures, you need to + call + @code + EnableTouchEvents(wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE); + @endcode + instead of calling it twice in a row as the second call would disable + the first gesture. + @param eventsMask Either wxTOUCH_NONE or wxTOUCH_ALL_GESTURES to disable or enable gesture events for this window. diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 26bc0734b7..eceb87ff9b 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -238,7 +238,7 @@ static bool gs_inSizeAllocate; class wxWindowGesturesData { public: - wxWindowGesturesData(wxWindow* win, GtkWidget *widget); + wxWindowGesturesData(wxWindow* win, GtkWidget *widget, int eventsMask); ~wxWindowGesturesData(); unsigned int m_touchCount; @@ -3344,7 +3344,9 @@ touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGT } } -wxWindowGesturesData::wxWindowGesturesData(wxWindowGTK* win, GtkWidget *widget) +wxWindowGesturesData::wxWindowGesturesData(wxWindowGTK* win, + GtkWidget *widget, + int eventsMask) { m_touchCount = 0; m_lastTouchTime = 0; @@ -3353,84 +3355,137 @@ wxWindowGesturesData::wxWindowGesturesData(wxWindowGTK* win, GtkWidget *widget) m_activeGestures = 0; m_touchSequence = NULL; - m_vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL); + if ( eventsMask & wxTOUCH_VERTICAL_PAN_GESTURE ) + { + eventsMask &= ~wxTOUCH_VERTICAL_PAN_GESTURE; - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_vertical_pan_gesture), GTK_PHASE_TARGET); + m_vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL); - 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); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_vertical_pan_gesture), GTK_PHASE_TARGET); - m_horizontal_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_HORIZONTAL); + 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; + } - // 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. + if ( eventsMask & wxTOUCH_HORIZONTAL_PAN_GESTURE ) + { + eventsMask &= ~wxTOUCH_HORIZONTAL_PAN_GESTURE; - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_horizontal_pan_gesture), GTK_PHASE_TARGET); + m_horizontal_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_HORIZONTAL); - 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); + // 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. - m_zoom_gesture = gtk_gesture_zoom_new(widget); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_horizontal_pan_gesture), GTK_PHASE_TARGET); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_zoom_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; + } - 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); + if ( eventsMask & wxTOUCH_ZOOM_GESTURE ) + { + eventsMask &= ~wxTOUCH_ZOOM_GESTURE; - m_rotate_gesture = gtk_gesture_rotate_new(widget); + m_zoom_gesture = gtk_gesture_zoom_new(widget); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_rotate_gesture), GTK_PHASE_TARGET); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_zoom_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); + 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; + } - m_long_press_gesture = gtk_gesture_long_press_new(widget); + if ( eventsMask & wxTOUCH_ROTATE_GESTURE ) + { + eventsMask &= ~wxTOUCH_ROTATE_GESTURE; - // "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. + m_rotate_gesture = gtk_gesture_rotate_new(widget); - gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_long_press_gesture), GTK_PHASE_TARGET); + 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" ); - g_signal_connect (m_long_press_gesture, "pressed", - G_CALLBACK(long_press_gesture_callback), win); g_signal_connect (widget, "touch-event", G_CALLBACK(touch_callback), win); } wxWindowGesturesData::~wxWindowGesturesData() { - g_object_unref(m_vertical_pan_gesture); - g_object_unref(m_horizontal_pan_gesture); - g_object_unref(m_zoom_gesture); - g_object_unref(m_rotate_gesture); - g_object_unref(m_long_press_gesture); + if ( m_vertical_pan_gesture ) + g_object_unref(m_vertical_pan_gesture); + if ( m_horizontal_pan_gesture ) + g_object_unref(m_horizontal_pan_gesture); + if ( m_zoom_gesture ) + g_object_unref(m_zoom_gesture); + if ( m_rotate_gesture ) + g_object_unref(m_rotate_gesture); + if ( m_long_press_gesture ) + g_object_unref(m_long_press_gesture); } #endif // wxGTK_HAS_GESTURES_SUPPORT @@ -3445,7 +3500,21 @@ bool wxWindowGTK::EnableTouchEvents(int eventsMask) // Check if gestures support is also available during run-time. if ( gtk_check_version(3, 14, 0) == NULL ) { - m_gesturesData = new wxWindowGesturesData(this, GetConnectWidget()); + if ( eventsMask == wxTOUCH_NONE ) + { + delete m_gesturesData; + m_gesturesData = NULL; + } + else + { + m_gesturesData = new wxWindowGesturesData + ( + this, + GetConnectWidget(), + eventsMask + ); + } + return true; } #endif // wxGTK_HAS_GESTURES_SUPPORT diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 0de02b2508..0f9f3f11e4 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -51,6 +51,7 @@ #include "wx/textctrl.h" #include "wx/menuitem.h" #include "wx/module.h" + #include "wx/vector.h" #endif #if wxUSE_OWNER_DRAWN && !defined(__WXUNIVERSAL__) @@ -909,17 +910,105 @@ void wxWindowMSW::WarpPointer(int x, int y) bool wxWindowMSW::EnableTouchEvents(int eventsMask) { #ifdef WM_GESTURE - if ( GestureFuncs::IsOk() && eventsMask == wxTOUCH_ALL_GESTURES ) + if ( GestureFuncs::IsOk() ) { - // Configure to receive all gestures - GESTURECONFIG gestureConfig = {0, GC_ALLGESTURES, 0}; + // Static struct used when we need to use just a single configuration. + GESTURECONFIG config = {0, 0, 0}; + + GESTURECONFIG* ptrConfigs = &config; + UINT numConfigs = 1; + + // This is used only if we need to allocate the configurations + // dynamically. + wxVector configs; + + // There are two simple cases: enabling or disabling all gestures. + if ( eventsMask == wxTOUCH_NONE ) + { + config.dwBlock = GC_ALLGESTURES; + } + else if ( eventsMask == wxTOUCH_ALL_GESTURES ) + { + config.dwWant = GC_ALLGESTURES; + } + else // Need to enable the individual gestures + { + int wantedPan = 0; + switch ( eventsMask & wxTOUCH_PAN_GESTURES ) + { + case wxTOUCH_VERTICAL_PAN_GESTURE: + wantedPan = GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; + break; + + case wxTOUCH_HORIZONTAL_PAN_GESTURE: + wantedPan = GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; + break; + + case wxTOUCH_PAN_GESTURES: + wantedPan = GC_PAN; + break; + + case 0: + // This is the only other possibility and wantedPan is + // already initialized to 0 anyhow, so don't do anything, + // just list it for completeness. + break; + } + + if ( wantedPan ) + { + eventsMask &= ~wxTOUCH_PAN_GESTURES; + + config.dwID = GID_PAN; + config.dwWant = wantedPan; + configs.push_back(config); + } + + if ( eventsMask & wxTOUCH_ZOOM_GESTURE ) + { + eventsMask &= ~wxTOUCH_ZOOM_GESTURE; + + config.dwID = GID_ZOOM; + config.dwWant = GC_ZOOM; + configs.push_back(config); + } + + if ( eventsMask & wxTOUCH_ROTATE_GESTURE ) + { + eventsMask &= ~wxTOUCH_ROTATE_GESTURE; + + config.dwID = GID_ROTATE; + config.dwWant = GC_ROTATE; + configs.push_back(config); + } + + if ( eventsMask & wxTOUCH_PRESS_GESTURES ) + { + eventsMask &= ~wxTOUCH_PRESS_GESTURES; + + config.dwID = GID_TWOFINGERTAP; + config.dwWant = GC_TWOFINGERTAP; + configs.push_back(config); + + config.dwID = GID_PRESSANDTAP; + config.dwWant = GC_PRESSANDTAP; + configs.push_back(config); + } + + // As we clear all the known bits if they're set in the code above, + // there should be nothing left. + wxCHECK_MSG( eventsMask == 0, false, + wxS("Unknown touch event mask bit specified") ); + + ptrConfigs = &configs[0]; + } if ( !GestureFuncs::SetGestureConfig() ( m_hWnd, 0, // Reserved, must be always 0. - 1, // Number of gesture configurations. - &gestureConfig, // Pointer to the first one. + numConfigs, // Number of gesture configurations. + ptrConfigs, // Pointer to the first one. sizeof(GESTURECONFIG) // Size of each configuration. ) ) diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 15abbc43b3..9df2c4b17c 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -3293,27 +3293,65 @@ bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) { if ( IsUserPane() ) { + if ( eventsMask == wxTOUCH_NONE ) + { + [m_panGestureRecognizer release]; + [m_magnificationGestureRecognizer release]; + [m_rotationGestureRecognizer release]; + [m_pressGestureRecognizer release]; + + [m_osxView setAcceptsTouchEvents:NO]; + + return true; + } + Class cls = [m_osxView class]; - m_panGestureRecognizer = - [[NSPanGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handlePanGesture:)]; - if ( !class_respondsToSelector(cls, @selector(handlePanGesture:)) ) - class_addMethod(cls, @selector(handlePanGesture:), (IMP) wxOSX_panGestureEvent, "v@:@" ); + if ( eventsMask & wxTOUCH_PAN_GESTURES ) + { + eventsMask &= ~wxTOUCH_PAN_GESTURES; - m_magnificationGestureRecognizer = - [[NSMagnificationGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleZoomGesture:)]; - if ( !class_respondsToSelector(cls, @selector(handleZoomGesture:)) ) - class_addMethod(cls, @selector(handleZoomGesture:), (IMP) wxOSX_zoomGestureEvent, "v@:@" ); + m_panGestureRecognizer = + [[NSPanGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handlePanGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handlePanGesture:)) ) + class_addMethod(cls, @selector(handlePanGesture:), (IMP) wxOSX_panGestureEvent, "v@:@" ); + [m_osxView addGestureRecognizer:m_panGestureRecognizer]; + } - m_rotationGestureRecognizer = - [[NSRotationGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleRotateGesture:)]; - if ( !class_respondsToSelector(cls, @selector(handleRotateGesture:)) ) - class_addMethod(cls, @selector(handleRotateGesture:), (IMP) wxOSX_rotateGestureEvent, "v@:@" ); + if ( eventsMask & wxTOUCH_ZOOM_GESTURE ) + { + eventsMask &= ~wxTOUCH_ZOOM_GESTURE; - m_pressGestureRecognizer = - [[NSPressGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleLongPressGesture:)]; - if ( !class_respondsToSelector(cls, @selector(handleLongPressGesture:)) ) - class_addMethod(cls, @selector(handleLongPressGesture:), (IMP) wxOSX_longPressEvent, "v@:@" ); + m_magnificationGestureRecognizer = + [[NSMagnificationGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleZoomGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handleZoomGesture:)) ) + class_addMethod(cls, @selector(handleZoomGesture:), (IMP) wxOSX_zoomGestureEvent, "v@:@" ); + [m_osxView addGestureRecognizer:m_magnificationGestureRecognizer]; + } + + if ( eventsMask & wxTOUCH_ROTATE_GESTURE ) + { + eventsMask &= ~wxTOUCH_ROTATE_GESTURE; + + m_rotationGestureRecognizer = + [[NSRotationGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleRotateGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handleRotateGesture:)) ) + class_addMethod(cls, @selector(handleRotateGesture:), (IMP) wxOSX_rotateGestureEvent, "v@:@" ); + [m_osxView addGestureRecognizer:m_rotationGestureRecognizer]; + } + + if ( eventsMask & wxTOUCH_PRESS_GESTURES ) + { + eventsMask &= ~wxTOUCH_PRESS_GESTURES; + + m_pressGestureRecognizer = + [[NSPressGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleLongPressGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handleLongPressGesture:)) ) + class_addMethod(cls, @selector(handleLongPressGesture:), (IMP) wxOSX_longPressEvent, "v@:@" ); + [m_osxView addGestureRecognizer:m_pressGestureRecognizer]; + } + + wxASSERT_MSG( eventsMask == 0, "Unknown touch event mask bit specified" ); if ( !class_respondsToSelector(cls, @selector(touchesBeganWithEvent:)) ) class_addMethod(cls, @selector(touchesBeganWithEvent:), (IMP) wxOSX_touchesBegan, "v@:@" ); @@ -3322,11 +3360,6 @@ bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) if ( !class_respondsToSelector(cls, @selector(touchesEndedWithEvent:)) ) class_addMethod(cls, @selector(touchesEndedWithEvent:), (IMP) wxOSX_touchesEnded, "v@:@" ); - [m_osxView addGestureRecognizer:m_panGestureRecognizer]; - [m_osxView addGestureRecognizer:m_magnificationGestureRecognizer]; - [m_osxView addGestureRecognizer:m_rotationGestureRecognizer]; - [m_osxView addGestureRecognizer:m_pressGestureRecognizer]; - [m_osxView setAcceptsTouchEvents:YES]; return true;