Delphi 2010 introduced Windows 7
touch and gesture (T/G) processing at VCL level.
For those that still use Delphi 7,
this text illustrates how to handle T/G messages
in a graphic component. The sample code contains 3
files: a container Form, a graphic component and a
small T/G API header translation.
The sample component is not very
useful. It allows you to manipulate a bunch of
rectangles with two finger gestures. You can move,
resize and deform the selected rectangle (the one
with fat lines). In order to receive WM_ messages,
your component must be a window (i.e. must descend
from TWinControl or TCustomControl).
The graphic component is created
at runtime, so you don't have to install it in the
D7 component palette. The idea is to illustrate
how to handle T/G messages in your component. Of
course you need Windows 7/8 and
a multi-touch enabled hardware (a Windows tablet
or desktop). If the hardware and/or OS does not
provide multi-touch service, the sample program
still runs, but you can't edit the rectangles :(
A checkbox click changes the
component state from gesture to multi-touch. In
this mode, you can draw several curves
simultaneously, by dragging multiple fingers on
the screen.
The gesture API is located at user32.dll
in Windows 7 and 8. In previous versions of the
OS, these functions are not present. For the
program to run on Vista and before, the gesture
API function addresses must be loaded at runtime
(dynamically). If you declare the functions
statically, the program will not start on Vista
and XP. Not acceptable.
Source code
WM_GESTURE - This message passes a
gesture info record. It can be retrieved by a call
to GetGestureInfo(Msg.lParam , gi) , like shown
below.
procedure TMyRectangleDesigner.WMGesture(var Msg: TMessage); var gi:TGestureInfo; bResult,bHandled:BOOL; dwError:DWORD; L:Integer; ptZoomCenterX,ptZoomCenterY,K,angle:Single; _ptSecond:TPoint; begin bHandled:= False; if bTouchGestureAPIPresent then begin L:=SizeOf(gi); FillChar(gi,L,#0); gi.cbSize := L; bResult := GetGestureInfoFn(Msg.lParam , gi); // retrieve gesture info with lParam
if bResult then begin bHandled:=True; case gi.dwID of GID_BEGIN: ; GID_END: ; GID_PAN: begin if gi.dwFlags=GF_Begin then begin f_ptFirst.x := gi.ptsLocation.x; f_ptFirst.y := gi.ptsLocation.y; f_ptFirst := ScreenToClient(f_ptFirst); end else begin // read the second point (middle between fingers in the position) _ptSecond.x := gi.ptsLocation.x; _ptSecond.y := gi.ptsLocation.y; _ptSecond := ScreenToClient(_ptSecond); ProcessGestureMove(_ptSecond.x-f_ptFirst.x,_ptSecond.y-f_ptFirst.y); f_ptFirst := _ptSecond; //save end; end; GID_ZOOM: begin if gi.dwFlags=GF_Begin then begin f_ptFirst.x := gi.ptsLocation.x; f_ptFirst.y := gi.ptsLocation.y; f_ptFirst := ScreenToClient(f_ptFirst); f_dwArguments := gi.ullArguments; end else begin _ptSecond.x := gi.ptsLocation.x; _ptSecond.y := gi.ptsLocation.y; _ptSecond := ScreenToClient(_ptSecond); ptZoomCenterX := (f_ptFirst.x + _ptSecond.x)/2; ptZoomCenterY := (f_ptFirst.y + _ptSecond.y)/2; k := gi.ullArguments/f_dwArguments; ProcessGestureZoom(k,ptZoomCenterX,ptZoomCenterY); f_ptFirst := _ptSecond; f_dwArguments := gi.ullArguments; end; end; GID_ROTATE: begin if gi.dwFlags=GF_BEGIN then f_dwArguments := 0 else begin f_ptFirst.x := gi.ptsLocation.x; f_ptFirst.y := gi.ptsLocation.y; f_ptFirst := ScreenToClient(f_ptFirst); angle := GID_ROTATE_ANGLE_FROM_ARGUMENT(LODWORD(gi.ullArguments)) - GID_ROTATE_ANGLE_FROM_ARGUMENT(f_dwArguments); ProcessGestureRotate( angle,f_ptFirst.x,f_ptFirst.y); //angle in radians f_dwArguments := LODWORD(gi.ullArguments); //save previous end; end; GID_TWOFINGERTAP: ; // 2 finger tap GID_PRESSANDTAP: ; // press n tap else bHandled:= False; end; end else begin //GetGestureInfo failed dwError := GetLastError; // Handle dwError ?? end;
CloseGestureInfoHandleFn(Msg.lParam); end; if bHandled then Msg.Result := 0 else Msg.Result := DefWindowProc(Handle, Msg.Msg, Msg.WParam, Msg.LParam); end;
WM_TOUCH handling (multi-touch)
has to be enabled by a call to RegisterTouchWindow
() - The component then receives WM_TOUCH messages
containing a collection of touch points. The
sample component simply draws a circle in each
point (not persistent). To return to gesture mode,
call UnRegisterTouchWindow()
procedure TMyRectangleDesigner.WMTouch(var Msg: TMessage);
function TouchPointToPoint(const TouchPoint: TTouchInput): TPoint; begin Result := Point(TouchPoint.X div 100, TouchPoint.Y div 100); PhysicalToLogicalPointFn(Handle, Result); Result := ScreenToClient(Result); end ;
var TouchInputs: array of TTouchInput; counter: Integer; Handled: Boolean; Point: TPoint; R:TRect; begin Handled := False; if not bTouchGestureAPIPresent then Exit;
SetLength(TouchInputs, Msg.WParam); GetTouchInputInfoFn(Msg.LParam, Msg.WParam, @TouchInputs[0], SizeOf(TTouchInput));
try for counter := 1 to Length (TouchInputs) do begin Point := TouchPointToPoint(TouchInputs [counter-1]); R := Rect(Point.X-5,Point.Y-5,Point.X+5,Point.Y+5); Canvas.Brush.Style := bsClear; Canvas.Pen.Style := psSolid; Canvas.Pen.Width := 1; Canvas.Pen.Color := clBlack; Canvas.Ellipse(R); end; Handled := True; finally if Handled then CloseTouchInputHandleFn(Msg.LParam) else inherited ; end; end;
Download
Download source
code: >
D7TouchSample.zip (md5 checksum
1D9EC7997613416236EE6BA24C829446)
External links
> LES OUTILS DU DÉVELOPPEUR
>
Windows 7 Multitouch
>
Windows touch overview
>
Building Windows - Touch hardware for Windows 8
Tags
Delphi 7 , multi-touch , gestures , WM_GESTURE ,
WM_TOUCH , GID_ZOOM , GID_PAN , GID_ROTATE
by omar reis - fev/2013
|