操作と慢性のサンプル
操作と慣性のサンプルでは、Windows Touch API を使用する Windows ベースのネイティブ アプリケーションに Windows タッチ サポートを追加する方法を示します。 このサンプルでは、API の基本機能を実装して、オブジェクトの移動、回転、スケーリングを可能にし、オブジェクトに慣性プロパティを適用します。 このサンプルでは、Windows タッチ アプリケーションに基本的なマウス サポートを提供する方法も示します。 次の図は、サンプル実行時のサンプルの外観を示しています。
Windows タッチをサポートするコンピューターからアプリケーションを実行する場合、ユーザーはグラデーション付きのボックスを個別に操作できます。
タッチ ウィンドウの登録
タッチ入力を受け取る前に、まずは次の関数を呼び出して、アプリケーションが Windows タッチ アプリケーションであることをシステムに通知する必要があります。
RegisterTouchWindow(g_hWnd, 0);
_IManipulationEventSink インターフェイスの実装
_IManipulationEvents イベント シンクには、ManipulationStarted、ManipulationDelta、および ManipulationCompleted の 3 つの関数が含まれています。 これらのコールバック関数は、ProcessTime、ProcessUpWithTime、ProcessDownWithTime、および ProcessMoveWithTime の各関数を呼び出した後にプロセッサによって計算される値を返すために、IManipulationProcessor インターフェイスと IInertiaProcessor インターフェイスで使用されます。 次のコード例は、_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;
}
COM オブジェクトの作成、および IManipulationProcessor インターフェイスと IInertiaProcessor インターフェイスの設定
この API は、IManipulationProcessor インターフェイスと IInertiaProcessor インターフェイスを実装します。 先ほど実装した IManipulationEvents イベント シンクから COM オブジェクトのインスタンスを作成して参照する必要があります。
WM_TOUCH メッセージの処理
入力データは WM_TOUCH メッセージから抽出し、後で処理して正しい操作プロセッサをフィードする必要があります。
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;
}
Note
ScreenToClient 関数を使用するには、アプリケーションの高 DPI サポートが必要です。 高 DPI のサポートの詳細については、「高 DPI」を参照してください。
TOUCHINPUT 構造体を適切なプロセッサに渡す
GetTouchInputInfo 関数を使用して WM_TOUCH メッセージからデータを抽出した後、TOUCHINPUT 構造体に設定されている dwFlag に応じて、ProcessUpWithTime 関数、ProcessDownWithTime 関数、または ProcessMoveWithTime 関数を呼び出し、操作プロセッサにデータをフィードします。
Note
複数の操作をサポートする場合は、TOUCHINPUT 構造体で定義されている dwID を使用して、正しい 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);
...
}
}
ManipulationCompleted 内での慣性の設定
ManipulationCompleted メソッドが呼び出された後、IManipulationProcessor オブジェクトでは、IManipulationProcessor にリンクされた IInertiaProcessor オブジェクトの値を設定して慣性を呼び出す必要があります。 次のコード例は、IManipulationProcessor メソッド ManipulationCompleted から IInertiaProcessor オブジェクトを設定する方法を示しています。
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);
COM オブジェクトのクリーンアップ
アプリケーションを終了するときは、COM オブジェクトをクリーンアップする必要があります。 次のコードは、サンプルで割り当てられたリソースを解放する方法を示しています。
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;
}
}