Merge branch 'gesture-events'

Integrate GSoC 2017 work by Prashant Kumar implementing support for
gesture events.

Closes https://github.com/wxWidgets/wxWidgets/pull/551

Closes https://github.com/wxWidgets/wxWidgets/pull/565
This commit is contained in:
Vadim Zeitlin
2017-12-02 18:46:53 +01:00
28 changed files with 2894 additions and 17 deletions

View File

@@ -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__)
@@ -205,6 +206,82 @@ bool gs_insideCaptureChanged = false;
} // anonymous namespace
#ifdef WM_GESTURE
namespace
{
// Class used to dynamically load gestures related API functions.
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;
}
typedef BOOL (WINAPI *GetGestureInfo_t)(HGESTUREINFO, PGESTUREINFO);
static GetGestureInfo_t GetGestureInfo()
{
return ms_pfnGetGestureInfo;
}
typedef BOOL (WINAPI *CloseGestureInfoHandle_t)(HGESTUREINFO);
static CloseGestureInfoHandle_t CloseGestureInfoHandle()
{
return ms_pfnCloseGestureInfoHandle;
}
typedef BOOL
(WINAPI *SetGestureConfig_t)(HWND, DWORD, UINT, PGESTURECONFIG, UINT);
static SetGestureConfig_t SetGestureConfig()
{
return ms_pfnSetGestureConfig;
}
private:
static void LoadGestureSymbols()
{
wxLoadedDLL dll(wxS("user32.dll"));
wxDL_INIT_FUNC(ms_pfn, GetGestureInfo, dll);
wxDL_INIT_FUNC(ms_pfn, CloseGestureInfoHandle, dll);
wxDL_INIT_FUNC(ms_pfn, SetGestureConfig, dll);
}
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;
} // anonymous namespace
#endif // WM_GESTURE
// ---------------------------------------------------------------------------
// private functions
// ---------------------------------------------------------------------------
@@ -830,6 +907,123 @@ void wxWindowMSW::WarpPointer(int x, int y)
}
}
bool wxWindowMSW::EnableTouchEvents(int eventsMask)
{
#ifdef WM_GESTURE
if ( GestureFuncs::IsOk() )
{
// 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<GESTURECONFIG> 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.
numConfigs, // Number of gesture configurations.
ptrConfigs, // 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
@@ -3124,6 +3318,84 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
}
break;
#ifdef WM_GESTURE
case WM_GESTURE:
{
if ( !GestureFuncs::IsOk() )
break;
HGESTUREINFO hGestureInfo = reinterpret_cast<HGESTUREINFO>(lParam);
WinStruct<GESTUREINFO> gestureInfo;
if ( !GestureFuncs::GetGestureInfo()(hGestureInfo, &gestureInfo) )
{
wxLogLastError("GetGestureInfo");
break;
}
if ( gestureInfo.hwndTarget != GetHWND() )
{
wxLogDebug("This is Not the window targeted by this gesture!");
}
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:
// Point contains the current position of the pan.
processed = HandlePanGesture(pt, gestureInfo.dwFlags);
break;
case GID_ZOOM:
// Point is the mid-point of 2 fingers and ullArgument
// contains the distance between the fingers in its lower
// half
processed = HandleZoomGesture
(
pt,
static_cast<DWORD>(gestureInfo.ullArguments),
gestureInfo.dwFlags
);
break;
case GID_ROTATE:
// Point is the center point of rotation and ullArguments
// contains the angle of rotation
processed = HandleRotateGesture
(
pt,
static_cast<DWORD>(gestureInfo.ullArguments),
gestureInfo.dwFlags
);
break;
case GID_TWOFINGERTAP:
processed = HandleTwoFingerTap(pt, gestureInfo.dwFlags);
break;
case GID_PRESSANDTAP:
processed = HandlePressAndTap(pt, gestureInfo.dwFlags);
break;
}
if ( processed )
{
// If processed, we must call this to avoid memory leaks
if ( !GestureFuncs::CloseGestureInfoHandle()(hGestureInfo) )
{
wxLogLastError("CloseGestureInfoHandle");
}
}
}
break;
#endif // WM_GESTURE
// CTLCOLOR messages are sent by children to query the parent for their
// colors
case WM_CTLCOLORMSGBOX:
@@ -5474,6 +5746,144 @@ void wxWindowMSW::GenerateMouseLeave()
(void)HandleWindowEvent(event);
}
#ifdef WM_GESTURE
// ---------------------------------------------------------------------------
// Gesture events
// ---------------------------------------------------------------------------
bool wxWindowMSW::InitGestureEvent(wxGestureEvent& event,
const wxPoint& pt,
WXDWORD flags)
{
event.SetEventObject(this);
event.SetTimestamp(::GetMessageTime());
event.SetPosition(pt);
if ( flags & GF_BEGIN )
event.SetGestureStart();
if ( flags & GF_END )
event.SetGestureEnd();
return (flags & GF_BEGIN) != 0;
}
bool wxWindowMSW::HandlePanGesture(const wxPoint& pt, WXDWORD flags)
{
// wxEVT_GESTURE_PAN
wxPanGestureEvent event(GetId());
// 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, pt, flags) )
{
s_previousLocation = pt;
}
// Determine the horizontal and vertical changes
event.SetDelta(pt - s_previousLocation);
// Update the last gesture event point
s_previousLocation = pt;
return HandleWindowEvent(event);
}
bool wxWindowMSW::HandleZoomGesture(const wxPoint& pt,
WXDWORD fingerDistance,
WXDWORD flags)
{
// wxEVT_GESTURE_ZOOM
wxZoomGestureEvent event(GetId());
// These are used to calculate the center of the zoom and zoom factor
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, pt, flags) )
{
s_previousLocation = pt;
s_intialFingerDistance = fingerDistance;
}
// 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.
const wxPoint ptCenter = (s_previousLocation + pt)/2;
const double zoomFactor = (double) fingerDistance / s_intialFingerDistance;
event.SetZoomFactor(zoomFactor);
event.SetPosition(ptCenter);
// Update gesture event point
s_previousLocation = pt;
return HandleWindowEvent(event);
}
bool wxWindowMSW::HandleRotateGesture(const wxPoint& pt,
WXDWORD angleArgument,
WXDWORD flags)
{
// wxEVT_GESTURE_ROTATE
wxRotateGestureEvent event(GetId());
if ( InitGestureEvent(event, pt, 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
// 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);
}
return HandleWindowEvent(event);
}
bool wxWindowMSW::HandleTwoFingerTap(const wxPoint& pt, WXDWORD flags)
{
// wxEVT_TWO_FINGER_TAP
wxTwoFingerTapEvent event(GetId());
InitGestureEvent(event, pt, flags);
return HandleWindowEvent(event);
}
bool wxWindowMSW::HandlePressAndTap(const wxPoint& pt, WXDWORD flags)
{
wxPressAndTapEvent event(GetId());
InitGestureEvent(event, pt, flags);
return HandleWindowEvent(event);
}
#endif // WM_GESTURE
// ---------------------------------------------------------------------------
// keyboard handling
// ---------------------------------------------------------------------------