Exemple de manipulation et d’inertie
L’exemple de manipulation et d’inertie montre comment ajouter la prise en charge de Windows Touch aux applications natives basées sur Windows qui utilisent l’API Windows Touch. L’exemple implémente les fonctionnalités de base de l’API pour permettre la translation, la rotation et le redimensionnement des objets et leur appliquer des propriétés d’inertie. L’exemple montre également comment donner à vos applications Windows Touch un support de base pour la souris. L’image suivante montre l’apparence de l’exemple lorsqu’il est exécuté.
Les boîtes avec dégradés peuvent être manipulées indépendamment par un utilisateur lorsqu’il exécute l’application depuis un ordinateur prenant en charge Windows Touch.
Enregistrer la fenêtre tactile
Avant de pouvoir recevoir des entrées tactiles, vous devez d’abord notifier le système que votre application est une application Windows Touch en appelant la fonction suivante :
RegisterTouchWindow(g_hWnd, 0);
Implémenter l’interface _IManipulationEventSink
Le récepteur d’événements _IManipulationEvents contient trois fonctions : ManipulationStarted, ManipulationDelta et ManipulationCompleted. Ces fonctions de rappel sont utilisées par l’interface IManipulationProcessor et l’interface IInertiaProcessor pour renvoyer les valeurs calculées par les processeurs après qu’ils aient invoqué les fonctions ProcessTime, ProcessUpWithTime, ProcessDownWithTime et ProcessMoveWithTime. L’exemple de code suivant montre une implémentation d’exemple d’une interface _IManipulationEvents.
#include "cmanipulationeventsink.h"
#include <math.h>
CManipulationEventSink::CManipulationEventSink(HWND hWnd, CDrawingObject *dObj, int iTimerId, BOOL inertia) {
// Manipulation & Inertia Processors
m_manip = NULL;
m_inert = NULL;
// Connection points for COM.
m_pConPointContainer = NULL;
m_pConnPoint = NULL;
// Reference to an object associated with this event sink.
m_dObj = dObj;
// Handle to the window used for computing boundaries.
m_hWnd = hWnd;
// The unique timer id for this manipulation event sink.
m_iTimerId = iTimerId;
m_bInertia = inertia;
m_cRefCount = 1;
}
CManipulationEventSink::~CManipulationEventSink()
{
}
HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationStarted(
FLOAT x,
FLOAT y)
{
KillTimer(m_hWnd, m_iTimerId);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationDelta(
FLOAT x,
FLOAT y,
FLOAT translationDeltaX,
FLOAT translationDeltaY,
FLOAT scaleDelta,
FLOAT expansionDelta,
FLOAT rotationDelta,
FLOAT cumulativeTranslationX,
FLOAT cumulativeTranslationY,
FLOAT cumulativeScale,
FLOAT cumulativeExpansion,
FLOAT cumulativeRotation)
{
FLOAT pivot = 0.0f;
// Apply transformation based on rotationDelta (in radians).
FLOAT rads = 180.0f / 3.14159f;
m_dObj->Rotate(rotationDelta*rads, x, y);
// Apply translation based on scaleDelta.
m_dObj->Scale(scaleDelta);
// Apply translation based on translationDelta.
m_dObj->Translate(translationDeltaX, translationDeltaY);
if(!m_bInertia)
{
// Set values for one-finger rotations.
FLOAT fPivotRadius = (FLOAT)(sqrt(pow(m_dObj->GetWidth()/2, 2)
+ pow(m_dObj->GetHeight()/2, 2)))*0.4f;
FLOAT fPivotPtX = m_dObj->GetCenterX();
FLOAT fPivotPtY = m_dObj->GetCenterY();
m_manip->put_PivotPointX(fPivotPtX);
m_manip->put_PivotPointY(fPivotPtY);
m_manip->put_PivotRadius(fPivotRadius);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationCompleted(
FLOAT x,
FLOAT y,
FLOAT cumulativeTranslationX,
FLOAT cumulativeTranslationY,
FLOAT cumulativeScale,
FLOAT cumulativeExpansion,
FLOAT cumulativeRotation)
{
if(!m_bInertia)
{
SetupInertia();
// Kick off timer that handles inertia.
SetTimer(m_hWnd, m_iTimerId, DESIRED_MILLISECONDS, NULL);
}
else
{
// Stop timer that handles inertia.
KillTimer(m_hWnd, m_iTimerId);
}
return S_OK;
}
Créer des objets COM et configurer les interfaces IManipulationProcessor et IInertiaProcessor
L’API fournit une implémentation des interfaces IManipulationProcessor et IInertiaProcessor. Vous devez créer une instance et référencer les objets COM à partir du récepteur d’événements IManipulationEvents qui a été implémenté précédemment.
Gérer les messages WM_TOUCH
Les données d’entrée doivent être extraites des messages WM_TOUCH puis traitées pour alimenter le bon processeur de manipulation.
switch (msg)
{
case WM_TOUCH:
iNumContacts = LOWORD(wParam);
hInput = (HTOUCHINPUT)lParam;
pInputs = new TOUCHINPUT[iNumContacts];
// Get each touch input info and feed each
// tagTOUCHINPUT into the process input handler.
if(pInputs != NULL)
{
if(GetTouchInputInfo(hInput, iNumContacts,
pInputs, sizeof(TOUCHINPUT)))
{
for(int i = 0; i < iNumContacts; i++)
{
// Bring touch input info into client coordinates.
ptInputs.x = pInputs[i].x/100;
ptInputs.y = pInputs[i].y/100;
ScreenToClient(g_hWnd, &ptInputs);
pInputs[i].x = ptInputs.x;
pInputs[i].y = ptInputs.y;
g_ctDriver->ProcessInputEvent(pInputs[i]);
}
}
}
delete [] pInputs;
break;
}
Remarque
Pour utiliser la fonction ScreenToClient, vous devez avoir un support DPI élevé dans votre application. Pour plus d’informations sur la prise en charge des DPI élevés, veuillez consulter la section DPI élevé.
Passer les structures TOUCHINPUT au processeur approprié
Après que les données soient extraites des messages WM_TOUCH en utilisant la fonction GetTouchInputInfo, alimentez les données dans le processeur de manipulation en invoquant les fonctions ProcessUpWithTime, ProcessDownWithTime ou ProcessMoveWithTime, selon l’indicateur dwFlag défini dans la structure TOUCHINPUT.
Remarque
Lors de la prise en charge de plusieurs manipulations, un nouveau processeur de manipulation doit être créé si le dwID défini dans la structure TOUCHINPUT doit être utilisé pour envoyer les données au bon objet IManipulationProcessor.
CoreObject* coCurrent = m_coHead;
while(coCurrent!=NULL && !bFoundObj)
{
if(dwEvent & TOUCHEVENTF_DOWN)
{
DownEvent(coCurrent, inData, &bFoundObj);
}
else if(dwEvent & TOUCHEVENTF_MOVE)
{
MoveEvent(coCurrent, inData);
}
else if(dwEvent & TOUCHEVENTF_UP)
{
UpEvent(coCurrent, inData);
}
coCurrent = coCurrent->coNext;
}
VOID CComTouchDriver::DownEvent(CoreObject* coRef, tagTOUCHINPUT inData, BOOL* bFound) {
DWORD dwPCursor = inData.dwID;
DWORD dwPTime = inData.dwTime;
int x = inData.x;
int y = inData.y;
// Check that the user has touched within an object's region and fed to the object's manipulation processor.
if(coRef->doDrawing->InRegion(x, y) &&
!HasCursor(coRef, dwPCursor))
{
...
// Feed values to the Manipulation Processor.
coRef->manipulationProc->ProcessDownWithTime(dwPCursor, (FLOAT)x, (FLOAT)y, dwPTime);
...
}
}
Configurer l’inertie dans ManipulationCompleted
Après que la méthode ManipulationCompleted soit invoquée, l’objet IManipulationProcessor doit définir les valeurs pour l’objet IInertiaProcessor lié au IManipulationProcessor pour invoquer l’inertie. L’exemple de code suivant montre comment configurer l’objet IInertiaProcessor à partir de la méthode IManipulationProcessor ManipulationCompleted.
int iVWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int iVHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
RECT rc;
GetClientRect(m_hWnd, &rc);
FLOAT lCWidth = (FLOAT)rc.right;
FLOAT lCHeight = (FLOAT)rc.bottom;
// Set properties for inertia events.
// Deceleration for tranlations in pixel / msec^2.
m_inert->put_DesiredDeceleration(0.001f);
// Deceleration for rotations in radians / msec^2.
m_inert->put_DesiredAngularDeceleration(0.00001f);
// Calculate borders and elastic margin to be set.
// They are relative to the width and height of the object.
FLOAT fHOffset = m_dObj->GetWidth() * 0.5f;
FLOAT fVOffset = m_dObj->GetHeight() * 0.5f;
// Elastic margin is in pixels - note that it offsets the boundary.
FLOAT fHElasticMargin = 25.0f;
FLOAT fVElasticMargin = 25.0f;
FLOAT fBoundaryLeft = fHOffset + fHElasticMargin;
FLOAT fBoundaryTop = fVOffset + fVElasticMargin;
FLOAT fBoundaryRight = lCWidth - fHOffset - fHElasticMargin;
FLOAT fBoundaryBottom = lCHeight - fVOffset - fVElasticMargin;
// Set borders and elastic margin.
m_inert->put_BoundaryLeft(fBoundaryLeft);
m_inert->put_BoundaryTop(fBoundaryTop);
m_inert->put_BoundaryRight(fBoundaryRight);
m_inert->put_BoundaryBottom(fBoundaryBottom);
m_inert->put_ElasticMarginLeft(fHElasticMargin);
m_inert->put_ElasticMarginTop(fVElasticMargin);
m_inert->put_ElasticMarginRight(fHElasticMargin);
m_inert->put_ElasticMarginBottom(fVElasticMargin);
// Set initial origins.
m_inert->put_InitialOriginX(m_dObj->GetCenterX());
m_inert->put_InitialOriginY(m_dObj->GetCenterY());
FLOAT fVX;
FLOAT fVY;
FLOAT fVR;
m_manip->GetVelocityX(&fVX);
m_manip->GetVelocityY(&fVY);
m_manip->GetAngularVelocity(&fVR);
// Set initial velocities for inertia processor.
m_inert->put_InitialVelocityX(fVX);
m_inert->put_InitialVelocityY(fVY);
m_inert->put_InitialAngularVelocity(fVR);
Nettoyer vos objets COM
Lorsque votre application se ferme, vous devez nettoyer vos objets COM. Le code suivant montre comment vous pouvez libérer les ressources allouées dans l’exemple.
CComTouchDriver::~CComTouchDriver(VOID) {
CoreObject* coCurrent = m_coHead;
// Clean up COM objects.
while(coCurrent!=NULL)
{
coCurrent->inertiaEventSink->Release();
coCurrent->manipulationEventSink->Release();
coCurrent->inertiaProc->Release();
coCurrent->manipulationProc->Release();
coCurrent = coCurrent->coNext;
}
}
Rubriques connexes
Application de manipulation multi-touch, Exemple de manipulation et d’inertie, Exemples de Windows Touch