From 261b04b5a3f764079e973579dd6d44325abcb9bd Mon Sep 17 00:00:00 2001 From: prashantkn94 Date: Sun, 10 Sep 2017 21:13:32 +0530 Subject: [PATCH 01/26] Merge multi-touch gestures event branch This is a squashed commit of the SOC2017_GESTURES branch from https://github.com/prashantkn94/wxWidgets.git Closes https://github.com/wxWidgets/wxWidgets/pull/551 --- include/wx/defs.h | 7 + include/wx/event.h | 237 +++++++++++++- include/wx/gtk/window.h | 10 + include/wx/msw/window.h | 6 + include/wx/osx/cocoa/private.h | 24 ++ interface/wx/event.h | 251 +++++++++++++++ samples/event/Makefile.in | 5 +- samples/event/event.bkl | 3 +- samples/event/event.cpp | 21 +- samples/event/event_vc7.vcproj | 11 + samples/event/event_vc8.vcproj | 14 + samples/event/event_vc9.vcproj | 14 + samples/event/gestures.cpp | 216 +++++++++++++ samples/event/gestures.h | 39 +++ samples/event/makefile.bcc | 5 +- samples/event/makefile.gcc | 6 +- samples/event/makefile.vc | 5 +- src/common/event.cpp | 15 + src/gtk/window.cpp | 548 +++++++++++++++++++++++++++++++++ src/msw/window.cpp | 359 +++++++++++++++++++++ src/osx/cocoa/window.mm | 532 +++++++++++++++++++++++++++++++- 21 files changed, 2318 insertions(+), 10 deletions(-) create mode 100644 samples/event/gestures.cpp create mode 100644 samples/event/gestures.h diff --git a/include/wx/defs.h b/include/wx/defs.h index bdf417e3ce..2c5cdeeeb6 100644 --- a/include/wx/defs.h +++ b/include/wx/defs.h @@ -3104,6 +3104,12 @@ DECLARE_WXCOCOA_OBJC_CLASS(NSView); DECLARE_WXCOCOA_OBJC_CLASS(NSOpenGLContext); DECLARE_WXCOCOA_OBJC_CLASS(NSOpenGLPixelFormat); DECLARE_WXCOCOA_OBJC_CLASS( NSPrintInfo ); +DECLARE_WXCOCOA_OBJC_CLASS(NSGestureRecognizer); +DECLARE_WXCOCOA_OBJC_CLASS(NSPanGestureRecognizer); +DECLARE_WXCOCOA_OBJC_CLASS(NSMagnificationGestureRecognizer); +DECLARE_WXCOCOA_OBJC_CLASS(NSRotationGestureRecognizer); +DECLARE_WXCOCOA_OBJC_CLASS(NSPressGestureRecognizer); +DECLARE_WXCOCOA_OBJC_CLASS(NSTouch); #endif /* __WXMAC__ &__DARWIN__ */ #ifdef __WXMAC__ @@ -3267,6 +3273,7 @@ typedef struct _GdkDragContext GdkDragContext; #if defined(__WXGTK3__) typedef struct _GdkWindow GdkWindow; + typedef struct _GdkEventSequence GdkEventSequence; #elif defined(__WXGTK20__) typedef struct _GdkDrawable GdkWindow; typedef struct _GdkDrawable GdkPixmap; diff --git a/include/wx/event.h b/include/wx/event.h index e6e58464cc..70852f9965 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -631,6 +631,13 @@ class WXDLLIMPEXP_FWD_CORE wxInitDialogEvent; class WXDLLIMPEXP_FWD_CORE wxUpdateUIEvent; class WXDLLIMPEXP_FWD_CORE wxClipboardTextEvent; class WXDLLIMPEXP_FWD_CORE wxHelpEvent; +class WXDLLIMPEXP_FWD_CORE wxGestureEvent; +class WXDLLIMPEXP_FWD_CORE wxPanGestureEvent; +class WXDLLIMPEXP_FWD_CORE wxZoomGestureEvent; +class WXDLLIMPEXP_FWD_CORE wxRotateGestureEvent; +class WXDLLIMPEXP_FWD_CORE wxTwoFingerTapEvent; +class WXDLLIMPEXP_FWD_CORE wxLongPressEvent; +class WXDLLIMPEXP_FWD_CORE wxPressAndTapEvent; // Command events @@ -736,6 +743,14 @@ wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SCROLLWIN_PAGEDOWN, wxScrollWin wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEvent); + // Gesture events +wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_GESTURE_PAN, wxPanGestureEvent); +wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_GESTURE_ZOOM, wxZoomGestureEvent); +wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_GESTURE_ROTATE, wxRotateGestureEvent); +wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_TWO_FINGER_TAP, wxTwoFingerTapEvent); +wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_LONG_PRESS, wxLongPressEvent); +wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_PRESS_AND_TAP, wxPressAndTapEvent); + // System events wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SIZE, wxSizeEvent); wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_MOVE, wxMoveEvent); @@ -1850,6 +1865,201 @@ private: wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxSetCursorEvent); }; + // Gesture Event + +const unsigned int wxTwoFingerTimeInterval = 200; + +class WXDLLIMPEXP_CORE wxGestureEvent : public wxEvent +{ +public: + wxGestureEvent(wxWindowID winid = 0, wxEventType type = wxEVT_NULL) + : wxEvent(winid, type) + { + m_isStart = false; + m_isEnd = false; + } + + wxGestureEvent(const wxGestureEvent& event) : wxEvent(event) + { + m_pos = event.m_pos; + m_isStart = event.m_isStart; + m_isEnd = event.m_isEnd; + } + + const wxPoint& GetPosition() const { return m_pos; } + void SetPosition(const wxPoint& pos) { m_pos = pos; } + bool IsGestureStart() const { return m_isStart; } + void SetGestureStart(bool isStart = true) { m_isStart = isStart; } + bool IsGestureEnd() const { return m_isEnd; } + void SetGestureEnd(bool isEnd = true) { m_isEnd = isEnd; } + + virtual wxEvent *Clone() const { return new wxGestureEvent(*this); } + +protected: + wxPoint m_pos; + bool m_isStart, m_isEnd; + + wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxGestureEvent); + +}; + + // Pan Gesture Event + + /* + wxEVT_GESTURE_PAN + */ + +class WXDLLIMPEXP_CORE wxPanGestureEvent : public wxGestureEvent +{ +public: + wxPanGestureEvent(wxWindowID winid = 0) + : wxGestureEvent(winid, wxEVT_GESTURE_PAN) + { + m_deltaX = 0; + m_deltaY = 0; + } + + wxPanGestureEvent(const wxPanGestureEvent& event) : wxGestureEvent(event) + { + m_deltaX = event.m_deltaX; + m_deltaY = event.m_deltaY; + } + + int GetDeltaX() const { return m_deltaX; } + void SetDeltaX(int DeltaX) { m_deltaX = DeltaX; } + int GetDeltaY() const { return m_deltaY; } + void SetDeltaY(int DeltaY) { m_deltaY = DeltaY; } + + virtual wxEvent *Clone() const { return new wxPanGestureEvent(*this); } + +private: + int m_deltaX, m_deltaY; + + wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxPanGestureEvent); +}; + + // Zoom Gesture Event + + /* + wxEVT_GESTURE_ZOOM + */ + +class WXDLLIMPEXP_CORE wxZoomGestureEvent : public wxGestureEvent +{ +public: + wxZoomGestureEvent(wxWindowID winid = 0) + : wxGestureEvent(winid, wxEVT_GESTURE_ZOOM) + { m_zoomFactor = 1.0; } + + wxZoomGestureEvent(const wxZoomGestureEvent& event) : wxGestureEvent(event) + { + m_zoomFactor = event.m_zoomFactor; + } + + double GetZoomFactor() const { return m_zoomFactor; } + void SetZoomFactor(double zoomFactor) { m_zoomFactor = zoomFactor; } + + virtual wxEvent *Clone() const { return new wxZoomGestureEvent(*this); } + +private: + double m_zoomFactor; + + wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxZoomGestureEvent); +}; + + // Rotate Gesture Event + + /* + wxEVT_GESTURE_ROTATE + */ + +class WXDLLIMPEXP_CORE wxRotateGestureEvent : public wxGestureEvent +{ +public: + wxRotateGestureEvent(wxWindowID winid = 0) + : wxGestureEvent(winid, wxEVT_GESTURE_ROTATE) + { m_rotationAngle = 0.0; } + + wxRotateGestureEvent(const wxRotateGestureEvent& event) : wxGestureEvent(event) + { + m_rotationAngle = event.m_rotationAngle; + } + + double GetRotationAngle() const { return m_rotationAngle; } + void SetRotationAngle(double rotationAngle) { m_rotationAngle = rotationAngle; } + + virtual wxEvent *Clone() const { return new wxRotateGestureEvent(*this); } + +private: + double m_rotationAngle; + + wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxRotateGestureEvent); +}; + + // Two Finger Tap Gesture Event + + /* + wxEVT_TWO_FINGER_TAP + */ + +class WXDLLIMPEXP_CORE wxTwoFingerTapEvent : public wxGestureEvent +{ +public: + wxTwoFingerTapEvent(wxWindowID winid = 0) + : wxGestureEvent(winid, wxEVT_TWO_FINGER_TAP) + { } + + wxTwoFingerTapEvent(const wxTwoFingerTapEvent& event) : wxGestureEvent(event) + { } + + virtual wxEvent *Clone() const { return new wxTwoFingerTapEvent(*this); } + +private: + wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxTwoFingerTapEvent); +}; + + // Long Press Gesture Event + + /* + wxEVT_LONG_PRESS + */ + +class WXDLLIMPEXP_CORE wxLongPressEvent : public wxGestureEvent +{ +public: + wxLongPressEvent(wxWindowID winid = 0) + : wxGestureEvent(winid, wxEVT_LONG_PRESS) + { } + + wxLongPressEvent(const wxLongPressEvent& event) : wxGestureEvent(event) + { } + + virtual wxEvent *Clone() const { return new wxLongPressEvent(*this); } +private: + wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxLongPressEvent); +}; + + // Press And Tap Gesture Event + + /* + wxEVT_PRESS_AND_TAP + */ + +class WXDLLIMPEXP_CORE wxPressAndTapEvent : public wxGestureEvent +{ +public: + wxPressAndTapEvent(wxWindowID winid = 0) + : wxGestureEvent(winid, wxEVT_PRESS_AND_TAP) + { } + + wxPressAndTapEvent(const wxPressAndTapEvent& event) : wxGestureEvent(event) + { } + + virtual wxEvent *Clone() const { return new wxPressAndTapEvent(*this); } +private: + wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxPressAndTapEvent); +}; + // Keyboard input event class /* @@ -3908,7 +4118,12 @@ typedef void (wxEvtHandler::*wxContextMenuEventFunction)(wxContextMenuEvent&); typedef void (wxEvtHandler::*wxMouseCaptureChangedEventFunction)(wxMouseCaptureChangedEvent&); typedef void (wxEvtHandler::*wxMouseCaptureLostEventFunction)(wxMouseCaptureLostEvent&); typedef void (wxEvtHandler::*wxClipboardTextEventFunction)(wxClipboardTextEvent&); - +typedef void (wxEvtHandler::*wxPanGestureEventFunction)(wxPanGestureEvent&); +typedef void (wxEvtHandler::*wxZoomGestureEventFunction)(wxZoomGestureEvent&); +typedef void (wxEvtHandler::*wxRotateGestureEventFunction)(wxRotateGestureEvent&); +typedef void (wxEvtHandler::*wxTwoFingerTapEventFunction)(wxTwoFingerTapEvent&); +typedef void (wxEvtHandler::*wxLongPressEventFunction)(wxLongPressEvent&); +typedef void (wxEvtHandler::*wxPressAndTapEventFunction)(wxPressAndTapEvent&); #define wxCommandEventHandler(func) \ wxEVENT_HANDLER_CAST(wxCommandEventFunction, func) @@ -3983,6 +4198,18 @@ typedef void (wxEvtHandler::*wxClipboardTextEventFunction)(wxClipboardTextEvent& wxEVENT_HANDLER_CAST(wxMouseCaptureLostEventFunction, func) #define wxClipboardTextEventHandler(func) \ wxEVENT_HANDLER_CAST(wxClipboardTextEventFunction, func) +#define wxPanGestureEventHandler(func) \ + wxEVENT_HANDLER_CAST(wxPanGestureEventFunction, func) +#define wxZoomGestureEventHandler(func) \ + wxEVENT_HANDLER_CAST(wxZoomGestureEventFunction, func) +#define wxRotateGestureEventHandler(func) \ + wxEVENT_HANDLER_CAST(wxRotateGestureEventFunction, func) +#define wxTwoFingerTapEventHandler(func) \ + wxEVENT_HANDLER_CAST(wxTwoFingerTapEventFunction, func) +#define wxLongPressEventHandler(func) \ + wxEVENT_HANDLER_CAST(wxLongPressEventFunction, func) +#define wxPressAndTapEvent(func) \ + wxEVENT_HANDLER_CAST(wxPressAndTapEventFunction, func) #endif // wxUSE_GUI @@ -4317,6 +4544,14 @@ typedef void (wxEvtHandler::*wxClipboardTextEventFunction)(wxClipboardTextEvent& EVT_COMMAND_SCROLL_THUMBRELEASE(winid, func) \ EVT_COMMAND_SCROLL_CHANGED(winid, func) +// Gesture events +#define EVT_GESTURE_PAN(winid, func) wx__DECLARE_EVT1(wxEVT_GESTURE_PAN, winid, wxPanGestureEventHandler(func)) +#define EVT_GESTURE_ZOOM(winid, func) wx__DECLARE_EVT1(wxEVT_GESTURE_ZOOM, winid, wxZoomGestureEventHandler(func)) +#define EVT_GESTURE_ROTATE(winid, func) wx__DECLARE_EVT1(wxEVT_GESTURE_ROTATE, winid, wxRotateGestureEventHandler(func)) +#define EVT_TWO_FINGER_TAP(winid, func) wx__DECLARE_EVT1(wxEVT_TWO_FINGER_TAP, winid, wxTwoFingerTapEventHandler(func)) +#define EVT_LONG_PRESS(winid, func) wx__DECLARE_EVT1(wxEVT_LONG_PRESS, winid, wxLongPressEventHandler(func)) +#define EVT_PRESS_AND_TAP(winid, func) wx__DECLARE_EVT1(wxEVT_PRESS_AND_TAP, winid, wxPressAndTapEvent(func)) + // Convenience macros for commonly-used commands #define EVT_CHECKBOX(winid, func) wx__DECLARE_EVT1(wxEVT_CHECKBOX, winid, wxCommandEventHandler(func)) #define EVT_CHOICE(winid, func) wx__DECLARE_EVT1(wxEVT_CHOICE, winid, wxCommandEventHandler(func)) diff --git a/include/wx/gtk/window.h b/include/wx/gtk/window.h index e4041d9a3b..0639b99ae7 100644 --- a/include/wx/gtk/window.h +++ b/include/wx/gtk/window.h @@ -355,6 +355,16 @@ public: wxRegion m_nativeUpdateRegion; // not transformed for RTL +#if defined(__WXGTK3__) + unsigned int m_touchCount; + unsigned int m_lastTouchTime; + int m_gestureState; + int m_allowedGestures; + int m_activeGestures; + wxPoint m_lastTouchPoint; + GdkEventSequence* m_touchSequence; +#endif + protected: // implement the base class pure virtuals virtual void DoGetTextExtent(const wxString& string, diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index 286d9855ad..3ceeabe5f6 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -360,6 +360,12 @@ public: bool HandleMouseWheel(wxMouseWheelAxis axis, WXWPARAM wParam, WXLPARAM lParam); + bool HandlePanGesture(int x, int y, WXDWORD flags); + bool HandleZoomGesture(int x, int y, WXDWORD fingerDistance, WXDWORD flags); + bool HandleRotateGesture(int x, int y, WXDWORD angleArgument, WXDWORD flags); + bool HandleTwoFingerTap(int x, int y, WXDWORD flags); + bool HandlePressAndTap(int x, int y, WXDWORD flags); + bool HandleChar(WXWPARAM wParam, WXLPARAM lParam); bool HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam); bool HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam); diff --git a/include/wx/osx/cocoa/private.h b/include/wx/osx/cocoa/private.h index 7900204a31..9fd9101a84 100644 --- a/include/wx/osx/cocoa/private.h +++ b/include/wx/osx/cocoa/private.h @@ -143,6 +143,15 @@ public : void SetupCoordinates(wxCoord &x, wxCoord &y, NSEvent *nsEvent); virtual bool SetupCursor(NSEvent* event); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + virtual void PanGestureEvent(NSPanGestureRecognizer *panGestureRecognizer); + virtual void ZoomGestureEvent(NSMagnificationGestureRecognizer *magnificationGestureRecognizer); + virtual void RotateGestureEvent(NSRotationGestureRecognizer *rotationGestureRecognizer); + virtual void LongPressEvent(NSPressGestureRecognizer *pressGestureRecognizer); + virtual void TouchesBegan(NSEvent *event); + virtual void TouchesMoved(NSEvent *event); + virtual void TouchesEnded(NSEvent *event); +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 #if !wxOSX_USE_NATIVE_FLIPPED void SetFlipped(bool flipped); @@ -190,6 +199,21 @@ protected: // events, don't resend them bool m_hasEditor; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + NSPanGestureRecognizer *m_panGestureRecognizer; + NSMagnificationGestureRecognizer *m_magnificationGestureRecognizer; + NSRotationGestureRecognizer *m_rotationGestureRecognizer; + NSPressGestureRecognizer *m_pressGestureRecognizer; + + int m_allowedGestures; + int m_activeGestures; + unsigned int m_touchCount; + unsigned int m_lastTouchTime; + + // Used to keep track of the touch corresponding to "press" in Press and Tap gesture + NSTouch* m_initialTouch; +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxWidgetCocoaImpl); }; diff --git a/interface/wx/event.h b/interface/wx/event.h index 3d92c2d0db..53030db240 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -3644,6 +3644,257 @@ public: void SetPosition(int pos); }; + + +/** @class wxGestureEvent + This is the base class for all supported gesture events. + + @library{wxcore} + @category{events} + + @see wxPanGestureEvent, wxZoomGestureEvent, wxRotateGestureEvent + + @since 3.1.1 +*/ +class wxGestureEvent : public wxEvent +{ +public: + /** + Constructor. + */ + wxGestureEvent(wxWindowID winid = 0, wxEventType type = wxEVT_NULL); + + /** + Returns the position where the event took effect, in client coordinates: position of Pan event, + center of zoom for Zoom event, center of rotation for Rotate event, center of box formed by 2 fingers + for Two Finger Tap event and position of the pressed finger for Press and Tap Event. + */ + const wxPoint& GetPosition() const; + + /** + Returns true if the event was the first in a gesture sequence. + */ + bool IsGestureStart() const; + + /** + Returns true if the event was the last in a gesture sequence. + */ + bool IsGestureEnd() const; + + /** + Sets the position where the event took effect, in client coordinates: position of Pan event, + center of zoom for Zoom event, center of rotation for Rotate event. + */ + void SetPosition(const wxPoint& pos); + + /** + Sets the event to be the first in a gesture sequence. + */ + void SetGestureStart(bool isStart = true); + + /** + Sets the event to be the last in a gesture sequence. + */ + void SetGestureEnd(bool isEnd = true); +}; + + +/** @class wxPanGestureEvent + + This event is generated when the user moves a finger on the surface. + + wxGTK also generates this event during mouse dragging (mouse motion while a left mouse button is depressed). + + Note that OSX requires the primary mouse button to be pressed while performing the finger movement. + + @beginEventTable{wxPanGestureEvent} + @event{EVT_GESTURE_PAN(id, func)} + Process a @c wxEVT_GESTURE_PAN. + @endEventTable + + @library{wxcore} + @category{events} + + @since 3.1.1 +*/ +class wxPanGestureEvent : class wxGestureEvent +{ +public: + /** + Constructor. + */ + wxPanGestureEvent(wxWindowID winid = 0); + + /** + Returns the horizontal component of the distance covered since the previous Pan event. + */ + int GetDeltaX() const; + + /** + Returns the vertical component of the distance covered since the previous Pan event. + */ + int GetDeltaY() const; + + /** + Sets the horizontal component of the distance covered since the previous Pan event. + */ + int SetDeltaX(int DeltaX); + + /** + Sets the vertical component of the distance covered since the previous Pan event. + */ + int SetDeltaY(int DeltaY); +}; + + +/** @class wxZoomGestureEvent + + This event is generated when two fingers pinch the surface to zoom in or out. + + @beginEventTable{wxZoomGestureEvent} + @event{EVT_GESTURE_ZOOM(id, func)} + Process a @c wxEVT_GESTURE_ZOOM. + @endEventTable + + @library{wxcore} + @category{events} + + @since 3.1.1 +*/ +class wxZoomGestureEvent : public wxGestureEvent +{ +public: + /** + Constructor. + */ + wxZoomGestureEvent(wxWindowID windid = 0); + + /** + Returns the zoom Factor since the gesture started. Hence, starting of the gesture + is considered as 1:1. A value greater than 1.0 means we should enlarge + (or zoom in), a value less than 1.0 means we should shrink (or zoom out). + */ + double GetZoomFactor() const; + + /** + Sets the zoom Factor. + */ + double SetZoomFactor() const; +}; + + +/** @class wxRotateGestureEvent + + This event is generated when two fingers move in opposite directions on the surface. + + @beginEventTable{wxRotateGestureEvent} + @event{EVT_GESTURE_ROTATE(id, func)} + Process a @c wxEVT_GESTURE_ROTATE. + @endEventTable + + @library{wxcore} + @category{events} + + @since 3.1.1 +*/ +class wxRotateGestureEvent : public wxGestureEvent +{ +public: + /** + Constructor. + */ + wxRotateGestureEvent(wxWindowID windid = 0); + + /** + Returns the total angle of rotation in radians in clockwise direction since the + gesture was first started i.e. when IsGestureStart() returned true. This value is always + greater than or equal to zero. + */ + double GetRotationAngle() const; + + /** + Sets the total angle of rotation in radians in clockwise direction since the + gesture was first started i.e. when IsGestureStart() returned true. This value is always + greater than or equal to zero. + */ + void SetRotationAngle(double rotationAngle); +}; + +/** @class wxTwoFingerTapEvent + + This event is generated when two fingers touch the surface at the same time. + + @beginEventTable{wxTwoFingerTapEvent} + @event{EVT_TWO_FINGER_TAP(id, func)} + Process a @c wxEVT_TWO_FINGER_TAP. + @endEventTable + + @library{wxcore} + @category{events} + + @since 3.1.1 +*/ +class wxTwoFingerTapEvent : public wxGestureEvent +{ +public: + /** + Constructor. + */ + wxTwoFingerTapEvent(wxWindowID windid = 0); +}; + +/** @class wxLongPressEvent + + This event is generated when one finger touches the surface and remains stationary. + + Note that currently it is only generated under wxGTK and wxOSX. + + wxGTK also generates this event when left mouse button is being pressed for some minimum duration of time. + + @beginEventTable{wxLongPressEvent} + @event{EVT_LONG_PRESS(id, func)} + Process a @c wxEVT_LONG_PRESS. + @endEventTable + + @library{wxcore} + @category{events} + + @since 3.1.1 +*/ +class wxLongPressEvent : public wxGestureEvent +{ +public: + /** + Constructor. + */ + wxLongPressEvent(wxWindowID windid = 0); +}; + +/** @class wxPressAndTapEvent + + This event is generated when the user press the surface with one finger and taps with another. + + Note that once started the event will also be generated when the finger that was pressed moves on surface. + + @beginEventTable{wxPressAndTapEvent} + @event{EVT_PRESS_AND_TAP(id, func)} + Process a @c wxEVT_PRESS_AND_TAP. + @endEventTable + + @library{wxcore} + @category{events} + + @since 3.1.1 +*/ +class wxPressAndTapEvent : public wxGestureEvent +{ +public: + /** + Constructor. + */ + wxPressAndTapEvent(wxWindowID windid = 0); +}; + #endif // wxUSE_GUI #if wxUSE_BASE diff --git a/samples/event/Makefile.in b/samples/event/Makefile.in index a790ba23cd..ea6a06a19e 100644 --- a/samples/event/Makefile.in +++ b/samples/event/Makefile.in @@ -49,7 +49,8 @@ EVENT_CXXFLAGS = -D__WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) \ $(SAMPLES_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) EVENT_OBJECTS = \ $(__event___win32rc) \ - event_event.o + event_event.o \ + event_gestures.o ### Conditionally set variables: ### @@ -177,6 +178,8 @@ event_sample_rc.o: $(srcdir)/../../samples/sample.rc event_event.o: $(srcdir)/event.cpp $(CXXC) -c -o $@ $(EVENT_CXXFLAGS) $(srcdir)/event.cpp +event_gestures.o: $(srcdir)/gestures.cpp + $(CXXC) -c -o $@ $(EVENT_CXXFLAGS) $(srcdir)/gestures.cpp # Include dependency info, if present: @IF_GNU_MAKE@-include ./.deps/*.d diff --git a/samples/event/event.bkl b/samples/event/event.bkl index e5b558fac3..6b830fbc66 100644 --- a/samples/event/event.bkl +++ b/samples/event/event.bkl @@ -4,7 +4,8 @@ - event.cpp + event.cpp gestures.cpp + gestures.h core base diff --git a/samples/event/event.cpp b/samples/event/event.cpp index ef19b8bb63..b397f828aa 100644 --- a/samples/event/event.cpp +++ b/samples/event/event.cpp @@ -35,6 +35,7 @@ #include #include +#include "gestures.h" // ---------------------------------------------------------------------------- // event constants @@ -144,6 +145,9 @@ public: void OnClickDynamicHandlerButton(wxCommandEvent& event); void OnClickStaticHandlerFrame(wxCommandEvent& event); + // Gesture + void OnGesture(wxCommandEvent& event); + private: // symbolic names for the status bar fields enum @@ -178,6 +182,8 @@ private: // the button used to highlight the event handlers execution order MyEvtTestButton *m_testBtn; + MyGestureFrame *m_gestureFrame; + // any class wishing to process wxWidgets events must use this macro wxDECLARE_EVENT_TABLE(); @@ -220,7 +226,8 @@ enum Event_Push, Event_Pop, Event_Custom, - Event_Test + Event_Test, + Event_Gesture }; // ---------------------------------------------------------------------------- @@ -252,6 +259,7 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(Event_Test, MyFrame::OnTest) EVT_MENU(Event_Push, MyFrame::OnPushEventHandler) EVT_MENU(Event_Pop, MyFrame::OnPopEventHandler) + EVT_MENU(Event_Gesture, MyFrame::OnGesture) EVT_UPDATE_UI(Event_Pop, MyFrame::OnUpdateUIPop) @@ -290,6 +298,8 @@ bool MyApp::OnInit() if ( !wxApp::OnInit() ) return false; + wxImage::AddHandler(new wxJPEGHandler); + // create the main application window MyFrame *frame = new MyFrame(wxT("Event wxWidgets Sample"), wxPoint(50, 50), wxSize(600, 340)); @@ -381,6 +391,8 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) menuEvent->AppendSeparator(); menuEvent->Append(Event_Custom, wxT("Fire c&ustom event\tCtrl-U"), wxT("Generate a custom event")); + menuEvent->Append(Event_Gesture, wxT("&Gesture events\tCtrl-G"), + wxT("Gesture event")); // now append the freshly created menu to the menu bar... wxMenuBar *menuBar = new wxMenuBar(); @@ -577,6 +589,12 @@ void MyFrame::OnPopEventHandler(wxCommandEvent& WXUNUSED(event)) #endif // wxUSE_STATUSBAR } +void MyFrame::OnGesture(wxCommandEvent& WXUNUSED(event)) +{ + m_gestureFrame = new MyGestureFrame(); + m_gestureFrame->Show(true); +} + void MyFrame::OnTest(wxCommandEvent& WXUNUSED(event)) { wxLogMessage(wxT("This is the test event handler in the main frame")); @@ -602,4 +620,3 @@ void MyFrame::OnProcessCustom(wxCommandEvent& WXUNUSED(event)) { wxLogMessage(wxT("Got a custom event!")); } - diff --git a/samples/event/event_vc7.vcproj b/samples/event/event_vc7.vcproj index cbf52c337f..0036a2c140 100644 --- a/samples/event/event_vc7.vcproj +++ b/samples/event/event_vc7.vcproj @@ -286,6 +286,17 @@ + + + + + + + + + + + + + + + + + + Add(myPanel, wxSizerFlags(1).Expand()); + sizer->Add(m_logText, wxSizerFlags().Expand()); + SetSizer(sizer); + + // Log to the text control + delete wxLog::SetActiveTarget(new wxLogTextCtrl(m_logText)); + + // Bind all gestures to the same event handler, which must run before + // the other handlers, to clear the log window + myPanel->Bind(wxEVT_GESTURE_PAN, &MyGestureFrame::OnGesture, this); + myPanel->Bind(wxEVT_GESTURE_ZOOM, &MyGestureFrame::OnGesture, this); + myPanel->Bind(wxEVT_GESTURE_ROTATE, &MyGestureFrame::OnGesture, this); + myPanel->Bind(wxEVT_TWO_FINGER_TAP, &MyGestureFrame::OnGesture, this); + myPanel->Bind(wxEVT_LONG_PRESS, &MyGestureFrame::OnGesture, this); + myPanel->Bind(wxEVT_PRESS_AND_TAP, &MyGestureFrame::OnGesture, this); + + Bind(wxEVT_CLOSE_WINDOW, &MyGestureFrame::OnQuit, this); +} + +MyGesturePanel::MyGesturePanel(MyGestureFrame *parent) : wxPanel(parent, wxID_ANY) +{ + // Load an image + if ( !m_bitmap.LoadFile("../image/horse.jpg", wxBITMAP_TYPE_JPEG) ) + { + wxLogError("Can't load the image"); + } + + else + { + Bind(wxEVT_PAINT, &MyGesturePanel::OnPaint, this); + } + + // Event handlers + Bind(wxEVT_GESTURE_PAN, &MyGesturePanel::OnPan, this); + Bind(wxEVT_GESTURE_ZOOM, &MyGesturePanel::OnZoom, this); + Bind(wxEVT_GESTURE_ROTATE, &MyGesturePanel::OnRotate, this); + Bind(wxEVT_TWO_FINGER_TAP, &MyGesturePanel::OnTwoFingerTap, this); + Bind(wxEVT_LONG_PRESS, &MyGesturePanel::OnLongPress, this); + Bind(wxEVT_PRESS_AND_TAP, &MyGesturePanel::OnPressAndTap, this); + + m_lastZoomFactor = 1.0; +} + +void MyGestureFrame::OnQuit(wxCloseEvent& WXUNUSED(event)) +{ + Destroy(); +} + +void MyGestureFrame::OnGesture(wxGestureEvent& event) +{ + if ( event.IsGestureStart() ) + m_logText->Clear(); + + event.Skip(); +} + +void MyGesturePanel::OnPaint(wxPaintEvent& WXUNUSED(event)) +{ + wxPaintDC paintDC(this); + + wxGCDC dc(paintDC); + + dc.Clear(); + + dc.SetTransformMatrix(m_affineMatrix); + dc.DrawBitmap(m_bitmap, wxRound(m_translateDistance.m_x), wxRound(m_translateDistance.m_y)); +} + +void MyGesturePanel::OnPan(wxPanGestureEvent& event) +{ + if ( event.IsGestureStart() ) + { + wxLogMessage("Pan gesture started\n"); + } + + wxLogMessage("Pan gesture performed with deltaX = %d and deltaY = %d, with current position (%d,%d)\n", + event.GetDeltaX(), event.GetDeltaY(), event.GetPosition().x, event.GetPosition().y); + + // Transform the distance using the tranpose of the matrix, + // in order to translate the image to match the screen coordinates + wxMatrix2D m; + m_affineMatrix.Get(&m, NULL); + + wxPoint2DDouble delta(m.m_11 * event.GetDeltaX() + m.m_12 * event.GetDeltaY(), + m.m_21 * event.GetDeltaX() + m.m_22 * event.GetDeltaY()); + + // Add it to the total translation + m_translateDistance += delta; + + if ( event.IsGestureEnd() ) + { + wxLogMessage("Pan gesture Ended\n"); + } + + Refresh(); +} + +void MyGesturePanel::OnZoom(wxZoomGestureEvent& event) +{ + if ( event.IsGestureStart() ) + { + wxLogMessage("Zoom gesture started\n"); + + m_lastZoomFactor = 1.0; + } + + wxLogMessage("Zoom gesture performed with zoom center at (%d, %d) and zoom Factor = %f\n", + event.GetPosition().x, event.GetPosition().y, event.GetZoomFactor()); + + const wxPoint& zoomCenter = event.GetPosition(); + + // Translate to zoom center + m_affineMatrix.Translate(zoomCenter.x, zoomCenter.y); + // Scale + m_affineMatrix.Scale(event.GetZoomFactor() / m_lastZoomFactor, event.GetZoomFactor() / m_lastZoomFactor); + // Translate back + m_affineMatrix.Translate(-zoomCenter.x, -zoomCenter.y); + + if ( event.IsGestureEnd() ) + { + wxLogMessage("Zoom gesture Ended\n"); + } + + m_lastZoomFactor = event.GetZoomFactor(); + + Refresh(); +} + +void MyGesturePanel::OnRotate(wxRotateGestureEvent& event) +{ + if ( event.IsGestureStart() ) + { + wxLogMessage("Rotate gesture started\n"); + + m_lastRotationAngle = 0.0; + } + + wxLogMessage("Rotate gesture performed with rotation center at (%d, %d) and cumulative rotation angle = %f\n", + event.GetPosition().x, event.GetPosition().y, event.GetRotationAngle()); + + const wxPoint& rotationCenter = event.GetPosition(); + + // Translate to rotation center + m_affineMatrix.Translate(rotationCenter.x, rotationCenter.y); + // Rotate + m_affineMatrix.Rotate(event.GetRotationAngle() - m_lastRotationAngle); + // Translate back + m_affineMatrix.Translate(-rotationCenter.x, -rotationCenter.y); + + if ( event.IsGestureEnd() ) + { + wxLogMessage("Rotate gesture Ended\n"); + } + + m_lastRotationAngle = event.GetRotationAngle(); + + Refresh(); +} + +void MyGesturePanel::OnTwoFingerTap(wxTwoFingerTapEvent& event) +{ + if ( event.IsGestureStart() ) + { + wxLogMessage("Two Finger Tap gesture gesture started\n"); + } + + wxLogMessage("Two Finger Tap gesture performed at (%d, %d)\n", event.GetPosition().x, event.GetPosition().y); + + if ( event.IsGestureEnd() ) + { + wxLogMessage("Two Finger Tap gesture Ended\n"); + } +} + +void MyGesturePanel::OnLongPress(wxLongPressEvent& event) +{ + if ( event.IsGestureStart() ) + { + wxLogMessage("Long Press gesture started\n"); + } + + wxLogMessage("Long Press gesture performed at (%d,%d)\n", event.GetPosition().x, event.GetPosition().y); + + if ( event.IsGestureEnd() ) + { + wxLogMessage("Long Press gesture Ended\n"); + } +} + +void MyGesturePanel::OnPressAndTap(wxPressAndTapEvent& event) +{ + if ( event.IsGestureStart() ) + { + wxLogMessage("Press and Tap gesture started\n"); + } + + wxLogMessage("Press and Tap gesture performed at (%d,%d)\n", event.GetPosition().x, event.GetPosition().y); + + if ( event.IsGestureEnd() ) + { + wxLogMessage("Press and Tap gesture Ended\n"); + } +} diff --git a/samples/event/gestures.h b/samples/event/gestures.h new file mode 100644 index 0000000000..c2add7f344 --- /dev/null +++ b/samples/event/gestures.h @@ -0,0 +1,39 @@ +#ifndef _WX_GESTURES_H_ +#define _WX_GESTURES_H_ + +#include "wx/wx.h" + +class MyGestureFrame : public wxFrame +{ +public: + MyGestureFrame(); + + void OnGesture(wxGestureEvent& event); + void OnQuit(wxCloseEvent& event); + +private: + wxTextCtrl *m_logText; +}; + +class MyGesturePanel : public wxPanel +{ +public: + MyGesturePanel(MyGestureFrame* parent); + + void OnPaint(wxPaintEvent& event); + void OnPan(wxPanGestureEvent& event); + void OnZoom(wxZoomGestureEvent& event); + void OnRotate(wxRotateGestureEvent& event); + void OnTwoFingerTap(wxTwoFingerTapEvent& event); + void OnLongPress(wxLongPressEvent& event); + void OnPressAndTap(wxPressAndTapEvent& event); + +private: + wxBitmap m_bitmap; + wxPoint2DDouble m_translateDistance; + wxAffineMatrix2D m_affineMatrix; + double m_lastZoomFactor; + double m_lastRotationAngle; +}; + +#endif // _WX_GESTURES_H_ diff --git a/samples/event/makefile.bcc b/samples/event/makefile.bcc index c079d2d558..62364b9296 100644 --- a/samples/event/makefile.bcc +++ b/samples/event/makefile.bcc @@ -36,7 +36,8 @@ EVENT_CXXFLAGS = $(__RUNTIME_LIBS_7) -I$(BCCDIR)\include $(__DEBUGINFO) \ -I$(SETUPHDIR) -I.\..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES_p) -I. \ $(__DLLFLAG_p) -I.\..\..\samples -DNOPCH $(CPPFLAGS) $(CXXFLAGS) EVENT_OBJECTS = \ - $(OBJS)\event_event.obj + $(OBJS)\event_event.obj \ + $(OBJS)\event_gestures.obj ### Conditionally set variables: ### @@ -237,3 +238,5 @@ $(OBJS)\event_sample.res: .\..\..\samples\sample.rc $(OBJS)\event_event.obj: .\event.cpp $(CXX) -q -c -P -o$@ $(EVENT_CXXFLAGS) .\event.cpp +$(OBJS)\event_gestures.obj: .\gestures.cpp + $(CXX) -q -c -P -o$@ $(EVENT_CXXFLAGS) .\gestures.cpp diff --git a/samples/event/makefile.gcc b/samples/event/makefile.gcc index 2e3508cef9..105c791901 100644 --- a/samples/event/makefile.gcc +++ b/samples/event/makefile.gcc @@ -30,7 +30,8 @@ EVENT_CXXFLAGS = $(__DEBUGINFO) $(__OPTIMIZEFLAG_2) $(__THREADSFLAG) \ $(__EXCEPTIONSFLAG_6) -Wno-ctor-dtor-privacy $(CPPFLAGS) $(CXXFLAGS) EVENT_OBJECTS = \ $(OBJS)\event_sample_rc.o \ - $(OBJS)\event_event.o + $(OBJS)\event_event.o \ + $(OBJS)\event_gestures.o ### Conditionally set variables: ### @@ -226,6 +227,9 @@ $(OBJS)\event_sample_rc.o: ./../../samples/sample.rc $(OBJS)\event_event.o: ./event.cpp $(CXX) -c -o $@ $(EVENT_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\event_gestures.o: ./gestures.cpp + $(CXX) -c -o $@ $(EVENT_CXXFLAGS) $(CPPDEPS) $< + .PHONY: all clean diff --git a/samples/event/makefile.vc b/samples/event/makefile.vc index cf6ad41a6c..bbf86a28b9 100644 --- a/samples/event/makefile.vc +++ b/samples/event/makefile.vc @@ -31,7 +31,8 @@ EVENT_CXXFLAGS = /M$(__RUNTIME_LIBS_10)$(__DEBUGRUNTIME_4) /DWIN32 \ /I.\..\..\samples /DNOPCH $(__RTTIFLAG_11) $(__EXCEPTIONSFLAG_12) \ $(CPPFLAGS) $(CXXFLAGS) EVENT_OBJECTS = \ - $(OBJS)\event_event.obj + $(OBJS)\event_event.obj \ + $(OBJS)\event_gestures.obj EVENT_RESOURCES = \ $(OBJS)\event_sample.res @@ -360,3 +361,5 @@ $(OBJS)\event_sample.res: .\..\..\samples\sample.rc $(OBJS)\event_event.obj: .\event.cpp $(CXX) /c /nologo /TP /Fo$@ $(EVENT_CXXFLAGS) .\event.cpp +$(OBJS)\event_gestures.obj: .\gestures.cpp + $(CXX) /c /nologo /TP /Fo$@ $(EVENT_CXXFLAGS) .\gestures.cpp diff --git a/src/common/event.cpp b/src/common/event.cpp index ee9f69aec1..d32a21d9d6 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -103,6 +103,13 @@ wxIMPLEMENT_DYNAMIC_CLASS(wxMouseCaptureChangedEvent, wxEvent); wxIMPLEMENT_DYNAMIC_CLASS(wxMouseCaptureLostEvent, wxEvent); wxIMPLEMENT_DYNAMIC_CLASS(wxClipboardTextEvent, wxCommandEvent); + wxIMPLEMENT_DYNAMIC_CLASS(wxGestureEvent, wxEvent); + wxIMPLEMENT_DYNAMIC_CLASS(wxPanGestureEvent, wxGestureEvent); + wxIMPLEMENT_DYNAMIC_CLASS(wxZoomGestureEvent, wxGestureEvent); + wxIMPLEMENT_DYNAMIC_CLASS(wxRotateGestureEvent, wxGestureEvent); + wxIMPLEMENT_DYNAMIC_CLASS(wxTwoFingerTapEvent, wxGestureEvent); + wxIMPLEMENT_DYNAMIC_CLASS(wxLongPressEvent, wxGestureEvent); + wxIMPLEMENT_DYNAMIC_CLASS(wxPressAndTapEvent, wxGestureEvent); #endif // wxUSE_GUI #if wxUSE_BASE @@ -258,6 +265,14 @@ wxDEFINE_EVENT( wxEVT_SCROLLWIN_PAGEDOWN, wxScrollWinEvent ); wxDEFINE_EVENT( wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEvent ); wxDEFINE_EVENT( wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEvent ); +// Gesture events +wxDEFINE_EVENT( wxEVT_GESTURE_PAN, wxPanGestureEvent ); +wxDEFINE_EVENT( wxEVT_GESTURE_ZOOM, wxZoomGestureEvent ); +wxDEFINE_EVENT( wxEVT_GESTURE_ROTATE, wxRotateGestureEvent ); +wxDEFINE_EVENT( wxEVT_TWO_FINGER_TAP, wxTwoFingerTapEvent ); +wxDEFINE_EVENT( wxEVT_LONG_PRESS, wxLongPressEvent ); +wxDEFINE_EVENT( wxEVT_PRESS_AND_TAP, wxPressAndTapEvent ); + // System events wxDEFINE_EVENT( wxEVT_SIZE, wxSizeEvent ); wxDEFINE_EVENT( wxEVT_SIZING, wxSizeEvent ); diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index bcf04ba8ad..064ba0fdec 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -228,6 +228,23 @@ static GList* gs_sizeRevalidateList; static bool gs_inSizeAllocate; #endif +#if GTK_CHECK_VERSION(3,14,0) +// 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 // GTK_CHECK_VERSION(3,14,0) + //----------------------------------------------------------------------------- // debug //----------------------------------------------------------------------------- @@ -2430,6 +2447,13 @@ void wxWindowGTK::Init() m_imKeyEvent = NULL; m_dirtyTabOrder = false; + +#if GTK_CHECK_VERSION(3,14,0) + m_touchCount = 0; + m_lastTouchTime = 0; + m_allowedGestures = 0; + m_activeGestures = 0; +#endif // GTK_CHECK_VERSION(3,14,0) } wxWindowGTK::wxWindowGTK() @@ -2828,6 +2852,455 @@ static gboolean source_dispatch(GSource*, GSourceFunc, void*) } } +#if GTK_CHECK_VERSION(3,14,0) +// 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) +{ + // Do not process horizontal pan, if there was no "pan" signal for it. + if ( !(win->m_allowedGestures & horizontal_pan) ) + { + return; + } + + gdouble x, y; + + if ( !gtk_gesture_get_point(gesture, sequence, &x, &y) ) + { + return; + } + + win->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) +{ + // Do not process vertical pan, if there was no "pan" signal for it. + if ( !(win->m_allowedGestures & vertical_pan) ) + { + return; + } + + gdouble x, y; + + if ( !gtk_gesture_get_point(gesture, sequence, &x, &y) ) + { + return; + } + + win->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))); + + // 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: + win->m_allowedGestures |= vertical_pan; + event.SetDeltaY(-delta); + break; + + case GTK_PAN_DIRECTION_DOWN: + win->m_allowedGestures |= vertical_pan; + event.SetDeltaY(delta); + break; + + case GTK_PAN_DIRECTION_RIGHT: + win->m_allowedGestures |= horizontal_pan; + event.SetDeltaX(delta); + break; + + case GTK_PAN_DIRECTION_LEFT: + win->m_allowedGestures |= horizontal_pan; + event.SetDeltaX(-delta); + 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( !(win->m_activeGestures & press_and_tap) ) + { + win->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); + + // Cancel "Two FInger Tap Event" if scale has changed + if ( wxRound(scale * 1000) != wxRound(gs_lastScale * 1000) ) + { + win->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); + + double lastX = win->m_lastTouchPoint.x; + double lastY = win->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); + + switch ( win->m_gestureState ) + { + case begin: + event.SetGestureStart(); + break; + + case update: + // Update touch point as the touch corresponding to "press" is moving + if ( win->m_touchSequence == gdk_event->sequence ) + { + win->m_lastTouchPoint.x = gdk_event->x; + win->m_lastTouchPoint.y = gdk_event->y; + } + break; + + case end: + event.SetGestureEnd(); + break; + } + + event.SetPosition(win->m_lastTouchPoint); + + win->GTKProcessEvent(event); +} + +static void +touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGTK* win) +{ + switch ( gdk_event->type ) + { + case GDK_TOUCH_BEGIN: + win->m_touchCount++; + + win->m_allowedGestures &= ~two_finger_tap; + + if ( win->m_touchCount == 1 ) + { + win->m_lastTouchTime = gdk_event->time; + win->m_lastTouchPoint.x = gdk_event->x; + win->m_lastTouchPoint.y = gdk_event->y; + + // Save the sequence which identifies touch corresponding to "press" + win->m_touchSequence = gdk_event->sequence; + + // "Press and Tap Event" may occur in future + win->m_allowedGestures |= press_and_tap; + } + + // Check if two fingers are placed together .i.e difference between their time stamps is <= 200 milliseconds + else if ( win->m_touchCount == 2 && gdk_event->time - win->m_lastTouchTime <= wxTwoFingerTimeInterval ) + { + // "Two Finger Tap Event" may be possible in the future + win->m_allowedGestures |= two_finger_tap; + + // Cancel "Press and Tap Event" + win->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 ( (win->m_activeGestures & press_and_tap) && gdk_event->sequence == win->m_touchSequence ) + { + win->m_gestureState = update; + wxEmitPressAndTapEvent(gdk_event, win); + } + break; + + case GDK_TOUCH_END: + case GDK_TOUCH_CANCEL: + win->m_touchCount--; + + if ( win->m_touchCount == 1 ) + { + win->m_lastTouchTime = gdk_event->time; + + // If the touch corresponding to "press" is present and "tap" is produced by some ather touch + if ( (win->m_allowedGestures & press_and_tap) && gdk_event->sequence != win->m_touchSequence ) + { + // Press and Tap gesture becomes active now + if ( !(win->m_activeGestures & press_and_tap) ) + { + win->m_gestureState = begin; + win->m_activeGestures |= press_and_tap; + } + + else + { + win->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 ( (win->m_allowedGestures & two_finger_tap) && !win->m_touchCount + && gdk_event->time - win->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 ( (win->m_activeGestures & press_and_tap) && gdk_event->sequence == win->m_touchSequence ) + { + win->m_gestureState = end; + + win->m_activeGestures &= ~press_and_tap; + + win->m_allowedGestures &= ~press_and_tap; + + wxEmitPressAndTapEvent(gdk_event, win); + } + break; + + default: + break; + } +} +#endif // GTK_CHECK_VERSION(3,14,0) + void wxWindowGTK::ConnectWidget( GtkWidget *widget ) { static bool isSourceAttached; @@ -2870,6 +3343,81 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget ) G_CALLBACK (gtk_window_enter_callback), this); g_signal_connect (widget, "leave_notify_event", G_CALLBACK (gtk_window_leave_callback), this); + +#if GTK_CHECK_VERSION(3,14,0) + if ( gtk_check_version(3, 14, 0) == NULL ) + { + GtkGesture* vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL); + + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(vertical_pan_gesture), GTK_PHASE_TARGET); + + g_signal_connect (vertical_pan_gesture, "begin", + G_CALLBACK(pan_gesture_begin_callback), this); + g_signal_connect (vertical_pan_gesture, "pan", + G_CALLBACK(pan_gesture_callback), this); + g_signal_connect (vertical_pan_gesture, "end", + G_CALLBACK(vertical_pan_gesture_end_callback), this); + g_signal_connect (vertical_pan_gesture, "cancel", + G_CALLBACK(vertical_pan_gesture_end_callback), this); + + GtkGesture* 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(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(horizontal_pan_gesture), GTK_PHASE_TARGET); + + g_signal_connect (horizontal_pan_gesture, "begin", + G_CALLBACK(pan_gesture_begin_callback), this); + g_signal_connect (horizontal_pan_gesture, "pan", + G_CALLBACK(pan_gesture_callback), this); + g_signal_connect (horizontal_pan_gesture, "end", + G_CALLBACK(horizontal_pan_gesture_end_callback), this); + g_signal_connect (horizontal_pan_gesture, "cancel", + G_CALLBACK(horizontal_pan_gesture_end_callback), this); + + GtkGesture* zoom_gesture = gtk_gesture_zoom_new(widget); + + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(zoom_gesture), GTK_PHASE_TARGET); + + g_signal_connect (zoom_gesture, "begin", + G_CALLBACK(zoom_gesture_begin_callback), this); + g_signal_connect (zoom_gesture, "scale-changed", + G_CALLBACK(zoom_gesture_callback), this); + g_signal_connect (zoom_gesture, "end", + G_CALLBACK(zoom_gesture_end_callback), this); + g_signal_connect (zoom_gesture, "cancel", + G_CALLBACK(zoom_gesture_end_callback), this); + + GtkGesture* rotate_gesture = gtk_gesture_rotate_new(widget); + + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(rotate_gesture), GTK_PHASE_TARGET); + + g_signal_connect (rotate_gesture, "begin", + G_CALLBACK(rotate_gesture_begin_callback), this); + g_signal_connect (rotate_gesture, "angle-changed", + G_CALLBACK(rotate_gesture_callback), this); + g_signal_connect (rotate_gesture, "end", + G_CALLBACK(rotate_gesture_end_callback), this); + g_signal_connect (rotate_gesture, "cancel", + G_CALLBACK(rotate_gesture_end_callback), this); + + GtkGesture* 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(long_press_gesture), TRUE) + // which will allow "pressed" signal only for Touch events. + + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(long_press_gesture), GTK_PHASE_TARGET); + + g_signal_connect (long_press_gesture, "pressed", + G_CALLBACK(long_press_gesture_callback), this); + g_signal_connect (widget, "touch-event", + G_CALLBACK(touch_callback), this); + } +#endif // GTK_CHECK_VERSION(3,14,0) } void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 68f0fedcc4..4b88bb2bfd 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -205,6 +205,85 @@ bool gs_insideCaptureChanged = false; } // anonymous namespace +namespace +{ + +// Class used to dynamically load gestures related API functions. +#ifdef WM_GESTURE +class GestureFuncs +{ +public: + static bool IsOk() + { + if ( !ms_gestureSymbolsLoaded ) + LoadGestureSymbols(); + + return ms_pfnGetGestureInfo && ms_pfnCloseGestureInfoHandle && ms_pfnSetGestureConfig; + } + + typedef BOOL (WINAPI *GetGestureInfo_t)(HGESTUREINFO, PGESTUREINFO); + + static GetGestureInfo_t GetGestureInfo() + { + if ( !ms_gestureSymbolsLoaded ) + LoadGestureSymbols(); + + return ms_pfnGetGestureInfo; + } + + typedef BOOL (WINAPI *CloseGestureInfoHandle_t)(HGESTUREINFO); + + static CloseGestureInfoHandle_t CloseGestureInfoHandle() + { + if ( !ms_gestureSymbolsLoaded ) + LoadGestureSymbols(); + + return ms_pfnCloseGestureInfoHandle; + } + + typedef BOOL (WINAPI *SetGestureConfig_t)(HWND, DWORD, UINT, PGESTURECONFIG, UINT); + + static SetGestureConfig_t SetGestureConfig() + { + if ( !ms_gestureSymbolsLoaded ) + LoadGestureSymbols(); + + return ms_pfnSetGestureConfig; + } + +private: + static void LoadGestureSymbols() + { + wxDynamicLibrary dll("User32.dll"); + + wxDL_INIT_FUNC(ms_pfn, GetGestureInfo, dll); + wxDL_INIT_FUNC(ms_pfn, CloseGestureInfoHandle, dll); + wxDL_INIT_FUNC(ms_pfn, SetGestureConfig, dll); + + ms_gestureSymbolsLoaded = true; + } + + static GetGestureInfo_t ms_pfnGetGestureInfo; + static CloseGestureInfoHandle_t ms_pfnCloseGestureInfoHandle; + static SetGestureConfig_t ms_pfnSetGestureConfig; + + static bool ms_gestureSymbolsLoaded; + +}; + +GestureFuncs::GetGestureInfo_t + GestureFuncs::ms_pfnGetGestureInfo = NULL; +GestureFuncs::CloseGestureInfoHandle_t + GestureFuncs::ms_pfnCloseGestureInfoHandle = NULL; +GestureFuncs::SetGestureConfig_t + GestureFuncs::ms_pfnSetGestureConfig = NULL; + +bool GestureFuncs::ms_gestureSymbolsLoaded = false; + +#endif // WM_GESTURE + +} // anonymous namespace + // --------------------------------------------------------------------------- // private functions // --------------------------------------------------------------------------- @@ -3124,6 +3203,97 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, } break; +#ifdef WM_GESTURE + case WM_GESTURE: + { + if ( !GestureFuncs::IsOk() ) + { + processed = false; + break; + } + + // gestureInfo will contain the information about the gesture + GESTUREINFO gestureInfo = {0}; + gestureInfo.cbSize = sizeof(GESTUREINFO); + + // This should fill gestureInfo with the gesture details + if ( !GestureFuncs::GetGestureInfo()((HGESTUREINFO)lParam, &gestureInfo) ) + { + wxLogLastError("GetGestureInfo"); + processed = false; + break; + } + + if ( gestureInfo.hwndTarget != GetHWND() ) + { + wxLogDebug("This is Not the window targeted by this gesture!"); + } + + int x = gestureInfo.ptsLocation.x; + int y = gestureInfo.ptsLocation.y; + ScreenToClient(&x, &y); + + // dwID field is used to determine the type of gesture + switch ( gestureInfo.dwID ) + { + // Pan gesture + case GID_PAN: + { + // (x,y) is the current position of the pan + processed = HandlePanGesture(x, y, gestureInfo.dwFlags); + break; + } + + // Zoom gesture + case GID_ZOOM: + { + // (x,y) is the mid-point of 2 fingers + // ullArgument field is a 64-bit unsigned integer, but the relevant information + // is in it's lower 4 bytes and represents distance between the fingers + // this is used to extract those lower 4 bytes + DWORD fingerDistance = (DWORD)((gestureInfo.ullArguments) & 0x00000000ffffffff); + processed = HandleZoomGesture(x, y, fingerDistance, gestureInfo.dwFlags); + break; + } + + // Rotate gesture + case GID_ROTATE: + { + // (x,y) is the center point of rotation + // Again, we need the lower 4 bytes and this will used as an argument + // to obtain the angle to rotate + DWORD angleArgument = (DWORD)((gestureInfo.ullArguments) & 0x00000000ffffffff); + processed = HandleRotateGesture(x, y, angleArgument, gestureInfo.dwFlags); + break; + } + + // Two Finger tap gesture + case GID_TWOFINGERTAP: + { + processed = HandleTwoFingerTap(x, y, gestureInfo.dwFlags); + break; + } + + // Press and Tap gesture + case GID_PRESSANDTAP: + { + processed = HandlePressAndTap(x, y, gestureInfo.dwFlags); + break; + } + } + + if ( processed ) + { + // If processed, we must call this to avoid memory leaks + if ( !GestureFuncs::CloseGestureInfoHandle()((HGESTUREINFO)lParam) ) + { + wxLogLastError("CloseGestureInfoHandle"); + } + } + } + break; +#endif // WM_GESTURE + // CTLCOLOR messages are sent by children to query the parent for their // colors case WM_CTLCOLORMSGBOX: @@ -3660,6 +3830,20 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, return false; } +#ifdef WM_GESTURE + if ( GestureFuncs::IsOk() ) + { + // Configure to receive all gestures + GESTURECONFIG gestureConfig = {0, GC_ALLGESTURES, 0}; + + // This functions sets the configuration to the window. Second argument is reserved to be 0. + // Third argument is the number of elements in gestureConfig array, currently equal to 1 + if ( !GestureFuncs::SetGestureConfig()(m_hWnd, 0, 1, &gestureConfig, sizeof(GESTURECONFIG)) ) + { + wxLogLastError("SetGestureConfig"); + } + } +#endif // WM_GESTURE SubclassWin(m_hWnd); @@ -5470,6 +5654,181 @@ void wxWindowMSW::GenerateMouseLeave() (void)HandleWindowEvent(event); } +#ifdef WM_GESTURE +// --------------------------------------------------------------------------- +// Gesture events +// --------------------------------------------------------------------------- + +bool wxWindowMSW::HandlePanGesture(int x, int y, WXDWORD flags) +{ + // wxEVT_GESTURE_PAN + wxPanGestureEvent event(GetId()); + + // These are used to calculate the pan delta + static int s_previousLocationX, s_previousLocationY; + + // This flag indicates that the gesture has just started + // Store the current point to determine the pan delta later on + if ( flags & GF_BEGIN ) + { + s_previousLocationX = x; + s_previousLocationY = y; + event.SetGestureStart(); + } + + if ( flags & GF_END ) + { + event.SetGestureEnd(); + } + + // Determine the horizontal and vertical changes + int DeltaX = x - s_previousLocationX, DeltaY = y - s_previousLocationY; + + event.SetEventObject(this); + event.SetTimestamp(::GetMessageTime()); + event.SetPosition(wxPoint(x, y)); + event.SetDeltaX(DeltaX); + event.SetDeltaY(DeltaY); + + // Update the last gesture event point + s_previousLocationX = x; + s_previousLocationY = y; + + return HandleWindowEvent(event); +} + +bool wxWindowMSW::HandleZoomGesture(int x, int y, WXDWORD fingerDistance, WXDWORD flags) +{ + // wxEVT_GESTURE_ZOOM + wxZoomGestureEvent event(GetId()); + + // These are used to calculate the center of the zoom and zoom factor + static int s_previousLocationX, s_previousLocationY, s_intialFingerDistance; + + // This flag indicates that the gesture has just started + // Store the current point and distance between the fingers for future calculations + if ( flags & GF_BEGIN ) + { + s_previousLocationX = x; + s_previousLocationY = y; + s_intialFingerDistance = fingerDistance; + event.SetGestureStart(); + } + + if ( flags & GF_END ) + { + event.SetGestureEnd(); + } + + // Calculate center point of the zoom + // Human beings are not very good at moving two fingers at exactly the same rate outwards/inwards + // There is usually some error, which can cause the center to shift slightly + // So, it is recommended to take the average of center of fingers in the current and last positions + wxPoint pt; + pt.x = (s_previousLocationX + x) / 2; + pt.y = (s_previousLocationY + y) / 2; + + double zoomFactor = (double) fingerDistance / (double) s_intialFingerDistance; + + event.SetZoomFactor(zoomFactor); + event.SetEventObject(this); + event.SetTimestamp(::GetMessageTime()); + + // This is not a gesture point but the center of a zoom + event.SetPosition(pt); + + // Update gesture event point + s_previousLocationX = x; + s_previousLocationY = y; + + return HandleWindowEvent(event); +} + +bool wxWindowMSW::HandleRotateGesture(int x, int y, WXDWORD angleArgument, WXDWORD flags) +{ + // wxEVT_GESTURE_ROTATE + wxRotateGestureEvent event(GetId()); + + // This flag indicates that the gesture has just started + if ( flags & GF_BEGIN ) + { + event.SetGestureStart(); + } + + else + { + // Use angleArgument to obtain the cumulative angle since the gesture was first + // started. This angle is in radians + // MSW returns negative angle for clockwise rotation and positive otherwise + // So, multiply angle by -1 for positive angle for clockwise and negative in case of counterclockwise + double angle = -GID_ROTATE_ANGLE_FROM_ARGUMENT(angleArgument); + + // If the rotation is anti-clockwise convert the angle to its corresponding positive value in a clockwise sense. + if ( angle < 0 ) + { + angle += 2 * M_PI; + } + + // Set the angle + event.SetRotationAngle(angle); + } + + if ( flags & GF_END ) + { + event.SetGestureEnd(); + } + + event.SetEventObject(this); + event.SetTimestamp(::GetMessageTime()); + event.SetPosition(wxPoint(x, y)); + + return HandleWindowEvent(event); +} + +bool wxWindowMSW::HandleTwoFingerTap(int x, int y, WXDWORD flags) +{ + // wxEVT_TWO_FINGER_TAP + wxTwoFingerTapEvent event(GetId()); + + if ( flags & GF_BEGIN ) + { + event.SetGestureStart(); + } + + event.SetEventObject(this); + event.SetTimestamp(::GetMessageTime()); + event.SetPosition(wxPoint(x, y)); + + if ( flags & GF_END ) + { + event.SetGestureEnd(); + } + + return HandleWindowEvent(event); +} + +bool wxWindowMSW::HandlePressAndTap(int x, int y, WXDWORD flags) +{ + wxPressAndTapEvent event(GetId()); + + if ( flags & GF_BEGIN ) + { + event.SetGestureStart(); + } + + event.SetEventObject(this); + event.SetTimestamp(::GetMessageTime()); + event.SetPosition(wxPoint(x, y)); + + if ( flags & GF_END ) + { + event.SetGestureEnd(); + } + + return HandleWindowEvent(event); +} +#endif // WM_GESTURE + // --------------------------------------------------------------------------- // keyboard handling // --------------------------------------------------------------------------- diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 3a34b6fc65..16f44d1eec 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -1042,6 +1042,71 @@ void wxOSX_insertText(NSView* self, SEL _cmd, NSString* text) impl->insertText(text, self, _cmd); } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 +void wxOSX_panGestureEvent(NSView* self, SEL _cmd, NSPanGestureRecognizer* panGestureRecognizer) +{ + wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); + if ( impl == NULL ) + return; + + impl->PanGestureEvent(panGestureRecognizer); +} + +void wxOSX_zoomGestureEvent(NSView* self, SEL _cmd, NSMagnificationGestureRecognizer* magnificationGestureRecognizer) +{ + wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); + if ( impl == NULL ) + return; + + impl->ZoomGestureEvent(magnificationGestureRecognizer); +} + +void wxOSX_rotateGestureEvent(NSView* self, SEL _cmd, NSRotationGestureRecognizer* rotationGestureRecognizer) +{ + wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); + if ( impl == NULL ) + return; + + impl->RotateGestureEvent(rotationGestureRecognizer); +} + +void wxOSX_longPressEvent(NSView* self, SEL _cmd, NSPressGestureRecognizer* pressGestureRecognizer) +{ + wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); + if ( impl ==NULL ) + return; + + impl->LongPressEvent(pressGestureRecognizer); +} + +void wxOSX_touchesBegan(NSView* self, SEL _cmd, NSEvent *event) +{ + wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); + if ( impl == NULL ) + return; + + impl->TouchesBegan(event); +} + +void wxOSX_touchesMoved(NSView* self, SEL _cmd, NSEvent *event) +{ + wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); + if ( impl == NULL ) + return; + + impl->TouchesMoved(event); +} + +void wxOSX_touchesEnded(NSView* self, SEL _cmd, NSEvent *event) +{ + wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); + if ( impl == NULL ) + return; + + impl->TouchesEnded(event); +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + BOOL wxOSX_performKeyEquivalent(NSView* self, SEL _cmd, NSEvent *event) { wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); @@ -1428,6 +1493,408 @@ void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd) m_lastKeyDownEvent = NULL; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 +void wxWidgetCocoaImpl::PanGestureEvent(NSPanGestureRecognizer* panGestureRecognizer) +{ + NSGestureRecognizerState gestureState; + + switch ( [panGestureRecognizer state] ) + { + case NSGestureRecognizerStateBegan: + gestureState = NSGestureRecognizerStateBegan; + break; + case NSGestureRecognizerStateChanged: + break; + case NSGestureRecognizerStateEnded: + case NSGestureRecognizerStateCancelled: + gestureState = NSGestureRecognizerStateEnded; + break; + // Do not process any other states + default: + return; + } + + wxPanGestureEvent wxevent(GetWXPeer()->GetId()); + wxevent.SetEventObject(GetWXPeer()); + + NSPoint nspoint = [panGestureRecognizer locationInView:m_osxView]; + wxPoint pt = wxFromNSPoint(m_osxView, nspoint); + + wxevent.SetPosition(pt); + + nspoint = [panGestureRecognizer translationInView:m_osxView]; + pt = wxFromNSPoint(m_osxView, nspoint); + + static int s_lastLocationX = 0, s_lastLocationY = 0; + + if ( gestureState == NSGestureRecognizerStateBegan ) + { + wxevent.SetGestureStart(); + s_lastLocationX = 0; + s_lastLocationY = 0; + } + + if ( gestureState == NSGestureRecognizerStateEnded ) + { + wxevent.SetGestureEnd(); + } + + // Set the offsets + wxevent.SetDeltaX(pt.x - s_lastLocationX); + wxevent.SetDeltaY(pt.y - s_lastLocationY); + + s_lastLocationX = pt.x; + s_lastLocationY = pt.y; + + GetWXPeer()->HandleWindowEvent(wxevent); +} + +void wxWidgetCocoaImpl::ZoomGestureEvent(NSMagnificationGestureRecognizer* magnificationGestureRecognizer) +{ + NSGestureRecognizerState gestureState; + + switch ( [magnificationGestureRecognizer state] ) + { + case NSGestureRecognizerStateBegan: + gestureState = NSGestureRecognizerStateBegan; + break; + case NSGestureRecognizerStateChanged: + break; + case NSGestureRecognizerStateEnded: + case NSGestureRecognizerStateCancelled: + gestureState = NSGestureRecognizerStateEnded; + break; + // Do not process any other states + default: + return; + } + + wxZoomGestureEvent wxevent(GetWXPeer()->GetId()); + wxevent.SetEventObject(GetWXPeer()); + + NSPoint nspoint = [magnificationGestureRecognizer locationInView:m_osxView]; + wxPoint pt = wxFromNSPoint(m_osxView, nspoint); + + wxevent.SetPosition(pt); + + if ( gestureState == NSGestureRecognizerStateBegan ) + { + wxevent.SetGestureStart(); + } + + if ( gestureState == NSGestureRecognizerStateEnded ) + { + wxevent.SetGestureEnd(); + } + + double magnification = [magnificationGestureRecognizer magnification]; + + // Add 1.0 get the magnification. + magnification += 1.0; + + wxevent.SetZoomFactor(magnification); + + GetWXPeer()->HandleWindowEvent(wxevent); +} + +void wxWidgetCocoaImpl::RotateGestureEvent(NSRotationGestureRecognizer* rotationGestureRecognizer) +{ + NSGestureRecognizerState gestureState; + + switch ( [rotationGestureRecognizer state] ) + { + case NSGestureRecognizerStateBegan: + gestureState = NSGestureRecognizerStateBegan; + break; + case NSGestureRecognizerStateChanged: + break; + case NSGestureRecognizerStateEnded: + case NSGestureRecognizerStateCancelled: + gestureState = NSGestureRecognizerStateEnded; + break; + // Do not process any other states + default: + return; + } + + wxRotateGestureEvent wxevent(GetWXPeer()->GetId()); + wxevent.SetEventObject(GetWXPeer()); + + NSPoint nspoint = [rotationGestureRecognizer locationInView:m_osxView]; + wxPoint pt = wxFromNSPoint(m_osxView, nspoint); + + wxevent.SetPosition(pt); + + if ( gestureState == NSGestureRecognizerStateBegan ) + { + wxevent.SetGestureStart(); + } + + if ( gestureState == NSGestureRecognizerStateEnded ) + { + wxevent.SetGestureEnd(); + } + // Multiply the returned rotation angle with -1 to obtain the angle in a clockwise sense. + double angle = -[rotationGestureRecognizer rotation]; + + // If the rotation is anti-clockwise convert the angle to its corresponding positive value in a clockwise sense. + if ( angle < 0 ) + { + angle += 2 * M_PI; + } + + wxevent.SetRotationAngle(angle); + + GetWXPeer()->HandleWindowEvent(wxevent); +} + +void wxWidgetCocoaImpl::LongPressEvent(NSPressGestureRecognizer* pressGestureRecognizer) +{ + NSGestureRecognizerState gestureState; + + switch ( [pressGestureRecognizer state] ) + { + case NSGestureRecognizerStateBegan: + gestureState = NSGestureRecognizerStateBegan; + break; + case NSGestureRecognizerStateChanged: + break; + case NSGestureRecognizerStateEnded: + case NSGestureRecognizerStateCancelled: + gestureState = NSGestureRecognizerStateEnded; + break; + // Do not process any other states + default: + return; + } + + wxLongPressEvent wxevent(GetWXPeer()->GetId()); + wxevent.SetEventObject(GetWXPeer()); + + NSPoint nspoint = [pressGestureRecognizer locationInView:m_osxView]; + wxPoint pt = wxFromNSPoint(m_osxView, nspoint); + + wxevent.SetPosition(pt); + + if ( gestureState == NSGestureRecognizerStateBegan ) + { + wxevent.SetGestureStart(); + } + + if ( gestureState == NSGestureRecognizerStateEnded ) + { + wxevent.SetGestureEnd(); + } + + GetWXPeer()->HandleWindowEvent(wxevent); +} + +enum TrackedGestures +{ + two_finger_tap = 0x0001, + press_and_tap = 0x0002 +}; + +void wxWidgetCocoaImpl::TouchesBegan(WX_NSEvent event) +{ + NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:m_osxView]; + + m_touchCount += touches.count; + m_allowedGestures &= ~two_finger_tap; + + // Check if 2 fingers are placed together + if ( m_touchCount == 2 && touches.count == 2 ) + { + // Two Finger Tap Event may occur in future + m_allowedGestures |= two_finger_tap; + + // Cancel Press and Tap Event + m_allowedGestures &= ~press_and_tap; + + return; + } + + // Time of event in milliseconds + const unsigned int eventTimeStamp = event.timestamp * 1000 + 0.5; + + if ( m_touchCount == 1 ) + { + // Save the time of event + m_lastTouchTime = eventTimeStamp; + + // Press and Tap may occur in future + m_allowedGestures |= press_and_tap; + + NSArray* array = [touches allObjects]; + + // Save the touch corresponding to "press" + m_initialTouch = [[array objectAtIndex:0] copy]; + } + + touches = [event touchesMatchingPhase:NSTouchPhaseStationary inView:m_osxView]; + + // Check if 2 fingers are placed within the time interval of 200 milliseconds + if ( m_touchCount == 2 && touches.count == 1 && eventTimeStamp - m_lastTouchTime <= wxTwoFingerTimeInterval ) + { + // Two Finger Tap Event may occur in future + m_allowedGestures |= two_finger_tap; + + // Cancel Press and Tap + m_allowedGestures &= ~press_and_tap; + + [m_initialTouch release]; + } +} + +void wxWidgetCocoaImpl::TouchesMoved(WX_NSEvent event) +{ + // Cancel Two Finger Tap Event if there is any movement + m_allowedGestures &= ~two_finger_tap; + + if ( !(m_allowedGestures & press_and_tap) ) + { + return; + } + + NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:m_osxView]; + + NSArray* array = [touches allObjects]; + + // Iterate through all moving touches to check if the touch corresponding to "press" + // in Press and Tap event is moving. + for ( int i = 0; i < [array count]; ++i ) + { + NSTouch* touch = [array objectAtIndex:i]; + + // Check if this touch and m_initialTouch are same + if ( [touch.identity isEqual:m_initialTouch.identity] ) + { + // Process Press and Tap Event if the touch corresponding to "press" is moving + // and the gesture is active. + if ( m_activeGestures & press_and_tap ) + { + wxPressAndTapEvent wxevent(GetWXPeer()->GetId()); + wxevent.SetEventObject(GetWXPeer()); + + // Get the mouse coordinates + wxCoord x, y; + SetupCoordinates(x, y, event); + wxevent.SetPosition(wxPoint (x,y)); + + GetWXPeer()->HandleWindowEvent(wxevent); + } + + // Cancel Press and Tap Event if the touch corresponding to "press" is moving + // and the gesture is not active. + else + { + m_allowedGestures &= ~press_and_tap; + } + + return; + } + } +} + +void wxWidgetCocoaImpl::TouchesEnded(WX_NSEvent event) +{ + NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:m_osxView]; + + m_touchCount -= touches.count; + + // Time of event in milliseconds + const unsigned int eventTimeStamp = event.timestamp * 1000 + 0.5; + + // Check if 2 fingers are lifted off together or if 2 fingers are lifted off within the time interval of 200 milliseconds + if ( (!m_touchCount && (m_allowedGestures & two_finger_tap)) && + (touches.count == 2 || + (touches.count == 1 && eventTimeStamp - m_lastTouchTime <= wxTwoFingerTimeInterval)) ) + { + wxTwoFingerTapEvent wxevent(GetWXPeer()->GetId()); + wxevent.SetEventObject(GetWXPeer()); + wxevent.SetGestureStart(); + wxevent.SetGestureEnd(); + + // Get the mouse coordinates + wxCoord x, y; + SetupCoordinates(x, y, event); + wxevent.SetPosition(wxPoint (x,y)); + + GetWXPeer()->HandleWindowEvent(wxevent); + } + + // If Two Finger Tap Event is possible in future then save the timestamp to use it when the other touch + // leaves the surface. + else if ( m_touchCount == 1 && (m_allowedGestures & two_finger_tap) ) + { + m_lastTouchTime = eventTimeStamp; + } + + // Check if Press and Tap event is possible. + else if ( m_allowedGestures & press_and_tap ) + { + NSArray* array = [touches allObjects]; + + // True if touch that ended is the touch corresponding to "press" + bool isPressTouch = false; + + // Iterate through all ended touches + for( int i = 0; i < [array count]; ++i ) + { + NSTouch* touch = [array objectAtIndex:i]; + + // Check if touch that ended is the touch corresponding to "press" + if ( [touch.identity isEqual:m_initialTouch.identity] ) + { + isPressTouch = true; + break; + } + } + + // Cancel Press and Tap Event if the touch corresponding to press is ended + // and Press and Tap was not active + if ( isPressTouch && !(m_activeGestures & press_and_tap) ) + { + m_allowedGestures &= ~press_and_tap; + return; + } + + wxPressAndTapEvent wxevent(GetWXPeer()->GetId()); + wxevent.SetEventObject(GetWXPeer()); + + // Get the mouse coordinates + wxCoord x, y; + SetupCoordinates(x, y, event); + wxevent.SetPosition(wxPoint (x,y)); + + if ( !(m_activeGestures & press_and_tap) ) + { + wxevent.SetGestureStart(); + m_activeGestures |= press_and_tap; + } + + // End Press and Tap Event if the touch corresponding to "press" is lifted off + else if ( isPressTouch ) + { + wxevent.SetGestureEnd(); + + m_activeGestures &= ~press_and_tap; + m_allowedGestures &= ~press_and_tap; + [m_initialTouch release]; + } + + GetWXPeer()->HandleWindowEvent(wxevent); + } + + else + { + m_allowedGestures &= ~press_and_tap; + m_allowedGestures &= ~two_finger_tap; + m_activeGestures &= ~press_and_tap; + } +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + void wxWidgetCocoaImpl::insertText(NSString* text, WXWidget slf, void *_cmd) { bool result = false; @@ -1775,7 +2242,17 @@ void wxOSXCocoaClassAddWXMethods(Class c) wxOSX_CLASS_ADD_METHOD(c, @selector(scrollWheel:), (IMP) wxOSX_mouseEvent, "v@:@" ) wxOSX_CLASS_ADD_METHOD(c, @selector(mouseEntered:), (IMP) wxOSX_mouseEvent, "v@:@" ) wxOSX_CLASS_ADD_METHOD(c, @selector(mouseExited:), (IMP) wxOSX_mouseEvent, "v@:@" ) - + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + wxOSX_CLASS_ADD_METHOD(c, @selector(handlePanGesture:), (IMP) wxOSX_panGestureEvent, "v@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(handleZoomGesture:), (IMP) wxOSX_zoomGestureEvent, "v@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(handleRotateGesture:), (IMP) wxOSX_rotateGestureEvent, "v@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(handleLongPressGesture:), (IMP) wxOSX_longPressEvent, "v@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(touchesBeganWithEvent:), (IMP) wxOSX_touchesBegan, "v@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(touchesMovedWithEvent:), (IMP) wxOSX_touchesMoved, "v@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(touchesEndedWithEvent:), (IMP) wxOSX_touchesEnded, "v@:@" ) +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + wxOSX_CLASS_ADD_METHOD(c, @selector(magnifyWithEvent:), (IMP)wxOSX_mouseEvent, "v@:@") wxOSX_CLASS_ADD_METHOD(c, @selector(cursorUpdate:), (IMP) wxOSX_cursorUpdate, "v@:@" ) @@ -1857,6 +2334,14 @@ void wxWidgetCocoaImpl::Init() #endif m_lastKeyDownEvent = NULL; m_hasEditor = false; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + m_touchCount = 0; + m_lastTouchTime = 0; + m_allowedGestures = 0; + m_activeGestures = 0; + m_initialTouch = NULL; +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 } wxWidgetCocoaImpl::~wxWidgetCocoaImpl() @@ -1875,6 +2360,22 @@ wxWidgetCocoaImpl::~wxWidgetCocoaImpl() // gc aware handling if ( m_osxView ) CFRelease(m_osxView); + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + if ( wxPlatformInfo::Get().CheckOSVersion(10, 10) ) + { + if ( IsUserPane() ) + { + [m_panGestureRecognizer release]; + [m_magnificationGestureRecognizer release]; + [m_rotationGestureRecognizer release]; + [m_pressGestureRecognizer release]; + + if ( m_initialTouch ) + [m_initialTouch release]; + } + } +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 } bool wxWidgetCocoaImpl::IsVisible() const @@ -2791,7 +3292,34 @@ void wxWidgetCocoaImpl::InstallEventHandler( WXWidget control ) NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect: NSZeroRect options: options owner: m_osxView userInfo: nil]; [m_osxView addTrackingArea: area]; [area release]; - } + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + if ( wxPlatformInfo::Get().CheckOSVersion(10, 10) ) + { + if ( IsUserPane() ) + { + m_panGestureRecognizer = + [[NSPanGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handlePanGesture:)]; + + m_magnificationGestureRecognizer = + [[NSMagnificationGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleZoomGesture:)]; + + m_rotationGestureRecognizer = + [[NSRotationGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleRotateGesture:)]; + + m_pressGestureRecognizer = + [[NSPressGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleLongPressGesture:)]; + + [m_osxView addGestureRecognizer:m_panGestureRecognizer]; + [m_osxView addGestureRecognizer:m_magnificationGestureRecognizer]; + [m_osxView addGestureRecognizer:m_rotationGestureRecognizer]; + [m_osxView addGestureRecognizer:m_pressGestureRecognizer]; + + [m_osxView setAcceptsTouchEvents:YES]; + } + } +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 +} bool wxWidgetCocoaImpl::DoHandleCharEvent(NSEvent *event, NSString *text) { From 06d936f3f708375524e35938fa5be67f82017283 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Oct 2017 00:00:54 +0200 Subject: [PATCH 02/26] Make tests for gestures in wxGTK code more clear Use wxGTK_HAS_GESTURES_SUPPORT to guard gesture-related code instead of checking for the 3.14 GTK+ version explicitly in several places. No real changes. --- src/gtk/window.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 064ba0fdec..dda38a052d 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -229,6 +229,11 @@ static bool gs_inSizeAllocate; #endif #if GTK_CHECK_VERSION(3,14,0) + #define wxGTK_HAS_GESTURES_SUPPORT +#endif + +#ifdef wxGTK_HAS_GESTURES_SUPPORT + // This is true when the gesture has just started (currently used for pan gesture only) static bool gs_gestureStart = false; @@ -243,7 +248,8 @@ static gdouble gs_lastAngle = 0; // Last Zoom/Rotate gesture point static wxPoint gs_lastGesturePoint; -#endif // GTK_CHECK_VERSION(3,14,0) + +#endif // wxGTK_HAS_GESTURES_SUPPORT //----------------------------------------------------------------------------- // debug @@ -2448,12 +2454,12 @@ void wxWindowGTK::Init() m_dirtyTabOrder = false; -#if GTK_CHECK_VERSION(3,14,0) +#ifdef wxGTK_HAS_GESTURES_SUPPORT m_touchCount = 0; m_lastTouchTime = 0; m_allowedGestures = 0; m_activeGestures = 0; -#endif // GTK_CHECK_VERSION(3,14,0) +#endif // wxGTK_HAS_GESTURES_SUPPORT } wxWindowGTK::wxWindowGTK() @@ -2852,7 +2858,8 @@ static gboolean source_dispatch(GSource*, GSourceFunc, void*) } } -#if GTK_CHECK_VERSION(3,14,0) +#ifdef wxGTK_HAS_GESTURES_SUPPORT + // Currently used for Press and Tap gesture only enum GestureStates { @@ -3299,7 +3306,8 @@ touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGT break; } } -#endif // GTK_CHECK_VERSION(3,14,0) + +#endif // wxGTK_HAS_GESTURES_SUPPORT void wxWindowGTK::ConnectWidget( GtkWidget *widget ) { @@ -3344,7 +3352,7 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget ) g_signal_connect (widget, "leave_notify_event", G_CALLBACK (gtk_window_leave_callback), this); -#if GTK_CHECK_VERSION(3,14,0) +#ifdef wxGTK_HAS_GESTURES_SUPPORT if ( gtk_check_version(3, 14, 0) == NULL ) { GtkGesture* vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL); @@ -3417,7 +3425,7 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget ) g_signal_connect (widget, "touch-event", G_CALLBACK(touch_callback), this); } -#endif // GTK_CHECK_VERSION(3,14,0) +#endif // wxGTK_HAS_GESTURES_SUPPORT } void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height) From 7b1dc191be747f5b1c103c931b15290878e44c5e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Oct 2017 00:22:24 +0200 Subject: [PATCH 03/26] Move wxGTK gesture-related data in a private opaque struct This is a first step towards enabling gesture events only for the windows that are interested in them as it will make it possible to avoid wasting space on unused data in the windows that don't need it. No real changes so far. --- include/wx/gtk/window.h | 11 +- src/gtk/window.cpp | 280 +++++++++++++++++++++++----------------- 2 files changed, 163 insertions(+), 128 deletions(-) diff --git a/include/wx/gtk/window.h b/include/wx/gtk/window.h index 0639b99ae7..d9db061dc6 100644 --- a/include/wx/gtk/window.h +++ b/include/wx/gtk/window.h @@ -356,14 +356,9 @@ public: wxRegion m_nativeUpdateRegion; // not transformed for RTL #if defined(__WXGTK3__) - unsigned int m_touchCount; - unsigned int m_lastTouchTime; - int m_gestureState; - int m_allowedGestures; - int m_activeGestures; - wxPoint m_lastTouchPoint; - GdkEventSequence* m_touchSequence; -#endif + // Data used for gesture support, if available. + class wxWindowGesturesData* m_gesturesData; +#endif // __WXGTK3__ protected: // implement the base class pure virtuals diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index dda38a052d..978e308655 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -234,6 +234,20 @@ static bool gs_inSizeAllocate; #ifdef wxGTK_HAS_GESTURES_SUPPORT +// Per-window data for gestures support. +struct wxWindowGesturesData +{ + wxWindowGesturesData(wxWindow* win, GtkWidget *widget); + + unsigned int m_touchCount; + unsigned int m_lastTouchTime; + int m_gestureState; + int m_allowedGestures; + int m_activeGestures; + wxPoint m_lastTouchPoint; + GdkEventSequence* m_touchSequence; +}; + // This is true when the gesture has just started (currently used for pan gesture only) static bool gs_gestureStart = false; @@ -2455,10 +2469,7 @@ void wxWindowGTK::Init() m_dirtyTabOrder = false; #ifdef wxGTK_HAS_GESTURES_SUPPORT - m_touchCount = 0; - m_lastTouchTime = 0; - m_allowedGestures = 0; - m_activeGestures = 0; + m_gesturesData = NULL; #endif // wxGTK_HAS_GESTURES_SUPPORT } @@ -2655,6 +2666,10 @@ wxWindowGTK::~wxWindowGTK() gs_sizeRevalidateList = g_list_remove_all(gs_sizeRevalidateList, this); #endif +#ifdef wxGTK_HAS_GESTURES_SUPPORT + delete m_gesturesData; +#endif // wxGTK_HAS_GESTURES_SUPPORT + if (m_widget) { // Note that gtk_widget_destroy() does not destroy the widget, it just @@ -2888,8 +2903,10 @@ pan_gesture_begin_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUN static void horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindowGTK* win) { + wxWindowGesturesData* const data = win->m_gesturesData; + // Do not process horizontal pan, if there was no "pan" signal for it. - if ( !(win->m_allowedGestures & horizontal_pan) ) + if ( !(data->m_allowedGestures & horizontal_pan) ) { return; } @@ -2901,7 +2918,7 @@ horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* seque return; } - win->m_allowedGestures &= ~horizontal_pan; + data->m_allowedGestures &= ~horizontal_pan; wxPanGestureEvent event(win->GetId()); @@ -2915,8 +2932,10 @@ horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* seque static void vertical_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindowGTK* win) { + wxWindowGesturesData* const data = win->m_gesturesData; + // Do not process vertical pan, if there was no "pan" signal for it. - if ( !(win->m_allowedGestures & vertical_pan) ) + if ( !(data->m_allowedGestures & vertical_pan) ) { return; } @@ -2928,7 +2947,7 @@ vertical_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequenc return; } - win->m_allowedGestures &= ~vertical_pan; + data->m_allowedGestures &= ~vertical_pan; wxPanGestureEvent event(win->GetId()); @@ -2963,28 +2982,30 @@ pan_gesture_callback(GtkGesture* gesture, GtkPanDirection direction, gdouble off event.SetEventObject(win); event.SetPosition(wxPoint(wxRound(x), wxRound(y))); + wxWindowGesturesData* const data = win->m_gesturesData; + // 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: - win->m_allowedGestures |= vertical_pan; + data->m_allowedGestures |= vertical_pan; event.SetDeltaY(-delta); break; case GTK_PAN_DIRECTION_DOWN: - win->m_allowedGestures |= vertical_pan; + data->m_allowedGestures |= vertical_pan; event.SetDeltaY(delta); break; case GTK_PAN_DIRECTION_RIGHT: - win->m_allowedGestures |= horizontal_pan; + data->m_allowedGestures |= horizontal_pan; event.SetDeltaX(delta); break; case GTK_PAN_DIRECTION_LEFT: - win->m_allowedGestures |= horizontal_pan; + data->m_allowedGestures |= horizontal_pan; event.SetDeltaX(-delta); break; } @@ -2999,9 +3020,9 @@ pan_gesture_callback(GtkGesture* gesture, GtkPanDirection direction, gdouble off } // Cancel press and tap gesture if it is not active during "pan" signal. - if( !(win->m_activeGestures & press_and_tap) ) + if( !(data->m_activeGestures & press_and_tap) ) { - win->m_allowedGestures &= ~press_and_tap; + data->m_allowedGestures &= ~press_and_tap; } win->GTKProcessEvent(event); @@ -3023,10 +3044,12 @@ zoom_gesture_callback(GtkGesture* gesture, gdouble scale, wxWindowGTK* win) event.SetPosition(wxPoint(wxRound(x), wxRound(y))); event.SetZoomFactor(scale); + wxWindowGesturesData* const data = win->m_gesturesData; + // Cancel "Two FInger Tap Event" if scale has changed if ( wxRound(scale * 1000) != wxRound(gs_lastScale * 1000) ) { - win->m_allowedGestures &= ~two_finger_tap; + data->m_allowedGestures &= ~two_finger_tap; } gs_lastScale = scale; @@ -3159,8 +3182,10 @@ wxEmitTwoFingerTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) event.SetEventObject(win); - double lastX = win->m_lastTouchPoint.x; - double lastY = win->m_lastTouchPoint.y; + wxWindowGesturesData* const data = win->m_gesturesData; + + 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; @@ -3186,7 +3211,9 @@ wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) event.SetEventObject(win); - switch ( win->m_gestureState ) + wxWindowGesturesData* const data = win->m_gesturesData; + + switch ( data->m_gestureState ) { case begin: event.SetGestureStart(); @@ -3194,10 +3221,10 @@ wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) case update: // Update touch point as the touch corresponding to "press" is moving - if ( win->m_touchSequence == gdk_event->sequence ) + if ( data->m_touchSequence == gdk_event->sequence ) { - win->m_lastTouchPoint.x = gdk_event->x; - win->m_lastTouchPoint.y = gdk_event->y; + data->m_lastTouchPoint.x = gdk_event->x; + data->m_lastTouchPoint.y = gdk_event->y; } break; @@ -3206,7 +3233,7 @@ wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) break; } - event.SetPosition(win->m_lastTouchPoint); + event.SetPosition(data->m_lastTouchPoint); win->GTKProcessEvent(event); } @@ -3214,67 +3241,69 @@ wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) static void touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGTK* win) { + wxWindowGesturesData* const data = win->m_gesturesData; + switch ( gdk_event->type ) { case GDK_TOUCH_BEGIN: - win->m_touchCount++; + data->m_touchCount++; - win->m_allowedGestures &= ~two_finger_tap; + data->m_allowedGestures &= ~two_finger_tap; - if ( win->m_touchCount == 1 ) + if ( data->m_touchCount == 1 ) { - win->m_lastTouchTime = gdk_event->time; - win->m_lastTouchPoint.x = gdk_event->x; - win->m_lastTouchPoint.y = gdk_event->y; + 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" - win->m_touchSequence = gdk_event->sequence; + data->m_touchSequence = gdk_event->sequence; // "Press and Tap Event" may occur in future - win->m_allowedGestures |= press_and_tap; + 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 ( win->m_touchCount == 2 && gdk_event->time - win->m_lastTouchTime <= wxTwoFingerTimeInterval ) + else if ( data->m_touchCount == 2 && gdk_event->time - data->m_lastTouchTime <= wxTwoFingerTimeInterval ) { // "Two Finger Tap Event" may be possible in the future - win->m_allowedGestures |= two_finger_tap; + data->m_allowedGestures |= two_finger_tap; // Cancel "Press and Tap Event" - win->m_allowedGestures &= ~press_and_tap; + 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 ( (win->m_activeGestures & press_and_tap) && gdk_event->sequence == win->m_touchSequence ) + if ( (data->m_activeGestures & press_and_tap) && gdk_event->sequence == data->m_touchSequence ) { - win->m_gestureState = update; + data->m_gestureState = update; wxEmitPressAndTapEvent(gdk_event, win); } break; case GDK_TOUCH_END: case GDK_TOUCH_CANCEL: - win->m_touchCount--; + data->m_touchCount--; - if ( win->m_touchCount == 1 ) + if ( data->m_touchCount == 1 ) { - win->m_lastTouchTime = gdk_event->time; + data->m_lastTouchTime = gdk_event->time; // If the touch corresponding to "press" is present and "tap" is produced by some ather touch - if ( (win->m_allowedGestures & press_and_tap) && gdk_event->sequence != win->m_touchSequence ) + if ( (data->m_allowedGestures & press_and_tap) && gdk_event->sequence != data->m_touchSequence ) { // Press and Tap gesture becomes active now - if ( !(win->m_activeGestures & press_and_tap) ) + if ( !(data->m_activeGestures & press_and_tap) ) { - win->m_gestureState = begin; - win->m_activeGestures |= press_and_tap; + data->m_gestureState = begin; + data->m_activeGestures |= press_and_tap; } else { - win->m_gestureState = update; + data->m_gestureState = update; } wxEmitPressAndTapEvent(gdk_event, win); @@ -3282,21 +3311,21 @@ touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGT } // Check if "Two Finger Tap Event" is possible and both the fingers have been lifted up together - else if ( (win->m_allowedGestures & two_finger_tap) && !win->m_touchCount - && gdk_event->time - win->m_lastTouchTime <= wxTwoFingerTimeInterval ) + 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 ( (win->m_activeGestures & press_and_tap) && gdk_event->sequence == win->m_touchSequence ) + if ( (data->m_activeGestures & press_and_tap) && gdk_event->sequence == data->m_touchSequence ) { - win->m_gestureState = end; + data->m_gestureState = end; - win->m_activeGestures &= ~press_and_tap; + data->m_activeGestures &= ~press_and_tap; - win->m_allowedGestures &= ~press_and_tap; + data->m_allowedGestures &= ~press_and_tap; wxEmitPressAndTapEvent(gdk_event, win); } @@ -3307,6 +3336,86 @@ touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGT } } +wxWindowGesturesData::wxWindowGesturesData(wxWindowGTK* win, GtkWidget *widget) +{ + m_touchCount = 0; + m_lastTouchTime = 0; + m_gestureState = 0; + m_allowedGestures = 0; + m_activeGestures = 0; + m_touchSequence = NULL; + + GtkGesture* vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL); + + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(vertical_pan_gesture), GTK_PHASE_TARGET); + + g_signal_connect (vertical_pan_gesture, "begin", + G_CALLBACK(pan_gesture_begin_callback), win); + g_signal_connect (vertical_pan_gesture, "pan", + G_CALLBACK(pan_gesture_callback), win); + g_signal_connect (vertical_pan_gesture, "end", + G_CALLBACK(vertical_pan_gesture_end_callback), win); + g_signal_connect (vertical_pan_gesture, "cancel", + G_CALLBACK(vertical_pan_gesture_end_callback), win); + + GtkGesture* 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(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(horizontal_pan_gesture), GTK_PHASE_TARGET); + + g_signal_connect (horizontal_pan_gesture, "begin", + G_CALLBACK(pan_gesture_begin_callback), win); + g_signal_connect (horizontal_pan_gesture, "pan", + G_CALLBACK(pan_gesture_callback), win); + g_signal_connect (horizontal_pan_gesture, "end", + G_CALLBACK(horizontal_pan_gesture_end_callback), win); + g_signal_connect (horizontal_pan_gesture, "cancel", + G_CALLBACK(horizontal_pan_gesture_end_callback), win); + + GtkGesture* zoom_gesture = gtk_gesture_zoom_new(widget); + + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(zoom_gesture), GTK_PHASE_TARGET); + + g_signal_connect (zoom_gesture, "begin", + G_CALLBACK(zoom_gesture_begin_callback), win); + g_signal_connect (zoom_gesture, "scale-changed", + G_CALLBACK(zoom_gesture_callback), win); + g_signal_connect (zoom_gesture, "end", + G_CALLBACK(zoom_gesture_end_callback), win); + g_signal_connect (zoom_gesture, "cancel", + G_CALLBACK(zoom_gesture_end_callback), win); + + GtkGesture* rotate_gesture = gtk_gesture_rotate_new(widget); + + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(rotate_gesture), GTK_PHASE_TARGET); + + g_signal_connect (rotate_gesture, "begin", + G_CALLBACK(rotate_gesture_begin_callback), win); + g_signal_connect (rotate_gesture, "angle-changed", + G_CALLBACK(rotate_gesture_callback), win); + g_signal_connect (rotate_gesture, "end", + G_CALLBACK(rotate_gesture_end_callback), win); + g_signal_connect (rotate_gesture, "cancel", + G_CALLBACK(rotate_gesture_end_callback), win); + + GtkGesture* 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(long_press_gesture), TRUE) + // which will allow "pressed" signal only for Touch events. + + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(long_press_gesture), GTK_PHASE_TARGET); + + g_signal_connect (long_press_gesture, "pressed", + G_CALLBACK(long_press_gesture_callback), win); + g_signal_connect (widget, "touch-event", + G_CALLBACK(touch_callback), win); +} + #endif // wxGTK_HAS_GESTURES_SUPPORT void wxWindowGTK::ConnectWidget( GtkWidget *widget ) @@ -3353,78 +3462,9 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget ) G_CALLBACK (gtk_window_leave_callback), this); #ifdef wxGTK_HAS_GESTURES_SUPPORT + // Check if gestures support is also available during run-time. if ( gtk_check_version(3, 14, 0) == NULL ) - { - GtkGesture* vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL); - - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(vertical_pan_gesture), GTK_PHASE_TARGET); - - g_signal_connect (vertical_pan_gesture, "begin", - G_CALLBACK(pan_gesture_begin_callback), this); - g_signal_connect (vertical_pan_gesture, "pan", - G_CALLBACK(pan_gesture_callback), this); - g_signal_connect (vertical_pan_gesture, "end", - G_CALLBACK(vertical_pan_gesture_end_callback), this); - g_signal_connect (vertical_pan_gesture, "cancel", - G_CALLBACK(vertical_pan_gesture_end_callback), this); - - GtkGesture* 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(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(horizontal_pan_gesture), GTK_PHASE_TARGET); - - g_signal_connect (horizontal_pan_gesture, "begin", - G_CALLBACK(pan_gesture_begin_callback), this); - g_signal_connect (horizontal_pan_gesture, "pan", - G_CALLBACK(pan_gesture_callback), this); - g_signal_connect (horizontal_pan_gesture, "end", - G_CALLBACK(horizontal_pan_gesture_end_callback), this); - g_signal_connect (horizontal_pan_gesture, "cancel", - G_CALLBACK(horizontal_pan_gesture_end_callback), this); - - GtkGesture* zoom_gesture = gtk_gesture_zoom_new(widget); - - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(zoom_gesture), GTK_PHASE_TARGET); - - g_signal_connect (zoom_gesture, "begin", - G_CALLBACK(zoom_gesture_begin_callback), this); - g_signal_connect (zoom_gesture, "scale-changed", - G_CALLBACK(zoom_gesture_callback), this); - g_signal_connect (zoom_gesture, "end", - G_CALLBACK(zoom_gesture_end_callback), this); - g_signal_connect (zoom_gesture, "cancel", - G_CALLBACK(zoom_gesture_end_callback), this); - - GtkGesture* rotate_gesture = gtk_gesture_rotate_new(widget); - - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(rotate_gesture), GTK_PHASE_TARGET); - - g_signal_connect (rotate_gesture, "begin", - G_CALLBACK(rotate_gesture_begin_callback), this); - g_signal_connect (rotate_gesture, "angle-changed", - G_CALLBACK(rotate_gesture_callback), this); - g_signal_connect (rotate_gesture, "end", - G_CALLBACK(rotate_gesture_end_callback), this); - g_signal_connect (rotate_gesture, "cancel", - G_CALLBACK(rotate_gesture_end_callback), this); - - GtkGesture* 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(long_press_gesture), TRUE) - // which will allow "pressed" signal only for Touch events. - - gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(long_press_gesture), GTK_PHASE_TARGET); - - g_signal_connect (long_press_gesture, "pressed", - G_CALLBACK(long_press_gesture_callback), this); - g_signal_connect (widget, "touch-event", - G_CALLBACK(touch_callback), this); - } + m_gesturesData = new wxWindowGesturesData(this, widget); #endif // wxGTK_HAS_GESTURES_SUPPORT } From 6ba3db77537fea7cba9df6d807e6ef46d35d4f13 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 12 Oct 2017 00:24:57 +0200 Subject: [PATCH 04/26] Destroy GtkGestures objects we create We seem to be leaking memory otherwise. --- src/gtk/window.cpp | 74 ++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 978e308655..55f2717c7f 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -238,6 +238,7 @@ static bool gs_inSizeAllocate; struct wxWindowGesturesData { wxWindowGesturesData(wxWindow* win, GtkWidget *widget); + ~wxWindowGesturesData(); unsigned int m_touchCount; unsigned int m_lastTouchTime; @@ -246,6 +247,12 @@ struct wxWindowGesturesData 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; }; // This is true when the gesture has just started (currently used for pan gesture only) @@ -3345,77 +3352,86 @@ wxWindowGesturesData::wxWindowGesturesData(wxWindowGTK* win, GtkWidget *widget) m_activeGestures = 0; m_touchSequence = NULL; - GtkGesture* vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL); + m_vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(vertical_pan_gesture), GTK_PHASE_TARGET); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_vertical_pan_gesture), GTK_PHASE_TARGET); - g_signal_connect (vertical_pan_gesture, "begin", + g_signal_connect (m_vertical_pan_gesture, "begin", G_CALLBACK(pan_gesture_begin_callback), win); - g_signal_connect (vertical_pan_gesture, "pan", + g_signal_connect (m_vertical_pan_gesture, "pan", G_CALLBACK(pan_gesture_callback), win); - g_signal_connect (vertical_pan_gesture, "end", + g_signal_connect (m_vertical_pan_gesture, "end", G_CALLBACK(vertical_pan_gesture_end_callback), win); - g_signal_connect (vertical_pan_gesture, "cancel", + g_signal_connect (m_vertical_pan_gesture, "cancel", G_CALLBACK(vertical_pan_gesture_end_callback), win); - GtkGesture* horizontal_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_HORIZONTAL); + 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(horizontal_pan_gesture), TRUE) and + // 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(horizontal_pan_gesture), GTK_PHASE_TARGET); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_horizontal_pan_gesture), GTK_PHASE_TARGET); - g_signal_connect (horizontal_pan_gesture, "begin", + g_signal_connect (m_horizontal_pan_gesture, "begin", G_CALLBACK(pan_gesture_begin_callback), win); - g_signal_connect (horizontal_pan_gesture, "pan", + g_signal_connect (m_horizontal_pan_gesture, "pan", G_CALLBACK(pan_gesture_callback), win); - g_signal_connect (horizontal_pan_gesture, "end", + g_signal_connect (m_horizontal_pan_gesture, "end", G_CALLBACK(horizontal_pan_gesture_end_callback), win); - g_signal_connect (horizontal_pan_gesture, "cancel", + g_signal_connect (m_horizontal_pan_gesture, "cancel", G_CALLBACK(horizontal_pan_gesture_end_callback), win); - GtkGesture* zoom_gesture = gtk_gesture_zoom_new(widget); + m_zoom_gesture = gtk_gesture_zoom_new(widget); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(zoom_gesture), GTK_PHASE_TARGET); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_zoom_gesture), GTK_PHASE_TARGET); - g_signal_connect (zoom_gesture, "begin", + g_signal_connect (m_zoom_gesture, "begin", G_CALLBACK(zoom_gesture_begin_callback), win); - g_signal_connect (zoom_gesture, "scale-changed", + g_signal_connect (m_zoom_gesture, "scale-changed", G_CALLBACK(zoom_gesture_callback), win); - g_signal_connect (zoom_gesture, "end", + g_signal_connect (m_zoom_gesture, "end", G_CALLBACK(zoom_gesture_end_callback), win); - g_signal_connect (zoom_gesture, "cancel", + g_signal_connect (m_zoom_gesture, "cancel", G_CALLBACK(zoom_gesture_end_callback), win); - GtkGesture* rotate_gesture = gtk_gesture_rotate_new(widget); + m_rotate_gesture = gtk_gesture_rotate_new(widget); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(rotate_gesture), GTK_PHASE_TARGET); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_rotate_gesture), GTK_PHASE_TARGET); - g_signal_connect (rotate_gesture, "begin", + g_signal_connect (m_rotate_gesture, "begin", G_CALLBACK(rotate_gesture_begin_callback), win); - g_signal_connect (rotate_gesture, "angle-changed", + g_signal_connect (m_rotate_gesture, "angle-changed", G_CALLBACK(rotate_gesture_callback), win); - g_signal_connect (rotate_gesture, "end", + g_signal_connect (m_rotate_gesture, "end", G_CALLBACK(rotate_gesture_end_callback), win); - g_signal_connect (rotate_gesture, "cancel", + g_signal_connect (m_rotate_gesture, "cancel", G_CALLBACK(rotate_gesture_end_callback), win); - GtkGesture* long_press_gesture = gtk_gesture_long_press_new(widget); + 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(long_press_gesture), TRUE) + // 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(long_press_gesture), GTK_PHASE_TARGET); + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_long_press_gesture), GTK_PHASE_TARGET); - g_signal_connect (long_press_gesture, "pressed", + 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); +} + #endif // wxGTK_HAS_GESTURES_SUPPORT void wxWindowGTK::ConnectWidget( GtkWidget *widget ) From bf929f98683ffb16dff7ce61b0172de26c1ea280 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 16:05:33 +0100 Subject: [PATCH 05/26] Use wxLoadedDLL for loading user32.dll dynamically This avoids unnecessarily loading and unloading (except that it's not really unloaded) a DLL which is always available anyhow. --- src/msw/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 4b88bb2bfd..a2bb278d59 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -254,7 +254,7 @@ public: private: static void LoadGestureSymbols() { - wxDynamicLibrary dll("User32.dll"); + wxLoadedDLL dll(wxS("user32.dll")); wxDL_INIT_FUNC(ms_pfn, GetGestureInfo, dll); wxDL_INIT_FUNC(ms_pfn, CloseGestureInfoHandle, dll); From 59f428d25efb118003f83ef239846e5de49ab78e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 16:06:29 +0100 Subject: [PATCH 06/26] Slightly simplify GestureFuncs code Document that the users of this class always must call IsOk() first, as they already do, and remove the checks for ms_gestureSymbolsLoaded and calls to LoadGestureSymbols() from all the other methods, which are unnecessary in this case. --- src/msw/window.cpp | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index a2bb278d59..7d86f026e7 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -205,29 +205,34 @@ bool gs_insideCaptureChanged = false; } // anonymous namespace +#ifdef WM_GESTURE + namespace { // Class used to dynamically load gestures related API functions. -#ifdef WM_GESTURE class GestureFuncs { public: + // Must be called before using any other methods of this class (and they + // can't be used if this one returns false). static bool IsOk() { if ( !ms_gestureSymbolsLoaded ) + { + ms_gestureSymbolsLoaded = true; LoadGestureSymbols(); + } - return ms_pfnGetGestureInfo && ms_pfnCloseGestureInfoHandle && ms_pfnSetGestureConfig; + return ms_pfnGetGestureInfo && + ms_pfnCloseGestureInfoHandle && + ms_pfnSetGestureConfig; } typedef BOOL (WINAPI *GetGestureInfo_t)(HGESTUREINFO, PGESTUREINFO); static GetGestureInfo_t GetGestureInfo() { - if ( !ms_gestureSymbolsLoaded ) - LoadGestureSymbols(); - return ms_pfnGetGestureInfo; } @@ -235,19 +240,14 @@ public: static CloseGestureInfoHandle_t CloseGestureInfoHandle() { - if ( !ms_gestureSymbolsLoaded ) - LoadGestureSymbols(); - return ms_pfnCloseGestureInfoHandle; } - typedef BOOL (WINAPI *SetGestureConfig_t)(HWND, DWORD, UINT, PGESTURECONFIG, UINT); + typedef BOOL + (WINAPI *SetGestureConfig_t)(HWND, DWORD, UINT, PGESTURECONFIG, UINT); static SetGestureConfig_t SetGestureConfig() { - if ( !ms_gestureSymbolsLoaded ) - LoadGestureSymbols(); - return ms_pfnSetGestureConfig; } @@ -259,8 +259,6 @@ private: wxDL_INIT_FUNC(ms_pfn, GetGestureInfo, dll); wxDL_INIT_FUNC(ms_pfn, CloseGestureInfoHandle, dll); wxDL_INIT_FUNC(ms_pfn, SetGestureConfig, dll); - - ms_gestureSymbolsLoaded = true; } static GetGestureInfo_t ms_pfnGetGestureInfo; @@ -268,7 +266,6 @@ private: static SetGestureConfig_t ms_pfnSetGestureConfig; static bool ms_gestureSymbolsLoaded; - }; GestureFuncs::GetGestureInfo_t @@ -280,10 +277,10 @@ GestureFuncs::SetGestureConfig_t bool GestureFuncs::ms_gestureSymbolsLoaded = false; -#endif // WM_GESTURE - } // anonymous namespace +#endif // WM_GESTURE + // --------------------------------------------------------------------------- // private functions // --------------------------------------------------------------------------- From 7ffb89781057f9daed7e56ce1b941dc51160c300 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 16:07:57 +0100 Subject: [PATCH 07/26] Remove unnecessary assignments in WM_GESTURE handler code The "processed" flag is false by default, no need to explicitly reset it, this was just confusing. --- src/msw/window.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 7d86f026e7..63d18e0c7a 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -3204,10 +3204,7 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, case WM_GESTURE: { if ( !GestureFuncs::IsOk() ) - { - processed = false; break; - } // gestureInfo will contain the information about the gesture GESTUREINFO gestureInfo = {0}; @@ -3217,7 +3214,6 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, if ( !GestureFuncs::GetGestureInfo()((HGESTUREINFO)lParam, &gestureInfo) ) { wxLogLastError("GetGestureInfo"); - processed = false; break; } From daf19c652c1ec37e8291b7a69ba99bae45726d8c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 16:09:43 +0100 Subject: [PATCH 08/26] Use WinStruct<> to initialize GESTUREINFO This is more concise and clear than initializing it manually. --- src/msw/window.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 63d18e0c7a..bfcda345b7 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -3206,12 +3206,10 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, if ( !GestureFuncs::IsOk() ) break; - // gestureInfo will contain the information about the gesture - GESTUREINFO gestureInfo = {0}; - gestureInfo.cbSize = sizeof(GESTUREINFO); + HGESTUREINFO hGestureInfo = reinterpret_cast(lParam); - // This should fill gestureInfo with the gesture details - if ( !GestureFuncs::GetGestureInfo()((HGESTUREINFO)lParam, &gestureInfo) ) + WinStruct gestureInfo; + if ( !GestureFuncs::GetGestureInfo()(hGestureInfo, &gestureInfo) ) { wxLogLastError("GetGestureInfo"); break; @@ -3278,7 +3276,7 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, if ( processed ) { // If processed, we must call this to avoid memory leaks - if ( !GestureFuncs::CloseGestureInfoHandle()((HGESTUREINFO)lParam) ) + if ( !GestureFuncs::CloseGestureInfoHandle()(hGestureInfo) ) { wxLogLastError("CloseGestureInfoHandle"); } From 989dd364054a34745fe34aa265d5082de9b82105 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 16:11:00 +0100 Subject: [PATCH 09/26] Style fixes in the MSW gestures code No real changes, just reformat code and comments to fit in 80 column lines. --- src/msw/window.cpp | 90 ++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index bfcda345b7..162d972380 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -3227,50 +3227,41 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, // dwID field is used to determine the type of gesture switch ( gestureInfo.dwID ) { - // Pan gesture case GID_PAN: - { // (x,y) is the current position of the pan processed = HandlePanGesture(x, y, gestureInfo.dwFlags); break; - } - // Zoom gesture case GID_ZOOM: - { - // (x,y) is the mid-point of 2 fingers - // ullArgument field is a 64-bit unsigned integer, but the relevant information - // is in it's lower 4 bytes and represents distance between the fingers - // this is used to extract those lower 4 bytes - DWORD fingerDistance = (DWORD)((gestureInfo.ullArguments) & 0x00000000ffffffff); - processed = HandleZoomGesture(x, y, fingerDistance, gestureInfo.dwFlags); + // (x,y) is the mid-point of 2 fingers and ullArgument + // contains the distance between the fingers in its lower + // half + processed = HandleZoomGesture + ( + x, y, + static_cast(gestureInfo.ullArguments), + gestureInfo.dwFlags + ); break; - } - // Rotate gesture case GID_ROTATE: - { - // (x,y) is the center point of rotation - // Again, we need the lower 4 bytes and this will used as an argument - // to obtain the angle to rotate - DWORD angleArgument = (DWORD)((gestureInfo.ullArguments) & 0x00000000ffffffff); - processed = HandleRotateGesture(x, y, angleArgument, gestureInfo.dwFlags); + // (x,y) is the center point of rotation and ullArguments + // contains the angle of rotation + processed = HandleRotateGesture + ( + x, y, + static_cast(gestureInfo.ullArguments), + gestureInfo.dwFlags + ); break; - } - // Two Finger tap gesture case GID_TWOFINGERTAP: - { processed = HandleTwoFingerTap(x, y, gestureInfo.dwFlags); break; - } - // Press and Tap gesture case GID_PRESSANDTAP: - { processed = HandlePressAndTap(x, y, gestureInfo.dwFlags); break; - } } if ( processed ) @@ -3827,9 +3818,15 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, // Configure to receive all gestures GESTURECONFIG gestureConfig = {0, GC_ALLGESTURES, 0}; - // This functions sets the configuration to the window. Second argument is reserved to be 0. - // Third argument is the number of elements in gestureConfig array, currently equal to 1 - if ( !GestureFuncs::SetGestureConfig()(m_hWnd, 0, 1, &gestureConfig, sizeof(GESTURECONFIG)) ) + if ( !GestureFuncs::SetGestureConfig() + ( + m_hWnd, + 0, // Reserved, must be always 0. + 1, // Number of gesture configurations. + &gestureConfig, // Pointer to the first one. + sizeof(GESTURECONFIG) // Size of each configuration. + ) + ) { wxLogLastError("SetGestureConfig"); } @@ -5688,7 +5685,9 @@ bool wxWindowMSW::HandlePanGesture(int x, int y, WXDWORD flags) return HandleWindowEvent(event); } -bool wxWindowMSW::HandleZoomGesture(int x, int y, WXDWORD fingerDistance, WXDWORD flags) +bool wxWindowMSW::HandleZoomGesture(int x, int y, + WXDWORD fingerDistance, + WXDWORD flags) { // wxEVT_GESTURE_ZOOM wxZoomGestureEvent event(GetId()); @@ -5696,8 +5695,8 @@ bool wxWindowMSW::HandleZoomGesture(int x, int y, WXDWORD fingerDistance, WXDWOR // These are used to calculate the center of the zoom and zoom factor static int s_previousLocationX, s_previousLocationY, s_intialFingerDistance; - // This flag indicates that the gesture has just started - // Store the current point and distance between the fingers for future calculations + // This flag indicates that the gesture has just started, store the current + // point and distance between the fingers for future calculations. if ( flags & GF_BEGIN ) { s_previousLocationX = x; @@ -5711,15 +5710,16 @@ bool wxWindowMSW::HandleZoomGesture(int x, int y, WXDWORD fingerDistance, WXDWOR event.SetGestureEnd(); } - // Calculate center point of the zoom - // Human beings are not very good at moving two fingers at exactly the same rate outwards/inwards - // There is usually some error, which can cause the center to shift slightly - // So, it is recommended to take the average of center of fingers in the current and last positions + // Calculate center point of the zoom. Human beings are not very good at + // moving two fingers at exactly the same rate outwards/inwards and there + // is usually some error, which can cause the center to shift slightly. So, + // it is recommended to take the average of center of fingers in the + // current and last positions. wxPoint pt; pt.x = (s_previousLocationX + x) / 2; pt.y = (s_previousLocationY + y) / 2; - double zoomFactor = (double) fingerDistance / (double) s_intialFingerDistance; + const double zoomFactor = (double) fingerDistance / s_intialFingerDistance; event.SetZoomFactor(zoomFactor); event.SetEventObject(this); @@ -5735,7 +5735,9 @@ bool wxWindowMSW::HandleZoomGesture(int x, int y, WXDWORD fingerDistance, WXDWOR return HandleWindowEvent(event); } -bool wxWindowMSW::HandleRotateGesture(int x, int y, WXDWORD angleArgument, WXDWORD flags) +bool wxWindowMSW::HandleRotateGesture(int x, int y, + WXDWORD angleArgument, + WXDWORD flags) { // wxEVT_GESTURE_ROTATE wxRotateGestureEvent event(GetId()); @@ -5748,13 +5750,15 @@ bool wxWindowMSW::HandleRotateGesture(int x, int y, WXDWORD angleArgument, WXDWO else { - // Use angleArgument to obtain the cumulative angle since the gesture was first - // started. This angle is in radians - // MSW returns negative angle for clockwise rotation and positive otherwise - // So, multiply angle by -1 for positive angle for clockwise and negative in case of counterclockwise + // Use angleArgument to obtain the cumulative angle since the gesture + // was first started. This angle is in radians and MSW returns negative + // angle for clockwise rotation and positive otherwise, so, multiply + // angle by -1 for positive angle for clockwise and negative in case of + // counterclockwise. double angle = -GID_ROTATE_ANGLE_FROM_ARGUMENT(angleArgument); - // If the rotation is anti-clockwise convert the angle to its corresponding positive value in a clockwise sense. + // If the rotation is anti-clockwise convert the angle to its + // corresponding positive value in a clockwise sense. if ( angle < 0 ) { angle += 2 * M_PI; From b3e726faed6f3399ab2aa23f8dd5dc95e4387f30 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 17:24:19 +0100 Subject: [PATCH 10/26] Refactor MSW gesture events initialization Don't repeat the same code for initializing events in several different functions but put it in a helper InitGestureEvent() function and just call it from the event-specific handlers. --- include/wx/msw/window.h | 4 ++ src/msw/window.cpp | 87 ++++++++++++----------------------------- 2 files changed, 28 insertions(+), 63 deletions(-) diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index 3ceeabe5f6..1de7a1359c 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -360,6 +360,10 @@ public: bool HandleMouseWheel(wxMouseWheelAxis axis, WXWPARAM wParam, WXLPARAM lParam); + // Common gesture event initialization, returns true if it is the initial + // event (GF_BEGIN set in flags), false otherwise. + bool InitGestureEvent(wxGestureEvent& event, int x, int y, WXDWORD flags); + bool HandlePanGesture(int x, int y, WXDWORD flags); bool HandleZoomGesture(int x, int y, WXDWORD fingerDistance, WXDWORD flags); bool HandleRotateGesture(int x, int y, WXDWORD angleArgument, WXDWORD flags); diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 162d972380..58aed9f66a 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -5647,6 +5647,23 @@ void wxWindowMSW::GenerateMouseLeave() // Gesture events // --------------------------------------------------------------------------- +bool wxWindowMSW::InitGestureEvent(wxGestureEvent& event, + int x, int y, + WXDWORD flags) +{ + event.SetEventObject(this); + event.SetTimestamp(::GetMessageTime()); + event.SetPosition(wxPoint(x, y)); + + if ( flags & GF_BEGIN ) + event.SetGestureStart(); + + if ( flags & GF_END ) + event.SetGestureEnd(); + + return (flags & GF_BEGIN) != 0; +} + bool wxWindowMSW::HandlePanGesture(int x, int y, WXDWORD flags) { // wxEVT_GESTURE_PAN @@ -5655,26 +5672,17 @@ bool wxWindowMSW::HandlePanGesture(int x, int y, WXDWORD flags) // These are used to calculate the pan delta static int s_previousLocationX, s_previousLocationY; - // This flag indicates that the gesture has just started - // Store the current point to determine the pan delta later on - if ( flags & GF_BEGIN ) + // If the gesture has just started, store the current point to determine + // the pan delta later on. + if ( InitGestureEvent(event, x, y, flags) ) { s_previousLocationX = x; s_previousLocationY = y; - event.SetGestureStart(); - } - - if ( flags & GF_END ) - { - event.SetGestureEnd(); } // Determine the horizontal and vertical changes int DeltaX = x - s_previousLocationX, DeltaY = y - s_previousLocationY; - event.SetEventObject(this); - event.SetTimestamp(::GetMessageTime()); - event.SetPosition(wxPoint(x, y)); event.SetDeltaX(DeltaX); event.SetDeltaY(DeltaY); @@ -5697,17 +5705,11 @@ bool wxWindowMSW::HandleZoomGesture(int x, int y, // This flag indicates that the gesture has just started, store the current // point and distance between the fingers for future calculations. - if ( flags & GF_BEGIN ) + if ( InitGestureEvent(event, x, y, flags) ) { s_previousLocationX = x; s_previousLocationY = y; s_intialFingerDistance = fingerDistance; - event.SetGestureStart(); - } - - if ( flags & GF_END ) - { - event.SetGestureEnd(); } // Calculate center point of the zoom. Human beings are not very good at @@ -5722,8 +5724,6 @@ bool wxWindowMSW::HandleZoomGesture(int x, int y, const double zoomFactor = (double) fingerDistance / s_intialFingerDistance; event.SetZoomFactor(zoomFactor); - event.SetEventObject(this); - event.SetTimestamp(::GetMessageTime()); // This is not a gesture point but the center of a zoom event.SetPosition(pt); @@ -5742,13 +5742,7 @@ bool wxWindowMSW::HandleRotateGesture(int x, int y, // wxEVT_GESTURE_ROTATE wxRotateGestureEvent event(GetId()); - // This flag indicates that the gesture has just started - if ( flags & GF_BEGIN ) - { - event.SetGestureStart(); - } - - else + if ( !InitGestureEvent(event, x, y, flags) ) { // Use angleArgument to obtain the cumulative angle since the gesture // was first started. This angle is in radians and MSW returns negative @@ -5768,15 +5762,6 @@ bool wxWindowMSW::HandleRotateGesture(int x, int y, event.SetRotationAngle(angle); } - if ( flags & GF_END ) - { - event.SetGestureEnd(); - } - - event.SetEventObject(this); - event.SetTimestamp(::GetMessageTime()); - event.SetPosition(wxPoint(x, y)); - return HandleWindowEvent(event); } @@ -5785,19 +5770,7 @@ bool wxWindowMSW::HandleTwoFingerTap(int x, int y, WXDWORD flags) // wxEVT_TWO_FINGER_TAP wxTwoFingerTapEvent event(GetId()); - if ( flags & GF_BEGIN ) - { - event.SetGestureStart(); - } - - event.SetEventObject(this); - event.SetTimestamp(::GetMessageTime()); - event.SetPosition(wxPoint(x, y)); - - if ( flags & GF_END ) - { - event.SetGestureEnd(); - } + InitGestureEvent(event, x, y, flags); return HandleWindowEvent(event); } @@ -5806,19 +5779,7 @@ bool wxWindowMSW::HandlePressAndTap(int x, int y, WXDWORD flags) { wxPressAndTapEvent event(GetId()); - if ( flags & GF_BEGIN ) - { - event.SetGestureStart(); - } - - event.SetEventObject(this); - event.SetTimestamp(::GetMessageTime()); - event.SetPosition(wxPoint(x, y)); - - if ( flags & GF_END ) - { - event.SetGestureEnd(); - } + InitGestureEvent(event, x, y, flags); return HandleWindowEvent(event); } From db71a10b29d9b929d72c19eb3d09da45387fc0e3 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 17:27:42 +0100 Subject: [PATCH 11/26] Set rotation angle for the initial rotate gesture event in MSW According to MSDN, for the initial rotation even the angle is just passed directly, so use it. --- src/msw/window.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 58aed9f66a..bbf245837f 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -5742,7 +5742,11 @@ bool wxWindowMSW::HandleRotateGesture(int x, int y, // wxEVT_GESTURE_ROTATE wxRotateGestureEvent event(GetId()); - if ( !InitGestureEvent(event, x, y, flags) ) + if ( InitGestureEvent(event, x, y, flags) ) + { + event.SetRotationAngle(angleArgument); + } + else // Not the first event. { // Use angleArgument to obtain the cumulative angle since the gesture // was first started. This angle is in radians and MSW returns negative From bb2887930f94a85493cf9efb2f38a3082f2743b1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 17:34:51 +0100 Subject: [PATCH 12/26] Use wxPoint instead of (x,y) pair in wxPanGestureEvent Using higher level objects makes the code generating and using this event shorter and more clear. --- include/wx/event.h | 16 +++----- include/wx/msw/window.h | 12 +++--- interface/wx/event.h | 18 ++------- samples/event/gestures.cpp | 15 +++++--- src/gtk/window.cpp | 8 ++-- src/msw/window.cpp | 79 +++++++++++++++++--------------------- src/osx/cocoa/window.mm | 13 +++---- 7 files changed, 70 insertions(+), 91 deletions(-) diff --git a/include/wx/event.h b/include/wx/event.h index 70852f9965..c9d95501d8 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -1915,25 +1915,21 @@ public: wxPanGestureEvent(wxWindowID winid = 0) : wxGestureEvent(winid, wxEVT_GESTURE_PAN) { - m_deltaX = 0; - m_deltaY = 0; } - wxPanGestureEvent(const wxPanGestureEvent& event) : wxGestureEvent(event) + wxPanGestureEvent(const wxPanGestureEvent& event) + : wxGestureEvent(event), + m_delta(event.m_delta) { - m_deltaX = event.m_deltaX; - m_deltaY = event.m_deltaY; } - int GetDeltaX() const { return m_deltaX; } - void SetDeltaX(int DeltaX) { m_deltaX = DeltaX; } - int GetDeltaY() const { return m_deltaY; } - void SetDeltaY(int DeltaY) { m_deltaY = DeltaY; } + wxPoint GetDelta() const { return m_delta; } + void SetDelta(const wxPoint& delta) { m_delta = delta; } virtual wxEvent *Clone() const { return new wxPanGestureEvent(*this); } private: - int m_deltaX, m_deltaY; + wxPoint m_delta; wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxPanGestureEvent); }; diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index 1de7a1359c..7f710a5870 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -362,13 +362,13 @@ public: // Common gesture event initialization, returns true if it is the initial // event (GF_BEGIN set in flags), false otherwise. - bool InitGestureEvent(wxGestureEvent& event, int x, int y, WXDWORD flags); + bool InitGestureEvent(wxGestureEvent& event, const wxPoint& pt, WXDWORD flags); - bool HandlePanGesture(int x, int y, WXDWORD flags); - bool HandleZoomGesture(int x, int y, WXDWORD fingerDistance, WXDWORD flags); - bool HandleRotateGesture(int x, int y, WXDWORD angleArgument, WXDWORD flags); - bool HandleTwoFingerTap(int x, int y, WXDWORD flags); - bool HandlePressAndTap(int x, int y, WXDWORD flags); + bool HandlePanGesture(const wxPoint& pt, WXDWORD flags); + bool HandleZoomGesture(const wxPoint& pt, WXDWORD fingerDistance, WXDWORD flags); + bool HandleRotateGesture(const wxPoint& pt, WXDWORD angleArgument, WXDWORD flags); + bool HandleTwoFingerTap(const wxPoint& pt, WXDWORD flags); + bool HandlePressAndTap(const wxPoint& pt, WXDWORD flags); bool HandleChar(WXWPARAM wParam, WXLPARAM lParam); bool HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam); diff --git a/interface/wx/event.h b/interface/wx/event.h index 53030db240..13460cf42e 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -3726,24 +3726,14 @@ public: wxPanGestureEvent(wxWindowID winid = 0); /** - Returns the horizontal component of the distance covered since the previous Pan event. + Returns the distance covered since the previous panning event. */ - int GetDeltaX() const; + wxPoint GetDelta() const; /** - Returns the vertical component of the distance covered since the previous Pan event. + Sets the distance covered since the previous panning event. */ - int GetDeltaY() const; - - /** - Sets the horizontal component of the distance covered since the previous Pan event. - */ - int SetDeltaX(int DeltaX); - - /** - Sets the vertical component of the distance covered since the previous Pan event. - */ - int SetDeltaY(int DeltaY); + void SetDelta(const wxPoint& delta); }; diff --git a/samples/event/gestures.cpp b/samples/event/gestures.cpp index 6a0425668a..ae32890feb 100644 --- a/samples/event/gestures.cpp +++ b/samples/event/gestures.cpp @@ -86,19 +86,22 @@ void MyGesturePanel::OnPan(wxPanGestureEvent& event) wxLogMessage("Pan gesture started\n"); } - wxLogMessage("Pan gesture performed with deltaX = %d and deltaY = %d, with current position (%d,%d)\n", - event.GetDeltaX(), event.GetDeltaY(), event.GetPosition().x, event.GetPosition().y); + const wxPoint delta = event.GetDelta(); + wxLogMessage("Pan gesture performed with delta = (%d,%d), " + "with current position (%d,%d)", + delta.x, delta.y, + event.GetPosition().x, event.GetPosition().y); - // Transform the distance using the tranpose of the matrix, + // Transform the distance using the transpose of the matrix, // in order to translate the image to match the screen coordinates wxMatrix2D m; m_affineMatrix.Get(&m, NULL); - wxPoint2DDouble delta(m.m_11 * event.GetDeltaX() + m.m_12 * event.GetDeltaY(), - m.m_21 * event.GetDeltaX() + m.m_22 * event.GetDeltaY()); + wxPoint2DDouble deltaD(m.m_11 * delta.x + m.m_12 * delta.y, + m.m_21 * delta.x + m.m_22 * delta.y); // Add it to the total translation - m_translateDistance += delta; + m_translateDistance += deltaD; if ( event.IsGestureEnd() ) { diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 55f2717c7f..c05ccb0651 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -2998,22 +2998,22 @@ pan_gesture_callback(GtkGesture* gesture, GtkPanDirection direction, gdouble off { case GTK_PAN_DIRECTION_UP: data->m_allowedGestures |= vertical_pan; - event.SetDeltaY(-delta); + event.SetDelta(wxPoint(0, -delta)); break; case GTK_PAN_DIRECTION_DOWN: data->m_allowedGestures |= vertical_pan; - event.SetDeltaY(delta); + event.SetDelta(wxPoint(0, delta)); break; case GTK_PAN_DIRECTION_RIGHT: data->m_allowedGestures |= horizontal_pan; - event.SetDeltaX(delta); + event.SetDelta(wxPoint(delta, 0)); break; case GTK_PAN_DIRECTION_LEFT: data->m_allowedGestures |= horizontal_pan; - event.SetDeltaX(-delta); + event.SetDelta(wxPoint(-delta, 0)); break; } diff --git a/src/msw/window.cpp b/src/msw/window.cpp index bbf245837f..9fb00bffeb 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -3220,47 +3220,49 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, wxLogDebug("This is Not the window targeted by this gesture!"); } - int x = gestureInfo.ptsLocation.x; - int y = gestureInfo.ptsLocation.y; - ScreenToClient(&x, &y); + const wxPoint pt = ScreenToClient + ( + wxPoint(gestureInfo.ptsLocation.x, + gestureInfo.ptsLocation.y) + ); // dwID field is used to determine the type of gesture switch ( gestureInfo.dwID ) { case GID_PAN: - // (x,y) is the current position of the pan - processed = HandlePanGesture(x, y, gestureInfo.dwFlags); + // Point contains the current position of the pan. + processed = HandlePanGesture(pt, gestureInfo.dwFlags); break; case GID_ZOOM: - // (x,y) is the mid-point of 2 fingers and ullArgument + // Point is the mid-point of 2 fingers and ullArgument // contains the distance between the fingers in its lower // half processed = HandleZoomGesture ( - x, y, + pt, static_cast(gestureInfo.ullArguments), gestureInfo.dwFlags ); break; case GID_ROTATE: - // (x,y) is the center point of rotation and ullArguments + // Point is the center point of rotation and ullArguments // contains the angle of rotation processed = HandleRotateGesture ( - x, y, + pt, static_cast(gestureInfo.ullArguments), gestureInfo.dwFlags ); break; case GID_TWOFINGERTAP: - processed = HandleTwoFingerTap(x, y, gestureInfo.dwFlags); + processed = HandleTwoFingerTap(pt, gestureInfo.dwFlags); break; case GID_PRESSANDTAP: - processed = HandlePressAndTap(x, y, gestureInfo.dwFlags); + processed = HandlePressAndTap(pt, gestureInfo.dwFlags); break; } @@ -5648,12 +5650,12 @@ void wxWindowMSW::GenerateMouseLeave() // --------------------------------------------------------------------------- bool wxWindowMSW::InitGestureEvent(wxGestureEvent& event, - int x, int y, + const wxPoint& pt, WXDWORD flags) { event.SetEventObject(this); event.SetTimestamp(::GetMessageTime()); - event.SetPosition(wxPoint(x, y)); + event.SetPosition(pt); if ( flags & GF_BEGIN ) event.SetGestureStart(); @@ -5664,36 +5666,31 @@ bool wxWindowMSW::InitGestureEvent(wxGestureEvent& event, return (flags & GF_BEGIN) != 0; } -bool wxWindowMSW::HandlePanGesture(int x, int y, WXDWORD flags) +bool wxWindowMSW::HandlePanGesture(const wxPoint& pt, WXDWORD flags) { // wxEVT_GESTURE_PAN wxPanGestureEvent event(GetId()); - // These are used to calculate the pan delta - static int s_previousLocationX, s_previousLocationY; + // This is used to calculate the pan delta. + static wxPoint s_previousLocation; // If the gesture has just started, store the current point to determine // the pan delta later on. - if ( InitGestureEvent(event, x, y, flags) ) + if ( InitGestureEvent(event, pt, flags) ) { - s_previousLocationX = x; - s_previousLocationY = y; + s_previousLocation = pt; } // Determine the horizontal and vertical changes - int DeltaX = x - s_previousLocationX, DeltaY = y - s_previousLocationY; - - event.SetDeltaX(DeltaX); - event.SetDeltaY(DeltaY); + event.SetDelta(pt - s_previousLocation); // Update the last gesture event point - s_previousLocationX = x; - s_previousLocationY = y; + s_previousLocation = pt; return HandleWindowEvent(event); } -bool wxWindowMSW::HandleZoomGesture(int x, int y, +bool wxWindowMSW::HandleZoomGesture(const wxPoint& pt, WXDWORD fingerDistance, WXDWORD flags) { @@ -5701,14 +5698,14 @@ bool wxWindowMSW::HandleZoomGesture(int x, int y, wxZoomGestureEvent event(GetId()); // These are used to calculate the center of the zoom and zoom factor - static int s_previousLocationX, s_previousLocationY, s_intialFingerDistance; + static wxPoint s_previousLocation; + static int s_intialFingerDistance; // This flag indicates that the gesture has just started, store the current // point and distance between the fingers for future calculations. - if ( InitGestureEvent(event, x, y, flags) ) + if ( InitGestureEvent(event, pt, flags) ) { - s_previousLocationX = x; - s_previousLocationY = y; + s_previousLocation = pt; s_intialFingerDistance = fingerDistance; } @@ -5717,32 +5714,28 @@ bool wxWindowMSW::HandleZoomGesture(int x, int y, // is usually some error, which can cause the center to shift slightly. So, // it is recommended to take the average of center of fingers in the // current and last positions. - wxPoint pt; - pt.x = (s_previousLocationX + x) / 2; - pt.y = (s_previousLocationY + y) / 2; + const wxPoint ptCenter = (s_previousLocation + pt)/2; const double zoomFactor = (double) fingerDistance / s_intialFingerDistance; event.SetZoomFactor(zoomFactor); - // This is not a gesture point but the center of a zoom - event.SetPosition(pt); + event.SetPosition(ptCenter); // Update gesture event point - s_previousLocationX = x; - s_previousLocationY = y; + s_previousLocation = pt; return HandleWindowEvent(event); } -bool wxWindowMSW::HandleRotateGesture(int x, int y, +bool wxWindowMSW::HandleRotateGesture(const wxPoint& pt, WXDWORD angleArgument, WXDWORD flags) { // wxEVT_GESTURE_ROTATE wxRotateGestureEvent event(GetId()); - if ( InitGestureEvent(event, x, y, flags) ) + if ( InitGestureEvent(event, pt, flags) ) { event.SetRotationAngle(angleArgument); } @@ -5769,21 +5762,21 @@ bool wxWindowMSW::HandleRotateGesture(int x, int y, return HandleWindowEvent(event); } -bool wxWindowMSW::HandleTwoFingerTap(int x, int y, WXDWORD flags) +bool wxWindowMSW::HandleTwoFingerTap(const wxPoint& pt, WXDWORD flags) { // wxEVT_TWO_FINGER_TAP wxTwoFingerTapEvent event(GetId()); - InitGestureEvent(event, x, y, flags); + InitGestureEvent(event, pt, flags); return HandleWindowEvent(event); } -bool wxWindowMSW::HandlePressAndTap(int x, int y, WXDWORD flags) +bool wxWindowMSW::HandlePressAndTap(const wxPoint& pt, WXDWORD flags) { wxPressAndTapEvent event(GetId()); - InitGestureEvent(event, x, y, flags); + InitGestureEvent(event, pt, flags); return HandleWindowEvent(event); } diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 16f44d1eec..8901482c27 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -1525,13 +1525,12 @@ void wxWidgetCocoaImpl::PanGestureEvent(NSPanGestureRecognizer* panGestureRecogn nspoint = [panGestureRecognizer translationInView:m_osxView]; pt = wxFromNSPoint(m_osxView, nspoint); - static int s_lastLocationX = 0, s_lastLocationY = 0; + static wxPoint s_lastLocation; if ( gestureState == NSGestureRecognizerStateBegan ) { wxevent.SetGestureStart(); - s_lastLocationX = 0; - s_lastLocationY = 0; + s_lastLocation = wxPoint(0, 0); } if ( gestureState == NSGestureRecognizerStateEnded ) @@ -1539,12 +1538,10 @@ void wxWidgetCocoaImpl::PanGestureEvent(NSPanGestureRecognizer* panGestureRecogn wxevent.SetGestureEnd(); } - // Set the offsets - wxevent.SetDeltaX(pt.x - s_lastLocationX); - wxevent.SetDeltaY(pt.y - s_lastLocationY); + // Set the offset + wxevent.SetDelta(pt - s_lastLocation); - s_lastLocationX = pt.x; - s_lastLocationY = pt.y; + s_lastLocation = pt; GetWXPeer()->HandleWindowEvent(wxevent); } From e7f4e232fa1a4518196c431138b99f5757535599 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 18:45:53 +0100 Subject: [PATCH 13/26] Use "class" for wxGTK wxWindowGesturesData definition No real changes, just be consistent with wxWindowGesturesData forward declaration as "class", clang (and MSVC) warn about mismatching keywords being used for declaration and definition. --- src/gtk/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index c05ccb0651..36c049b5d2 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -235,8 +235,9 @@ static bool gs_inSizeAllocate; #ifdef wxGTK_HAS_GESTURES_SUPPORT // Per-window data for gestures support. -struct wxWindowGesturesData +class wxWindowGesturesData { +public: wxWindowGesturesData(wxWindow* win, GtkWidget *widget); ~wxWindowGesturesData(); From 842dd1cfd96aeff4a9206d99e85a16c38df840c9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 19:23:34 +0100 Subject: [PATCH 14/26] Add wxWindow::EnableTouchEvents() Don't request touch event generation for all windows by default, this has an inherent overhead and is not needed for 99% of the application windows, so require calling EnableTouchEvents() explicitly to do it instead. Note that this requires properly initializing gesture recognizers in wxOSX now that they're not always allocated, otherwise releasing them when destroying the window would crash. --- include/wx/gtk/window.h | 3 ++ include/wx/msw/window.h | 1 + include/wx/osx/cocoa/private.h | 1 + include/wx/osx/core/private.h | 2 ++ include/wx/osx/iphone/private.h | 1 + include/wx/osx/window.h | 1 + include/wx/window.h | 14 ++++++++++ interface/wx/event.h | 4 +++ interface/wx/window.h | 13 +++++++++ samples/event/gestures.cpp | 7 +++++ src/gtk/window.cpp | 26 +++++++++++++---- src/msw/window.cpp | 49 +++++++++++++++++++-------------- src/osx/cocoa/window.mm | 40 ++++++++++++++++++++------- src/osx/window_osx.cpp | 5 ++++ 14 files changed, 131 insertions(+), 36 deletions(-) diff --git a/include/wx/gtk/window.h b/include/wx/gtk/window.h index d9db061dc6..0e6d2633c3 100644 --- a/include/wx/gtk/window.h +++ b/include/wx/gtk/window.h @@ -77,6 +77,9 @@ public: virtual bool Reparent( wxWindowBase *newParent ) wxOVERRIDE; virtual void WarpPointer(int x, int y) wxOVERRIDE; +#ifdef __WXGTK3__ + virtual bool EnableTouchEvents(int eventsMask) wxOVERRIDE; +#endif // __WXGTK3__ virtual void Refresh( bool eraseBackground = true, const wxRect *rect = (const wxRect *) NULL ) wxOVERRIDE; diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index 7f710a5870..1a6af48f5b 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -100,6 +100,7 @@ public: virtual bool Reparent(wxWindowBase *newParent) wxOVERRIDE; virtual void WarpPointer(int x, int y) wxOVERRIDE; + virtual bool EnableTouchEvents(int eventsMask) wxOVERRIDE; virtual void Refresh( bool eraseBackground = true, const wxRect *rect = (const wxRect *) NULL ) wxOVERRIDE; diff --git a/include/wx/osx/cocoa/private.h b/include/wx/osx/cocoa/private.h index 9fd9101a84..dd20e0074e 100644 --- a/include/wx/osx/cocoa/private.h +++ b/include/wx/osx/cocoa/private.h @@ -128,6 +128,7 @@ public : void SetToolTip( wxToolTip* tooltip ); void InstallEventHandler( WXWidget control = NULL ); + bool EnableTouchEvents(int eventsMask); virtual bool ShouldHandleKeyNavigation(const wxKeyEvent &event) const; bool DoHandleKeyNavigation(const wxKeyEvent &event); diff --git a/include/wx/osx/core/private.h b/include/wx/osx/core/private.h index a372aa529a..5b012f9048 100644 --- a/include/wx/osx/core/private.h +++ b/include/wx/osx/core/private.h @@ -326,6 +326,8 @@ public : virtual void InstallEventHandler( WXWidget control = NULL ) = 0; + virtual bool EnableTouchEvents(int eventsMask) = 0; + // Mechanism used to keep track of whether a change should send an event // Do SendEvents(false) when starting actions that would trigger programmatic events // and SendEvents(true) at the end of the block. diff --git a/include/wx/osx/iphone/private.h b/include/wx/osx/iphone/private.h index b88a863a54..484e7525ee 100644 --- a/include/wx/osx/iphone/private.h +++ b/include/wx/osx/iphone/private.h @@ -105,6 +105,7 @@ public : void SetFont( const wxFont & font , const wxColour& foreground , long windowStyle, bool ignoreBlack = true ); void InstallEventHandler( WXWidget control = NULL ); + bool EnableTouchEvents(int WXUNUSED(eventsMask)) { return false; } virtual void DoNotifyFocusEvent(bool receivedFocus, wxWidgetImpl* otherWindow); diff --git a/include/wx/osx/window.h b/include/wx/osx/window.h index 7db20f4810..caee9c1659 100644 --- a/include/wx/osx/window.h +++ b/include/wx/osx/window.h @@ -77,6 +77,7 @@ public: virtual void SetFocus() wxOVERRIDE; virtual void WarpPointer( int x, int y ) wxOVERRIDE; + virtual bool EnableTouchEvents(int eventsMask) wxOVERRIDE; virtual void Refresh( bool eraseBackground = true, const wxRect *rect = NULL ) wxOVERRIDE; diff --git a/include/wx/window.h b/include/wx/window.h index 440f097a21..27a9c856cb 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -133,6 +133,13 @@ enum wxShowEffect wxSHOW_EFFECT_MAX }; +// Values for EnableTouchEvents() mask. +enum +{ + wxTOUCH_NONE = 0x0000, + wxTOUCH_ALL_GESTURES = 0x00ff +}; + // flags for SendSizeEvent() enum { @@ -1033,6 +1040,13 @@ public: virtual bool HasCapture() const { return (wxWindow *)this == GetCapture(); } + // enable the specified touch events for this window, return false if + // the requested events are not supported + virtual bool EnableTouchEvents(int WXUNUSED(eventsMask)) + { + return false; + } + // painting the window // ------------------- diff --git a/interface/wx/event.h b/interface/wx/event.h index 13460cf42e..ae1eaeee9a 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -3649,6 +3649,10 @@ public: /** @class wxGestureEvent This is the base class for all supported gesture events. + @note Gesture events are not generated by default, you must call + wxWindow::EnableTouchEvents() with the appropriate parameter to + request their generation. + @library{wxcore} @category{events} diff --git a/interface/wx/window.h b/interface/wx/window.h index 0fa2a58c50..b0712fb378 100644 --- a/interface/wx/window.h +++ b/interface/wx/window.h @@ -3424,6 +3424,19 @@ public: */ virtual void WarpPointer(int x, int y); + /** + Request generation of touch events for this window. + + @param eventsMask Either wxTOUCH_NONE or wxTOUCH_ALL_GESTURES to + disable or enable gesture events for this window. + + @return @true if the specified events were enabled or @false if the + current platform doesn't support touch events. + + @since 3.1.1 + */ + virtual bool EnableTouchEvents(int eventsMask); + //@} diff --git a/samples/event/gestures.cpp b/samples/event/gestures.cpp index ae32890feb..06b6cd794a 100644 --- a/samples/event/gestures.cpp +++ b/samples/event/gestures.cpp @@ -43,6 +43,13 @@ MyGesturePanel::MyGesturePanel(MyGestureFrame *parent) : wxPanel(parent, wxID_AN Bind(wxEVT_PAINT, &MyGesturePanel::OnPaint, this); } + if ( !EnableTouchEvents(wxTOUCH_ALL_GESTURES) ) + { + wxLogError("Failed to enable touch events"); + + // Still bind event handlers just in case they still work? + } + // Event handlers Bind(wxEVT_GESTURE_PAN, &MyGesturePanel::OnPan, this); Bind(wxEVT_GESTURE_ZOOM, &MyGesturePanel::OnZoom, this); diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 36c049b5d2..26bc0734b7 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -3435,6 +3435,26 @@ wxWindowGesturesData::~wxWindowGesturesData() #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 ) + { + m_gesturesData = new wxWindowGesturesData(this, GetConnectWidget()); + return true; + } +#endif // wxGTK_HAS_GESTURES_SUPPORT + + return wxWindowBase::EnableTouchEvents(eventsMask); +} + +#endif // __WXGTK3__ + void wxWindowGTK::ConnectWidget( GtkWidget *widget ) { static bool isSourceAttached; @@ -3477,12 +3497,6 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget ) G_CALLBACK (gtk_window_enter_callback), this); g_signal_connect (widget, "leave_notify_event", G_CALLBACK (gtk_window_leave_callback), this); - -#ifdef wxGTK_HAS_GESTURES_SUPPORT - // Check if gestures support is also available during run-time. - if ( gtk_check_version(3, 14, 0) == NULL ) - m_gesturesData = new wxWindowGesturesData(this, widget); -#endif // wxGTK_HAS_GESTURES_SUPPORT } void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height) diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 9fb00bffeb..0de02b2508 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -906,6 +906,35 @@ void wxWindowMSW::WarpPointer(int x, int y) } } +bool wxWindowMSW::EnableTouchEvents(int eventsMask) +{ +#ifdef WM_GESTURE + if ( GestureFuncs::IsOk() && eventsMask == wxTOUCH_ALL_GESTURES ) + { + // Configure to receive all gestures + GESTURECONFIG gestureConfig = {0, GC_ALLGESTURES, 0}; + + if ( !GestureFuncs::SetGestureConfig() + ( + m_hWnd, + 0, // Reserved, must be always 0. + 1, // Number of gesture configurations. + &gestureConfig, // Pointer to the first one. + sizeof(GESTURECONFIG) // Size of each configuration. + ) + ) + { + wxLogLastError("SetGestureConfig"); + return false; + } + + return true; + } +#endif // WM_GESTURE + + return wxWindowBase::EnableTouchEvents(eventsMask); +} + void wxWindowMSW::MSWUpdateUIState(int action, int state) { // we send WM_CHANGEUISTATE so if nothing needs changing then the system @@ -3814,26 +3843,6 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass, return false; } -#ifdef WM_GESTURE - if ( GestureFuncs::IsOk() ) - { - // Configure to receive all gestures - GESTURECONFIG gestureConfig = {0, GC_ALLGESTURES, 0}; - - if ( !GestureFuncs::SetGestureConfig() - ( - m_hWnd, - 0, // Reserved, must be always 0. - 1, // Number of gesture configurations. - &gestureConfig, // Pointer to the first one. - sizeof(GESTURECONFIG) // Size of each configuration. - ) - ) - { - wxLogLastError("SetGestureConfig"); - } - } -#endif // WM_GESTURE SubclassWin(m_hWnd); diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 8901482c27..15abbc43b3 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -2240,16 +2240,6 @@ void wxOSXCocoaClassAddWXMethods(Class c) wxOSX_CLASS_ADD_METHOD(c, @selector(mouseEntered:), (IMP) wxOSX_mouseEvent, "v@:@" ) wxOSX_CLASS_ADD_METHOD(c, @selector(mouseExited:), (IMP) wxOSX_mouseEvent, "v@:@" ) -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - wxOSX_CLASS_ADD_METHOD(c, @selector(handlePanGesture:), (IMP) wxOSX_panGestureEvent, "v@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(handleZoomGesture:), (IMP) wxOSX_zoomGestureEvent, "v@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(handleRotateGesture:), (IMP) wxOSX_rotateGestureEvent, "v@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(handleLongPressGesture:), (IMP) wxOSX_longPressEvent, "v@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(touchesBeganWithEvent:), (IMP) wxOSX_touchesBegan, "v@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(touchesMovedWithEvent:), (IMP) wxOSX_touchesMoved, "v@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(touchesEndedWithEvent:), (IMP) wxOSX_touchesEnded, "v@:@" ) -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - wxOSX_CLASS_ADD_METHOD(c, @selector(magnifyWithEvent:), (IMP)wxOSX_mouseEvent, "v@:@") wxOSX_CLASS_ADD_METHOD(c, @selector(cursorUpdate:), (IMP) wxOSX_cursorUpdate, "v@:@" ) @@ -2333,6 +2323,11 @@ void wxWidgetCocoaImpl::Init() m_hasEditor = false; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + m_panGestureRecognizer = nil; + m_magnificationGestureRecognizer = nil; + m_rotationGestureRecognizer = nil; + m_pressGestureRecognizer = nil; + m_touchCount = 0; m_lastTouchTime = 0; m_allowedGestures = 0; @@ -3289,23 +3284,43 @@ void wxWidgetCocoaImpl::InstallEventHandler( WXWidget control ) NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect: NSZeroRect options: options owner: m_osxView userInfo: nil]; [m_osxView addTrackingArea: area]; [area release]; +} +bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) +{ #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 if ( wxPlatformInfo::Get().CheckOSVersion(10, 10) ) { if ( IsUserPane() ) { + 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@:@" ); 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_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_pressGestureRecognizer = [[NSPressGestureRecognizer alloc] initWithTarget:m_osxView action: @selector(handleLongPressGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handleLongPressGesture:)) ) + class_addMethod(cls, @selector(handleLongPressGesture:), (IMP) wxOSX_longPressEvent, "v@:@" ); + + if ( !class_respondsToSelector(cls, @selector(touchesBeganWithEvent:)) ) + class_addMethod(cls, @selector(touchesBeganWithEvent:), (IMP) wxOSX_touchesBegan, "v@:@" ); + if ( !class_respondsToSelector(cls, @selector(touchesMovedWithEvent:)) ) + class_addMethod(cls, @selector(touchesMovedWithEvent:), (IMP) wxOSX_touchesMoved, "v@:@" ); + 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]; @@ -3313,9 +3328,14 @@ void wxWidgetCocoaImpl::InstallEventHandler( WXWidget control ) [m_osxView addGestureRecognizer:m_pressGestureRecognizer]; [m_osxView setAcceptsTouchEvents:YES]; + + return true; } } #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + + wxUnusedVar(eventsMask); + return false; } bool wxWidgetCocoaImpl::DoHandleCharEvent(NSEvent *event, NSString *text) diff --git a/src/osx/window_osx.cpp b/src/osx/window_osx.cpp index 823c0beae8..c725ef8d57 100644 --- a/src/osx/window_osx.cpp +++ b/src/osx/window_osx.cpp @@ -1429,6 +1429,11 @@ void wxWindowMac::WarpPointer(int x_pos, int y_pos) #endif } +bool wxWindowMac::EnableTouchEvents(int eventsMask) +{ + return GetPeer() ? GetPeer()->EnableTouchEvents(eventsMask) : false; +} + int wxWindowMac::GetScrollPos(int orient) const { #if wxUSE_SCROLLBAR From a8dfaa569bdb45e029c31e7a7f4cf20b01839df8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 22:46:56 +0100 Subject: [PATCH 15/26] Allow enabling individual touch gesture events Implement support for enabling just some gesture events instead of having to choose between getting none or all of them. Also make wxTOUCH_NONE really disable the gestures events generation instead of just doing nothing as before. --- include/wx/window.h | 11 ++- interface/wx/window.h | 71 +++++++++++++++ src/gtk/window.cpp | 187 +++++++++++++++++++++++++++------------- src/msw/window.cpp | 99 +++++++++++++++++++-- src/osx/cocoa/window.mm | 75 +++++++++++----- 5 files changed, 356 insertions(+), 87 deletions(-) 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; From fd3a3e607b36877882cbc3e1019d0f0e46f19a15 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 23:17:11 +0100 Subject: [PATCH 16/26] Extract SetupCoordinates() from wxWidgetCocoaImpl Add a free function that can be used from outside wxWidgetCocoaImpl class too and keep the old method as a trivial wrapper around it. No changes, this is a pure refactoring. --- src/osx/cocoa/window.mm | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 9df2c4b17c..44abf0198c 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -528,29 +528,35 @@ bool g_lastButtonWasFakeRight = false ; - (CGFloat)scrollingDeltaY; @end -void wxWidgetCocoaImpl::SetupCoordinates(wxCoord &x, wxCoord &y, NSEvent* nsEvent) +static void +wxSetupCoordinates(NSView* view, wxCoord &x, wxCoord &y, NSEvent* nsEvent) { NSRect locationInWindow = NSZeroRect; locationInWindow.origin = [nsEvent locationInWindow]; // adjust coordinates for the window of the target view - if ( [nsEvent window] != [m_osxView window] ) + if ( [nsEvent window] != [view window] ) { if ( [nsEvent window] != nil ) locationInWindow = [[nsEvent window] convertRectToScreen:locationInWindow]; - if ( [m_osxView window] != nil ) - locationInWindow = [[m_osxView window] convertRectFromScreen:locationInWindow]; + if ( [view window] != nil ) + locationInWindow = [[view window] convertRectFromScreen:locationInWindow]; } - NSPoint locationInView = [m_osxView convertPoint:locationInWindow.origin fromView:nil]; - wxPoint locationInViewWX = wxFromNSPoint( m_osxView, locationInView ); + NSPoint locationInView = [view convertPoint:locationInWindow.origin fromView:nil]; + wxPoint locationInViewWX = wxFromNSPoint( view, locationInView ); x = locationInViewWX.x; y = locationInViewWX.y; } +void wxWidgetCocoaImpl::SetupCoordinates(wxCoord &x, wxCoord &y, NSEvent* nsEvent) +{ + wxSetupCoordinates(m_osxView, x, y, nsEvent); +} + void wxWidgetCocoaImpl::SetupMouseEvent( wxMouseEvent &wxevent , NSEvent * nsEvent ) { int eventType = [nsEvent type]; From 3aefb679e45c4a71b6d600a31eccac39f115bfee Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 23:18:20 +0100 Subject: [PATCH 17/26] Encapsulate gestures-related data in wxCocoaGesturesImpl Avoid allocating a lot of almost never used data for every window and use just a single pointer for holding all of it instead. --- include/wx/osx/cocoa/private.h | 14 +- src/osx/cocoa/window.mm | 267 +++++++++++++++++++++------------ 2 files changed, 169 insertions(+), 112 deletions(-) diff --git a/include/wx/osx/cocoa/private.h b/include/wx/osx/cocoa/private.h index dd20e0074e..30537bd0a3 100644 --- a/include/wx/osx/cocoa/private.h +++ b/include/wx/osx/cocoa/private.h @@ -201,18 +201,8 @@ protected: bool m_hasEditor; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - NSPanGestureRecognizer *m_panGestureRecognizer; - NSMagnificationGestureRecognizer *m_magnificationGestureRecognizer; - NSRotationGestureRecognizer *m_rotationGestureRecognizer; - NSPressGestureRecognizer *m_pressGestureRecognizer; - - int m_allowedGestures; - int m_activeGestures; - unsigned int m_touchCount; - unsigned int m_lastTouchTime; - - // Used to keep track of the touch corresponding to "press" in Press and Tap gesture - NSTouch* m_initialTouch; + // Data used for gesture support, if available. + class wxCocoaGesturesImpl* m_gesturesImpl; #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxWidgetCocoaImpl); diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 44abf0198c..39eb5f7c8c 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -1500,6 +1500,126 @@ void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd) } #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + +// Class containing data used for gestures support. +class wxCocoaGesturesImpl +{ +public: + wxCocoaGesturesImpl(wxWidgetCocoaImpl* impl, NSView* view, int eventsMask) + : m_win(impl->GetWXPeer()), + m_view(view) + { + m_touchCount = 0; + m_lastTouchTime = 0; + m_allowedGestures = 0; + m_activeGestures = 0; + m_initialTouch = NULL; + + Class cls = [m_view class]; + + if ( eventsMask & wxTOUCH_PAN_GESTURES ) + { + eventsMask &= ~wxTOUCH_PAN_GESTURES; + + m_panGestureRecognizer = + [[NSPanGestureRecognizer alloc] initWithTarget:m_view action: @selector(handlePanGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handlePanGesture:)) ) + class_addMethod(cls, @selector(handlePanGesture:), (IMP) wxOSX_panGestureEvent, "v@:@" ); + [m_view addGestureRecognizer:m_panGestureRecognizer]; + } + else + { + m_panGestureRecognizer = nil; + } + + if ( eventsMask & wxTOUCH_ZOOM_GESTURE ) + { + eventsMask &= ~wxTOUCH_ZOOM_GESTURE; + + m_magnificationGestureRecognizer = + [[NSMagnificationGestureRecognizer alloc] initWithTarget:m_view action: @selector(handleZoomGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handleZoomGesture:)) ) + class_addMethod(cls, @selector(handleZoomGesture:), (IMP) wxOSX_zoomGestureEvent, "v@:@" ); + [m_view addGestureRecognizer:m_magnificationGestureRecognizer]; + } + else + { + m_magnificationGestureRecognizer = nil; + } + + if ( eventsMask & wxTOUCH_ROTATE_GESTURE ) + { + eventsMask &= ~wxTOUCH_ROTATE_GESTURE; + + m_rotationGestureRecognizer = + [[NSRotationGestureRecognizer alloc] initWithTarget:m_view action: @selector(handleRotateGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handleRotateGesture:)) ) + class_addMethod(cls, @selector(handleRotateGesture:), (IMP) wxOSX_rotateGestureEvent, "v@:@" ); + [m_view addGestureRecognizer:m_rotationGestureRecognizer]; + } + else + { + m_rotationGestureRecognizer = nil; + } + + if ( eventsMask & wxTOUCH_PRESS_GESTURES ) + { + eventsMask &= ~wxTOUCH_PRESS_GESTURES; + + m_pressGestureRecognizer = + [[NSPressGestureRecognizer alloc] initWithTarget:m_view action: @selector(handleLongPressGesture:)]; + if ( !class_respondsToSelector(cls, @selector(handleLongPressGesture:)) ) + class_addMethod(cls, @selector(handleLongPressGesture:), (IMP) wxOSX_longPressEvent, "v@:@" ); + [m_view addGestureRecognizer:m_pressGestureRecognizer]; + } + else + { + m_pressGestureRecognizer = nil; + } + + 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@:@" ); + if ( !class_respondsToSelector(cls, @selector(touchesMovedWithEvent:)) ) + class_addMethod(cls, @selector(touchesMovedWithEvent:), (IMP) wxOSX_touchesMoved, "v@:@" ); + if ( !class_respondsToSelector(cls, @selector(touchesEndedWithEvent:)) ) + class_addMethod(cls, @selector(touchesEndedWithEvent:), (IMP) wxOSX_touchesEnded, "v@:@" ); + } + + ~wxCocoaGesturesImpl() + { + [m_panGestureRecognizer release]; + [m_magnificationGestureRecognizer release]; + [m_rotationGestureRecognizer release]; + [m_pressGestureRecognizer release]; + [m_initialTouch release]; + } + + void TouchesBegan(NSEvent* event); + void TouchesMoved(NSEvent* event); + void TouchesEnded(NSEvent* event); + +private: + wxWindowMac* const m_win; + NSView* const m_view; + + NSPanGestureRecognizer *m_panGestureRecognizer; + NSMagnificationGestureRecognizer *m_magnificationGestureRecognizer; + NSRotationGestureRecognizer *m_rotationGestureRecognizer; + NSPressGestureRecognizer *m_pressGestureRecognizer; + + int m_allowedGestures; + int m_activeGestures; + unsigned int m_touchCount; + unsigned int m_lastTouchTime; + + // Used to keep track of the touch corresponding to "press" in Press and Tap gesture + NSTouch* m_initialTouch; + + wxDECLARE_NO_COPY_CLASS(wxCocoaGesturesImpl); +}; + void wxWidgetCocoaImpl::PanGestureEvent(NSPanGestureRecognizer* panGestureRecognizer) { NSGestureRecognizerState gestureState; @@ -1700,7 +1820,13 @@ enum TrackedGestures void wxWidgetCocoaImpl::TouchesBegan(WX_NSEvent event) { - NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:m_osxView]; + if ( m_gesturesImpl ) + m_gesturesImpl->TouchesBegan(event); +} + +void wxCocoaGesturesImpl::TouchesBegan(NSEvent* event) +{ + NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:m_view]; m_touchCount += touches.count; m_allowedGestures &= ~two_finger_tap; @@ -1734,7 +1860,7 @@ void wxWidgetCocoaImpl::TouchesBegan(WX_NSEvent event) m_initialTouch = [[array objectAtIndex:0] copy]; } - touches = [event touchesMatchingPhase:NSTouchPhaseStationary inView:m_osxView]; + touches = [event touchesMatchingPhase:NSTouchPhaseStationary inView:m_view]; // Check if 2 fingers are placed within the time interval of 200 milliseconds if ( m_touchCount == 2 && touches.count == 1 && eventTimeStamp - m_lastTouchTime <= wxTwoFingerTimeInterval ) @@ -1750,6 +1876,12 @@ void wxWidgetCocoaImpl::TouchesBegan(WX_NSEvent event) } void wxWidgetCocoaImpl::TouchesMoved(WX_NSEvent event) +{ + if ( m_gesturesImpl ) + m_gesturesImpl->TouchesMoved(event); +} + +void wxCocoaGesturesImpl::TouchesMoved(NSEvent* event) { // Cancel Two Finger Tap Event if there is any movement m_allowedGestures &= ~two_finger_tap; @@ -1759,7 +1891,7 @@ void wxWidgetCocoaImpl::TouchesMoved(WX_NSEvent event) return; } - NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:m_osxView]; + NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:m_view]; NSArray* array = [touches allObjects]; @@ -1776,15 +1908,15 @@ void wxWidgetCocoaImpl::TouchesMoved(WX_NSEvent event) // and the gesture is active. if ( m_activeGestures & press_and_tap ) { - wxPressAndTapEvent wxevent(GetWXPeer()->GetId()); - wxevent.SetEventObject(GetWXPeer()); + wxPressAndTapEvent wxevent(m_win->GetId()); + wxevent.SetEventObject(m_win); // Get the mouse coordinates wxCoord x, y; - SetupCoordinates(x, y, event); + wxSetupCoordinates(m_view, x, y, event); wxevent.SetPosition(wxPoint (x,y)); - GetWXPeer()->HandleWindowEvent(wxevent); + m_win->HandleWindowEvent(wxevent); } // Cancel Press and Tap Event if the touch corresponding to "press" is moving @@ -1801,7 +1933,13 @@ void wxWidgetCocoaImpl::TouchesMoved(WX_NSEvent event) void wxWidgetCocoaImpl::TouchesEnded(WX_NSEvent event) { - NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:m_osxView]; + if ( m_gesturesImpl ) + m_gesturesImpl->TouchesEnded(event); +} + +void wxCocoaGesturesImpl::TouchesEnded(NSEvent* event) +{ + NSSet* touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:m_view]; m_touchCount -= touches.count; @@ -1813,17 +1951,17 @@ void wxWidgetCocoaImpl::TouchesEnded(WX_NSEvent event) (touches.count == 2 || (touches.count == 1 && eventTimeStamp - m_lastTouchTime <= wxTwoFingerTimeInterval)) ) { - wxTwoFingerTapEvent wxevent(GetWXPeer()->GetId()); - wxevent.SetEventObject(GetWXPeer()); + wxTwoFingerTapEvent wxevent(m_win->GetId()); + wxevent.SetEventObject(m_win); wxevent.SetGestureStart(); wxevent.SetGestureEnd(); // Get the mouse coordinates wxCoord x, y; - SetupCoordinates(x, y, event); + wxSetupCoordinates(m_view, x, y, event); wxevent.SetPosition(wxPoint (x,y)); - GetWXPeer()->HandleWindowEvent(wxevent); + m_win->HandleWindowEvent(wxevent); } // If Two Finger Tap Event is possible in future then save the timestamp to use it when the other touch @@ -1862,12 +2000,12 @@ void wxWidgetCocoaImpl::TouchesEnded(WX_NSEvent event) return; } - wxPressAndTapEvent wxevent(GetWXPeer()->GetId()); - wxevent.SetEventObject(GetWXPeer()); + wxPressAndTapEvent wxevent(m_win->GetId()); + wxevent.SetEventObject(m_win); // Get the mouse coordinates wxCoord x, y; - SetupCoordinates(x, y, event); + wxSetupCoordinates(m_view, x, y, event); wxevent.SetPosition(wxPoint (x,y)); if ( !(m_activeGestures & press_and_tap) ) @@ -1886,7 +2024,7 @@ void wxWidgetCocoaImpl::TouchesEnded(WX_NSEvent event) [m_initialTouch release]; } - GetWXPeer()->HandleWindowEvent(wxevent); + m_win->HandleWindowEvent(wxevent); } else @@ -2329,16 +2467,7 @@ void wxWidgetCocoaImpl::Init() m_hasEditor = false; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - m_panGestureRecognizer = nil; - m_magnificationGestureRecognizer = nil; - m_rotationGestureRecognizer = nil; - m_pressGestureRecognizer = nil; - - m_touchCount = 0; - m_lastTouchTime = 0; - m_allowedGestures = 0; - m_activeGestures = 0; - m_initialTouch = NULL; + m_gesturesImpl = NULL; #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 } @@ -2360,19 +2489,7 @@ wxWidgetCocoaImpl::~wxWidgetCocoaImpl() CFRelease(m_osxView); #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - if ( wxPlatformInfo::Get().CheckOSVersion(10, 10) ) - { - if ( IsUserPane() ) - { - [m_panGestureRecognizer release]; - [m_magnificationGestureRecognizer release]; - [m_rotationGestureRecognizer release]; - [m_pressGestureRecognizer release]; - - if ( m_initialTouch ) - [m_initialTouch release]; - } - } + delete m_gesturesImpl; #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 } @@ -3299,75 +3416,25 @@ bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) { if ( IsUserPane() ) { + // We need to get rid of the old data, if we have it, in any case. + delete m_gesturesImpl; + if ( eventsMask == wxTOUCH_NONE ) { - [m_panGestureRecognizer release]; - [m_magnificationGestureRecognizer release]; - [m_rotationGestureRecognizer release]; - [m_pressGestureRecognizer release]; + if ( m_gesturesImpl ) + { + m_gesturesImpl = NULL; - [m_osxView setAcceptsTouchEvents:NO]; - - return true; + [m_osxView setAcceptsTouchEvents:NO]; + } } - - Class cls = [m_osxView class]; - - if ( eventsMask & wxTOUCH_PAN_GESTURES ) + else // We do want to have gesture events. { - eventsMask &= ~wxTOUCH_PAN_GESTURES; + m_gesturesImpl = new wxCocoaGesturesImpl(this, m_osxView, eventsMask); - 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_osxView setAcceptsTouchEvents:YES]; } - if ( eventsMask & wxTOUCH_ZOOM_GESTURE ) - { - eventsMask &= ~wxTOUCH_ZOOM_GESTURE; - - 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@:@" ); - if ( !class_respondsToSelector(cls, @selector(touchesMovedWithEvent:)) ) - class_addMethod(cls, @selector(touchesMovedWithEvent:), (IMP) wxOSX_touchesMoved, "v@:@" ); - if ( !class_respondsToSelector(cls, @selector(touchesEndedWithEvent:)) ) - class_addMethod(cls, @selector(touchesEndedWithEvent:), (IMP) wxOSX_touchesEnded, "v@:@" ); - - [m_osxView setAcceptsTouchEvents:YES]; - return true; } } From 9cb1f25a25735f95d8b157fa7052a8831d978f4f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 21 Nov 2017 23:43:15 +0100 Subject: [PATCH 18/26] Use global map for storing wxCocoaGesturesImpl This decreases the speed of access to this struct (which shouldn't matter that much) but avoids allocating an extra and almost always unneeded pointer for each and every window, which seems like a good trade-off. --- include/wx/osx/cocoa/private.h | 5 -- src/osx/cocoa/window.mm | 87 +++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/include/wx/osx/cocoa/private.h b/include/wx/osx/cocoa/private.h index 30537bd0a3..db182e4479 100644 --- a/include/wx/osx/cocoa/private.h +++ b/include/wx/osx/cocoa/private.h @@ -200,11 +200,6 @@ protected: // events, don't resend them bool m_hasEditor; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - // Data used for gesture support, if available. - class wxCocoaGesturesImpl* m_gesturesImpl; -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxWidgetCocoaImpl); }; diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 39eb5f7c8c..ae28a12e2e 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -24,6 +24,7 @@ #endif #include "wx/evtloop.h" +#include "wx/hashmap.h" #if wxUSE_CARET #include "wx/caret.h" @@ -1501,6 +1502,11 @@ void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd) #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 +class wxCocoaGesturesImpl; +WX_DECLARE_HASH_MAP(wxWidgetCocoaImpl*, wxCocoaGesturesImpl*, + wxPointerHash, wxPointerEqual, + wxCocoaGesturesImplMap); + // Class containing data used for gestures support. class wxCocoaGesturesImpl { @@ -1596,11 +1602,58 @@ public: [m_initialTouch release]; } + // We keep all existing wxCocoaGesturesImpl objects in a + // wxWidgetCocoaImpl-indexed map. We do this instead of just having a data + // member containing wxCocoaGesturesImpl pointer in wxWidgetCocoaImpl + // itself because most windows don't need it and it seems wasteful to + // always increase their size unnecessarily. + + // Store the object to be used for the given window, replacing the existing + // one, if any. + // + // This method takes ownership of wxCocoaGesturesImpl pointer which will be + // destroyed by EraseForWindow(). + static void StoreForWindow(wxWidgetCocoaImpl* impl, wxCocoaGesturesImpl* obj) + { + const wxCocoaGesturesImplMap::iterator it = ms_map.find(impl); + if ( it != ms_map.end() ) + { + delete it->second; + it->second = obj; + } + else + { + ms_map.insert(wxCocoaGesturesImplMap::value_type(impl, obj)); + } + } + + // Find the object for the corresponding window. + static wxCocoaGesturesImpl* FromWindow(wxWidgetCocoaImpl* impl) + { + const wxCocoaGesturesImplMap::const_iterator it = ms_map.find(impl); + return it == ms_map.end() ? NULL : it->second; + } + + // Erase the object used for the corresponding window, return true if there + // was one or false otherwise. + static bool EraseForWindow(wxWidgetCocoaImpl* impl) + { + const wxCocoaGesturesImplMap::iterator it = ms_map.find(impl); + if ( it == ms_map.end() ) + return false; + + delete it->second; + ms_map.erase(it); + return true; + } + void TouchesBegan(NSEvent* event); void TouchesMoved(NSEvent* event); void TouchesEnded(NSEvent* event); private: + static wxCocoaGesturesImplMap ms_map; + wxWindowMac* const m_win; NSView* const m_view; @@ -1620,6 +1673,8 @@ private: wxDECLARE_NO_COPY_CLASS(wxCocoaGesturesImpl); }; +wxCocoaGesturesImplMap wxCocoaGesturesImpl::ms_map; + void wxWidgetCocoaImpl::PanGestureEvent(NSPanGestureRecognizer* panGestureRecognizer) { NSGestureRecognizerState gestureState; @@ -1820,8 +1875,8 @@ enum TrackedGestures void wxWidgetCocoaImpl::TouchesBegan(WX_NSEvent event) { - if ( m_gesturesImpl ) - m_gesturesImpl->TouchesBegan(event); + if ( wxCocoaGesturesImpl* gestures = wxCocoaGesturesImpl::FromWindow(this) ) + gestures->TouchesBegan(event); } void wxCocoaGesturesImpl::TouchesBegan(NSEvent* event) @@ -1877,8 +1932,8 @@ void wxCocoaGesturesImpl::TouchesBegan(NSEvent* event) void wxWidgetCocoaImpl::TouchesMoved(WX_NSEvent event) { - if ( m_gesturesImpl ) - m_gesturesImpl->TouchesMoved(event); + if ( wxCocoaGesturesImpl* gestures = wxCocoaGesturesImpl::FromWindow(this) ) + gestures->TouchesMoved(event); } void wxCocoaGesturesImpl::TouchesMoved(NSEvent* event) @@ -1933,8 +1988,8 @@ void wxCocoaGesturesImpl::TouchesMoved(NSEvent* event) void wxWidgetCocoaImpl::TouchesEnded(WX_NSEvent event) { - if ( m_gesturesImpl ) - m_gesturesImpl->TouchesEnded(event); + if ( wxCocoaGesturesImpl* gestures = wxCocoaGesturesImpl::FromWindow(this) ) + gestures->TouchesEnded(event); } void wxCocoaGesturesImpl::TouchesEnded(NSEvent* event) @@ -2465,10 +2520,6 @@ void wxWidgetCocoaImpl::Init() #endif m_lastKeyDownEvent = NULL; m_hasEditor = false; - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - m_gesturesImpl = NULL; -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 } wxWidgetCocoaImpl::~wxWidgetCocoaImpl() @@ -2489,7 +2540,7 @@ wxWidgetCocoaImpl::~wxWidgetCocoaImpl() CFRelease(m_osxView); #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - delete m_gesturesImpl; + wxCocoaGesturesImpl::EraseForWindow(this); #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 } @@ -3416,21 +3467,21 @@ bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) { if ( IsUserPane() ) { - // We need to get rid of the old data, if we have it, in any case. - delete m_gesturesImpl; - if ( eventsMask == wxTOUCH_NONE ) { - if ( m_gesturesImpl ) + if ( wxCocoaGesturesImpl::EraseForWindow(this) ) { - m_gesturesImpl = NULL; - [m_osxView setAcceptsTouchEvents:NO]; } + //else: we didn't have any gesture data anyhow } else // We do want to have gesture events. { - m_gesturesImpl = new wxCocoaGesturesImpl(this, m_osxView, eventsMask); + wxCocoaGesturesImpl::StoreForWindow + ( + this, + new wxCocoaGesturesImpl(this, m_osxView, eventsMask) + ); [m_osxView setAcceptsTouchEvents:YES]; } From 6fd435b83f3e037e7ec9239e87a216979b4aafd1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 22 Nov 2017 00:06:37 +0100 Subject: [PATCH 19/26] Extract "external field" approach in a reusable class Add wxExternalField<> template that can be used for other fields and/or in other ports too. --- include/wx/private/extfield.h | 84 +++++++++++++++++++++++++++++++++++ src/osx/cocoa/window.mm | 81 +++++++++------------------------ 2 files changed, 105 insertions(+), 60 deletions(-) create mode 100644 include/wx/private/extfield.h diff --git a/include/wx/private/extfield.h b/include/wx/private/extfield.h new file mode 100644 index 0000000000..f180ab0b47 --- /dev/null +++ b/include/wx/private/extfield.h @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/private/extfield.h +// Purpose: Declare wxExternalField helper +// Author: Vadim Zeitlin +// Created: 2017-11-21 +// Copyright: (c) 2017 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_PRIVATE_EXTFIELD_H_ +#define _WX_PRIVATE_EXTFIELD_H_ + +// ---------------------------------------------------------------------------- +// wxExternalField: store object data outside of it +// ---------------------------------------------------------------------------- + +// This class allows to store some data without consuming space for the objects +// that don't need it and can be useful for avoiding to add rarely used fields +// to the classes that are used by many objects, e.g. wxWindow. +// +// Note that using this class costs in speed and convenience of access to the +// field, which requires a hash lookup instead of accessing it directly. It +// also only currently works for heap-allocated fields as it's probably never +// worth using it for fields of simple types. +// +// Template parameter Object is the class that "contains" the field, Field is +// the type of the field itself and FieldMap is the hash map, defined +// separately using WX_DECLARE_HASH_MAP(), with Object* as the key and Field* +// as the value type. +template +class wxExternalField +{ +public: + typedef Object ObjectType; + typedef Field FieldType; + typedef FieldMap MapType; + + // Store the field object to be used for the given object, replacing the + // existing one, if any. + // + // This method takes ownership of the field pointer which will be destroyed + // by EraseForWindow(). + static void StoreForWindow(ObjectType* obj, FieldType* field) + { + const typename MapType::iterator it = ms_map.find(obj); + if ( it != ms_map.end() ) + { + delete it->second; + it->second = field; + } + else + { + ms_map.insert(typename MapType::value_type(obj, field)); + } + } + + // Find the object for the corresponding window. + static FieldType* FromWindow(ObjectType* obj) + { + const typename MapType::const_iterator it = ms_map.find(obj); + return it == ms_map.end() ? NULL : it->second; + } + + // Erase the object used for the corresponding window, return true if there + // was one or false otherwise. + static bool EraseForWindow(ObjectType* obj) + { + const typename MapType::iterator it = ms_map.find(obj); + if ( it == ms_map.end() ) + return false; + + delete it->second; + ms_map.erase(it); + return true; + } + +private: + static FieldMap ms_map; +}; + +template +M wxExternalField::ms_map; + +#endif // _WX_PRIVATE_EXTFIELD_H_ diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index ae28a12e2e..cc89d8ef92 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -24,7 +24,6 @@ #endif #include "wx/evtloop.h" -#include "wx/hashmap.h" #if wxUSE_CARET #include "wx/caret.h" @@ -1502,11 +1501,6 @@ void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd) #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 -class wxCocoaGesturesImpl; -WX_DECLARE_HASH_MAP(wxWidgetCocoaImpl*, wxCocoaGesturesImpl*, - wxPointerHash, wxPointerEqual, - wxCocoaGesturesImplMap); - // Class containing data used for gestures support. class wxCocoaGesturesImpl { @@ -1602,58 +1596,11 @@ public: [m_initialTouch release]; } - // We keep all existing wxCocoaGesturesImpl objects in a - // wxWidgetCocoaImpl-indexed map. We do this instead of just having a data - // member containing wxCocoaGesturesImpl pointer in wxWidgetCocoaImpl - // itself because most windows don't need it and it seems wasteful to - // always increase their size unnecessarily. - - // Store the object to be used for the given window, replacing the existing - // one, if any. - // - // This method takes ownership of wxCocoaGesturesImpl pointer which will be - // destroyed by EraseForWindow(). - static void StoreForWindow(wxWidgetCocoaImpl* impl, wxCocoaGesturesImpl* obj) - { - const wxCocoaGesturesImplMap::iterator it = ms_map.find(impl); - if ( it != ms_map.end() ) - { - delete it->second; - it->second = obj; - } - else - { - ms_map.insert(wxCocoaGesturesImplMap::value_type(impl, obj)); - } - } - - // Find the object for the corresponding window. - static wxCocoaGesturesImpl* FromWindow(wxWidgetCocoaImpl* impl) - { - const wxCocoaGesturesImplMap::const_iterator it = ms_map.find(impl); - return it == ms_map.end() ? NULL : it->second; - } - - // Erase the object used for the corresponding window, return true if there - // was one or false otherwise. - static bool EraseForWindow(wxWidgetCocoaImpl* impl) - { - const wxCocoaGesturesImplMap::iterator it = ms_map.find(impl); - if ( it == ms_map.end() ) - return false; - - delete it->second; - ms_map.erase(it); - return true; - } - void TouchesBegan(NSEvent* event); void TouchesMoved(NSEvent* event); void TouchesEnded(NSEvent* event); private: - static wxCocoaGesturesImplMap ms_map; - wxWindowMac* const m_win; NSView* const m_view; @@ -1673,7 +1620,21 @@ private: wxDECLARE_NO_COPY_CLASS(wxCocoaGesturesImpl); }; -wxCocoaGesturesImplMap wxCocoaGesturesImpl::ms_map; +// We keep all existing wxCocoaGesturesImpl objects in a +// wxWidgetCocoaImpl-indexed map. We do this instead of just having a data +// member containing wxCocoaGesturesImpl pointer in wxWidgetCocoaImpl +// itself because most windows don't need it and it seems wasteful to +// always increase their size unnecessarily. + +#include "wx/hashmap.h" +WX_DECLARE_HASH_MAP(wxWidgetCocoaImpl*, wxCocoaGesturesImpl*, + wxPointerHash, wxPointerEqual, + wxCocoaGesturesImplMap); + +#include "wx/private/extfield.h" +typedef wxExternalField wxCocoaGestures; void wxWidgetCocoaImpl::PanGestureEvent(NSPanGestureRecognizer* panGestureRecognizer) { @@ -1875,7 +1836,7 @@ enum TrackedGestures void wxWidgetCocoaImpl::TouchesBegan(WX_NSEvent event) { - if ( wxCocoaGesturesImpl* gestures = wxCocoaGesturesImpl::FromWindow(this) ) + if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromWindow(this) ) gestures->TouchesBegan(event); } @@ -1932,7 +1893,7 @@ void wxCocoaGesturesImpl::TouchesBegan(NSEvent* event) void wxWidgetCocoaImpl::TouchesMoved(WX_NSEvent event) { - if ( wxCocoaGesturesImpl* gestures = wxCocoaGesturesImpl::FromWindow(this) ) + if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromWindow(this) ) gestures->TouchesMoved(event); } @@ -1988,7 +1949,7 @@ void wxCocoaGesturesImpl::TouchesMoved(NSEvent* event) void wxWidgetCocoaImpl::TouchesEnded(WX_NSEvent event) { - if ( wxCocoaGesturesImpl* gestures = wxCocoaGesturesImpl::FromWindow(this) ) + if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromWindow(this) ) gestures->TouchesEnded(event); } @@ -2540,7 +2501,7 @@ wxWidgetCocoaImpl::~wxWidgetCocoaImpl() CFRelease(m_osxView); #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - wxCocoaGesturesImpl::EraseForWindow(this); + wxCocoaGestures::EraseForWindow(this); #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 } @@ -3469,7 +3430,7 @@ bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) { if ( eventsMask == wxTOUCH_NONE ) { - if ( wxCocoaGesturesImpl::EraseForWindow(this) ) + if ( wxCocoaGestures::EraseForWindow(this) ) { [m_osxView setAcceptsTouchEvents:NO]; } @@ -3477,7 +3438,7 @@ bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) } else // We do want to have gesture events. { - wxCocoaGesturesImpl::StoreForWindow + wxCocoaGestures::StoreForWindow ( this, new wxCocoaGesturesImpl(this, m_osxView, eventsMask) From 1863f494f326686ffd506e46d46dda020676998e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 22 Nov 2017 00:14:09 +0100 Subject: [PATCH 20/26] Use wxExternalField for gestures data in wxGTK too Don't waste memory on gesture data in all windows. --- include/wx/gtk/window.h | 5 ---- src/gtk/window.cpp | 64 ++++++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/include/wx/gtk/window.h b/include/wx/gtk/window.h index 0e6d2633c3..fd3f683491 100644 --- a/include/wx/gtk/window.h +++ b/include/wx/gtk/window.h @@ -358,11 +358,6 @@ public: wxRegion m_nativeUpdateRegion; // not transformed for RTL -#if defined(__WXGTK3__) - // Data used for gesture support, if available. - class wxWindowGesturesData* m_gesturesData; -#endif // __WXGTK3__ - protected: // implement the base class pure virtuals virtual void DoGetTextExtent(const wxString& string, diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index eceb87ff9b..d8bc4b69f9 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -234,6 +234,12 @@ static bool gs_inSizeAllocate; #ifdef wxGTK_HAS_GESTURES_SUPPORT +#include "wx/hashmap.h" +#include "wx/private/extfield.h" + +namespace +{ + // Per-window data for gestures support. class wxWindowGesturesData { @@ -256,6 +262,16 @@ public: GtkGesture* m_long_press_gesture; }; +WX_DECLARE_HASH_MAP(wxWindow*, wxWindowGesturesData*, + wxPointerHash, wxPointerEqual, + wxWindowGesturesMap); + +typedef wxExternalField wxWindowGestures; + +} // anonymous namespace + // This is true when the gesture has just started (currently used for pan gesture only) static bool gs_gestureStart = false; @@ -2475,10 +2491,6 @@ void wxWindowGTK::Init() m_imKeyEvent = NULL; m_dirtyTabOrder = false; - -#ifdef wxGTK_HAS_GESTURES_SUPPORT - m_gesturesData = NULL; -#endif // wxGTK_HAS_GESTURES_SUPPORT } wxWindowGTK::wxWindowGTK() @@ -2675,7 +2687,7 @@ wxWindowGTK::~wxWindowGTK() #endif #ifdef wxGTK_HAS_GESTURES_SUPPORT - delete m_gesturesData; + wxWindowGestures::EraseForWindow(this); #endif // wxGTK_HAS_GESTURES_SUPPORT if (m_widget) @@ -2911,7 +2923,9 @@ pan_gesture_begin_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUN static void horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindowGTK* win) { - wxWindowGesturesData* const data = win->m_gesturesData; + wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + if ( !data ) + return; // Do not process horizontal pan, if there was no "pan" signal for it. if ( !(data->m_allowedGestures & horizontal_pan) ) @@ -2940,7 +2954,9 @@ horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* seque static void vertical_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindowGTK* win) { - wxWindowGesturesData* const data = win->m_gesturesData; + wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + if ( !data ) + return; // Do not process vertical pan, if there was no "pan" signal for it. if ( !(data->m_allowedGestures & vertical_pan) ) @@ -2990,7 +3006,9 @@ pan_gesture_callback(GtkGesture* gesture, GtkPanDirection direction, gdouble off event.SetEventObject(win); event.SetPosition(wxPoint(wxRound(x), wxRound(y))); - wxWindowGesturesData* const data = win->m_gesturesData; + wxWindowGesturesData* const data = wxWindowGestures::FromWindow(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); @@ -3052,7 +3070,9 @@ zoom_gesture_callback(GtkGesture* gesture, gdouble scale, wxWindowGTK* win) event.SetPosition(wxPoint(wxRound(x), wxRound(y))); event.SetZoomFactor(scale); - wxWindowGesturesData* const data = win->m_gesturesData; + wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + if ( !data ) + return; // Cancel "Two FInger Tap Event" if scale has changed if ( wxRound(scale * 1000) != wxRound(gs_lastScale * 1000) ) @@ -3190,7 +3210,9 @@ wxEmitTwoFingerTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) event.SetEventObject(win); - wxWindowGesturesData* const data = win->m_gesturesData; + wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + if ( !data ) + return; double lastX = data->m_lastTouchPoint.x; double lastY = data->m_lastTouchPoint.y; @@ -3219,7 +3241,9 @@ wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) event.SetEventObject(win); - wxWindowGesturesData* const data = win->m_gesturesData; + wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + if ( !data ) + return; switch ( data->m_gestureState ) { @@ -3249,7 +3273,9 @@ wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) static void touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGTK* win) { - wxWindowGesturesData* const data = win->m_gesturesData; + wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + if ( !data ) + return; switch ( gdk_event->type ) { @@ -3502,17 +3528,15 @@ bool wxWindowGTK::EnableTouchEvents(int eventsMask) { if ( eventsMask == wxTOUCH_NONE ) { - delete m_gesturesData; - m_gesturesData = NULL; + wxWindowGestures::EraseForWindow(this); } else { - m_gesturesData = new wxWindowGesturesData - ( - this, - GetConnectWidget(), - eventsMask - ); + wxWindowGestures::StoreForWindow + ( + this, + new wxWindowGesturesData(this, GetConnectWidget(), eventsMask) + ); } return true; From 020c598c18b17e20a3a23c6db1bce4fa02bd2a49 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 22 Nov 2017 00:16:45 +0100 Subject: [PATCH 21/26] Rename wxExternalField methods to not mention "Window" This class doesn't have to be used with wxWindow or similar as object type, so use a more neutral "Object" instead of "Window". --- include/wx/private/extfield.h | 8 ++++---- src/gtk/window.cpp | 20 ++++++++++---------- src/osx/cocoa/window.mm | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/wx/private/extfield.h b/include/wx/private/extfield.h index f180ab0b47..01451e83eb 100644 --- a/include/wx/private/extfield.h +++ b/include/wx/private/extfield.h @@ -39,8 +39,8 @@ public: // existing one, if any. // // This method takes ownership of the field pointer which will be destroyed - // by EraseForWindow(). - static void StoreForWindow(ObjectType* obj, FieldType* field) + // by EraseForObject(). + static void StoreForObject(ObjectType* obj, FieldType* field) { const typename MapType::iterator it = ms_map.find(obj); if ( it != ms_map.end() ) @@ -55,7 +55,7 @@ public: } // Find the object for the corresponding window. - static FieldType* FromWindow(ObjectType* obj) + static FieldType* FromObject(ObjectType* obj) { const typename MapType::const_iterator it = ms_map.find(obj); return it == ms_map.end() ? NULL : it->second; @@ -63,7 +63,7 @@ public: // Erase the object used for the corresponding window, return true if there // was one or false otherwise. - static bool EraseForWindow(ObjectType* obj) + static bool EraseForObject(ObjectType* obj) { const typename MapType::iterator it = ms_map.find(obj); if ( it == ms_map.end() ) diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index d8bc4b69f9..d18aa15662 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -2687,7 +2687,7 @@ wxWindowGTK::~wxWindowGTK() #endif #ifdef wxGTK_HAS_GESTURES_SUPPORT - wxWindowGestures::EraseForWindow(this); + wxWindowGestures::EraseForObject(this); #endif // wxGTK_HAS_GESTURES_SUPPORT if (m_widget) @@ -2923,7 +2923,7 @@ pan_gesture_begin_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUN static void horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindowGTK* win) { - wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + wxWindowGesturesData* const data = wxWindowGestures::FromObject(win); if ( !data ) return; @@ -2954,7 +2954,7 @@ horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* seque static void vertical_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindowGTK* win) { - wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + wxWindowGesturesData* const data = wxWindowGestures::FromObject(win); if ( !data ) return; @@ -3006,7 +3006,7 @@ pan_gesture_callback(GtkGesture* gesture, GtkPanDirection direction, gdouble off event.SetEventObject(win); event.SetPosition(wxPoint(wxRound(x), wxRound(y))); - wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + wxWindowGesturesData* const data = wxWindowGestures::FromObject(win); if ( !data ) return; @@ -3070,7 +3070,7 @@ zoom_gesture_callback(GtkGesture* gesture, gdouble scale, wxWindowGTK* win) event.SetPosition(wxPoint(wxRound(x), wxRound(y))); event.SetZoomFactor(scale); - wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + wxWindowGesturesData* const data = wxWindowGestures::FromObject(win); if ( !data ) return; @@ -3210,7 +3210,7 @@ wxEmitTwoFingerTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) event.SetEventObject(win); - wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + wxWindowGesturesData* const data = wxWindowGestures::FromObject(win); if ( !data ) return; @@ -3241,7 +3241,7 @@ wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) event.SetEventObject(win); - wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + wxWindowGesturesData* const data = wxWindowGestures::FromObject(win); if ( !data ) return; @@ -3273,7 +3273,7 @@ wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindowGTK* win) static void touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGTK* win) { - wxWindowGesturesData* const data = wxWindowGestures::FromWindow(win); + wxWindowGesturesData* const data = wxWindowGestures::FromObject(win); if ( !data ) return; @@ -3528,11 +3528,11 @@ bool wxWindowGTK::EnableTouchEvents(int eventsMask) { if ( eventsMask == wxTOUCH_NONE ) { - wxWindowGestures::EraseForWindow(this); + wxWindowGestures::EraseForObject(this); } else { - wxWindowGestures::StoreForWindow + wxWindowGestures::StoreForObject ( this, new wxWindowGesturesData(this, GetConnectWidget(), eventsMask) diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index cc89d8ef92..b218573f9e 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -1836,7 +1836,7 @@ enum TrackedGestures void wxWidgetCocoaImpl::TouchesBegan(WX_NSEvent event) { - if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromWindow(this) ) + if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromObject(this) ) gestures->TouchesBegan(event); } @@ -1893,7 +1893,7 @@ void wxCocoaGesturesImpl::TouchesBegan(NSEvent* event) void wxWidgetCocoaImpl::TouchesMoved(WX_NSEvent event) { - if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromWindow(this) ) + if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromObject(this) ) gestures->TouchesMoved(event); } @@ -1949,7 +1949,7 @@ void wxCocoaGesturesImpl::TouchesMoved(NSEvent* event) void wxWidgetCocoaImpl::TouchesEnded(WX_NSEvent event) { - if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromWindow(this) ) + if ( wxCocoaGesturesImpl* gestures = wxCocoaGestures::FromObject(this) ) gestures->TouchesEnded(event); } @@ -2501,7 +2501,7 @@ wxWidgetCocoaImpl::~wxWidgetCocoaImpl() CFRelease(m_osxView); #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - wxCocoaGestures::EraseForWindow(this); + wxCocoaGestures::EraseForObject(this); #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 } @@ -3430,7 +3430,7 @@ bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) { if ( eventsMask == wxTOUCH_NONE ) { - if ( wxCocoaGestures::EraseForWindow(this) ) + if ( wxCocoaGestures::EraseForObject(this) ) { [m_osxView setAcceptsTouchEvents:NO]; } @@ -3438,7 +3438,7 @@ bool wxWidgetCocoaImpl::EnableTouchEvents(int eventsMask) } else // We do want to have gesture events. { - wxCocoaGestures::StoreForWindow + wxCocoaGestures::StoreForObject ( this, new wxCocoaGesturesImpl(this, m_osxView, eventsMask) From 7535854ec40d94723aa5f4b9fb0210a56629a46f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 22 Nov 2017 03:03:48 +0100 Subject: [PATCH 22/26] Embed the image into the event sample Don't load it from the image sample directory, this doesn't necessarily work under Unix if the image sample hadn't been built. Just embed an XPM image directly into the sample, this is good enough for demonstration purposes. --- samples/event/event.cpp | 2 -- samples/event/gestures.cpp | 17 ++++++----------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/samples/event/event.cpp b/samples/event/event.cpp index b397f828aa..7e1af4cf80 100644 --- a/samples/event/event.cpp +++ b/samples/event/event.cpp @@ -298,8 +298,6 @@ bool MyApp::OnInit() if ( !wxApp::OnInit() ) return false; - wxImage::AddHandler(new wxJPEGHandler); - // create the main application window MyFrame *frame = new MyFrame(wxT("Event wxWidgets Sample"), wxPoint(50, 50), wxSize(600, 340)); diff --git a/samples/event/gestures.cpp b/samples/event/gestures.cpp index 06b6cd794a..f28c53dc89 100644 --- a/samples/event/gestures.cpp +++ b/samples/event/gestures.cpp @@ -1,6 +1,8 @@ #include "gestures.h" #include "wx/dcgraph.h" +#include "../image/horse.xpm" + MyGestureFrame::MyGestureFrame() : wxFrame(NULL, wxID_ANY, "Multi-touch Gestures", wxDefaultPosition, wxSize(800, 600)) { @@ -30,18 +32,11 @@ MyGestureFrame::MyGestureFrame() Bind(wxEVT_CLOSE_WINDOW, &MyGestureFrame::OnQuit, this); } -MyGesturePanel::MyGesturePanel(MyGestureFrame *parent) : wxPanel(parent, wxID_ANY) +MyGesturePanel::MyGesturePanel(MyGestureFrame *parent) + : wxPanel(parent, wxID_ANY), + m_bitmap(horse_xpm) { - // Load an image - if ( !m_bitmap.LoadFile("../image/horse.jpg", wxBITMAP_TYPE_JPEG) ) - { - wxLogError("Can't load the image"); - } - - else - { - Bind(wxEVT_PAINT, &MyGesturePanel::OnPaint, this); - } + Bind(wxEVT_PAINT, &MyGesturePanel::OnPaint, this); if ( !EnableTouchEvents(wxTOUCH_ALL_GESTURES) ) { From 5f5c5b8f29c83046544f0b82d249c88b1e801145 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 22 Nov 2017 03:04:44 +0100 Subject: [PATCH 23/26] Remove hard TAB from event sample bakefile No real changes. --- samples/event/event.bkl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/event/event.bkl b/samples/event/event.bkl index 6b830fbc66..ab8ee98c5a 100644 --- a/samples/event/event.bkl +++ b/samples/event/event.bkl @@ -5,7 +5,7 @@ event.cpp gestures.cpp - gestures.h + gestures.h core base From 25da7a58ef72fd521401a04dcc24433bbadf4636 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 23 Nov 2017 13:33:36 +0100 Subject: [PATCH 24/26] Allow safely disabling touch events from a touch event handler Don't delete the window wxWindowGesturesData when calling EnableTouchEvents(wxTOUCH_NONE) but just free it, to make it safe to call EnableTouchEvents() even from a touch event handler, which later returns to wxGTK code that could still use the gestures data object. --- src/gtk/window.cpp | 65 ++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index d18aa15662..ab80ffe4f4 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -244,8 +244,21 @@ namespace class wxWindowGesturesData { public: - wxWindowGesturesData(wxWindow* win, GtkWidget *widget, int eventsMask); - ~wxWindowGesturesData(); + // 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; @@ -3370,9 +3383,9 @@ touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindowGT } } -wxWindowGesturesData::wxWindowGesturesData(wxWindowGTK* win, - GtkWidget *widget, - int eventsMask) +void wxWindowGesturesData::Reinit(wxWindowGTK* win, + GtkWidget *widget, + int eventsMask) { m_touchCount = 0; m_lastTouchTime = 0; @@ -3500,18 +3513,13 @@ wxWindowGesturesData::wxWindowGesturesData(wxWindowGTK* win, G_CALLBACK(touch_callback), win); } -wxWindowGesturesData::~wxWindowGesturesData() +void wxWindowGesturesData::Free() { - 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); + 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); } #endif // wxGTK_HAS_GESTURES_SUPPORT @@ -3526,17 +3534,30 @@ bool wxWindowGTK::EnableTouchEvents(int eventsMask) // 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 ) { - wxWindowGestures::EraseForObject(this); + // 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 { - wxWindowGestures::StoreForObject - ( - this, - new wxWindowGesturesData(this, GetConnectWidget(), eventsMask) - ); + 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; From fc9244614ab875beb1c9864220e3143d58b12788 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 30 Nov 2017 21:36:49 +0100 Subject: [PATCH 25/26] Request GDK_TOUCHPAD_GESTURE_MASK events in wxGTK This is apparently needed in order to get the gesture events. --- src/gtk/window.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index ab80ffe4f4..ae231f7cb0 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -3509,6 +3509,8 @@ void wxWindowGesturesData::Reinit(wxWindowGTK* win, 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); } @@ -3520,6 +3522,11 @@ void wxWindowGesturesData::Free() 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 From 9655ca9e687cae9f5117455cbb75cb9e475d80f1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 30 Nov 2017 21:40:24 +0100 Subject: [PATCH 26/26] Close the gestures frame too in the event sample Make "Exit" menu item really exit the sample again instead of just closing the main window, but possibly leaving the gestures testing window still open and thus not really exiting the program. Also avoid creating more than one gesture window, use the existing one if we had already opened it. --- samples/event/event.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/samples/event/event.cpp b/samples/event/event.cpp index 7e1af4cf80..8cc96efd18 100644 --- a/samples/event/event.cpp +++ b/samples/event/event.cpp @@ -182,7 +182,7 @@ private: // the button used to highlight the event handlers execution order MyEvtTestButton *m_testBtn; - MyGestureFrame *m_gestureFrame; + wxWindowRef m_gestureFrame; // any class wishing to process wxWidgets events must use this macro @@ -457,7 +457,8 @@ MyFrame::~MyFrame() void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) { - // true is to force the frame to close + if ( m_gestureFrame ) + m_gestureFrame->Close(true); Close(true); } @@ -589,8 +590,15 @@ void MyFrame::OnPopEventHandler(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnGesture(wxCommandEvent& WXUNUSED(event)) { - m_gestureFrame = new MyGestureFrame(); - m_gestureFrame->Show(true); + if ( m_gestureFrame ) + { + m_gestureFrame->Raise(); + } + else + { + m_gestureFrame = new MyGestureFrame(); + m_gestureFrame->Show(true); + } } void MyFrame::OnTest(wxCommandEvent& WXUNUSED(event))