Windows タッチ操作のサンプル (MTManipulation)
このセクションでは、Windows タッチ操作のサンプルについて説明します。
Windows タッチ操作のサンプルでは、 IManipulationProcessor インターフェイスを使用してオブジェクトを変換、回転、スケーリングし、 _IManipulationEvents イベント シンクを実装する方法を示します。 次のスクリーン ショットは、実行中のサンプルの外観を示しています。
このサンプルでは、プログラムで変換、回転、またはスケーリングできる CDrawingObject クラスが作成されます。 IManipulationProcessor インターフェイスがインスタンス化されます。 CDrawingObject クラスとそのコンストラクターの IManipulationProcessor インターフェイスへのポインターを受け取る操作イベント シンクが作成されます。 IManipulationProcessor への接続ポイントは、 IManipulationProcessor によって発生したイベントがイベント シンクによって受信されるように、操作イベント シンクの実装に作成されます。 タッチ データは IManipulationProcessor インターフェイスに送られ、インターフェイスは _IManipulationEvent イベントを発生させます。 CManipulationEventSink クラスのイベント ハンドラーは、CDrawingObject へのポインターでアクセサーを呼び出すことによって、CDrawingObject の向きを更新します。
次のコードは、ウィンドウをタッチ用に設定する方法と 、CDrawingObject と IManipulationProcessor をインスタンス化して CManipulationEventSink コンストラクターに渡す方法を示しています。
CDrawingObject g_cRect; // CDrawingObject class holds information about the rectangle
// and it is responsible for painting the rectangle.
(...)
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
(...)
// Register application window for receiving multi-touch input. Use default settings.
if (!RegisterTouchWindow(hWnd, 0))
{
MessageBox(hWnd, L"Cannot register application window for multi-touch input", L"Error", MB_OK);
return FALSE;
}
ASSERT(IsTouchWindow(hWnd, NULL));
// Instantiate the ManipulationProcessor object
HRESULT hr = CoCreateInstance(__uuidof(ManipulationProcessor), NULL, CLSCTX_ALL, IID_PPV_ARGS(&g_pIManipProc));
if (FAILED(hr))
{
ASSERT(SUCCEEDED(hr) && L"InitInstance: failed to instantiate the ManipulationProcessor object");
return FALSE;
}
// Instantiate the event sink with the manipulation processor and pointer to the rectangle object
g_pManipulationEventSink = new CManipulationEventSink(&g_cRect);
if (g_pManipulationEventSink == NULL)
{
ASSERT(g_pManipulationEventSink && L"InitInstance: failed to instantiate the CManipulationEventSink class");
g_pIManipProc->Release();
g_pIManipProc = NULL;
return FALSE;
}
// Establish the link between ManipulationEventSink and ManipulationProcessor
if (!g_pManipulationEventSink->Connect(g_pIManipProc))
{
ASSERT(FALSE && L"InitInstance: failed to connect ManipulationEventSink and ManipulationProcessor");
g_pIManipProc->Release();
g_pIManipProc = NULL;
g_pManipulationEventSink->Release();
g_pManipulationEventSink = NULL;
return FALSE;
}
次のコードは、操作イベント シンク CManipulationEventSink のコンストラクターを示しています。
CManipulationEventSink::CManipulationEventSink(CDrawingObject* pcDrawingObject)
: m_cRefCount(1),
m_pConnection(NULL),
m_dwCookie(0),
m_pcDrawingObject(pcDrawingObject)
{
ASSERT((pcDrawingObject != NULL) && L"CManipulationEventSink constructor: incorrect argument");
}
次のコードは、イベント シンクが操作プロセッサに接続される方法を示しています。
bool CManipulationEventSink::Connect(IManipulationProcessor* pManipulationProcessor)
{
// Check input arguments
if (pManipulationProcessor == NULL)
{
ASSERT((pManipulationProcessor != NULL) && L"CManipulationEventSink::Create : incorrect arguments");
return false;
}
// Check object state
if ((m_dwCookie != 0) || (m_pConnection != NULL))
{
ASSERT((m_dwCookie == 0) && (m_pConnection == NULL) && L"CManipulationEventSink::Connect : connection already established");
return false;
}
// Get the container with the connection points.
IConnectionPointContainer* pConnectionContainer = NULL;
HRESULT hr = pManipulationProcessor->QueryInterface(&pConnectionContainer);
if (FAILED(hr))
{
ASSERT(SUCCEEDED(hr) && L"CManipulationEventSink::Connect : failed to get the container with the connection points");
return false;
}
// Get a connection point.
hr = pConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnection);
if (FAILED(hr))
{
ASSERT(SUCCEEDED(hr) && L"CManipulationEventSink::Connect : failed to get a connection point");
pConnectionContainer->Release();
return false;
}
// Release the connection container.
pConnectionContainer->Release();
// Advise. Establishes an advisory connection between the connection point and the
// caller's sink object.
hr = m_pConnection->Advise(this, &m_dwCookie);
if (FAILED(hr))
{
ASSERT(SUCCEEDED(hr) && L"CManipulationEventSink::Connect : failed to Advise");
m_pConnection->Release();
m_pConnection = NULL;
return false;
}
return true;
}
次のコードは、タッチ データを操作イベント シンクに渡す方法を示しています。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
(...)
switch (message)
{
(...)
// WM_TOUCH message handlers
case WM_TOUCH:
{
// WM_TOUCH message can contain several messages from different contacts
// packed together.
// Message parameters need to be decoded:
UINT cInputs = (int) wParam; // Number of actual per-contact messages
TOUCHINPUT* pInputs = new TOUCHINPUT[cInputs]; // Allocate the storage for the parameters of the per-contact messages
if (pInputs == NULL)
{
break;
}
// Unpack message parameters into the array of TOUCHINPUT structures, each
// representing a message for one single contact.
if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT)))
{
// For each contact, dispatch the message to the appropriate message
// handler.
for (unsigned int i = 0; i < cInputs; i++)
{
if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN)
{
g_pIManipProc->ProcessDown(pInputs[i].dwID, (FLOAT)pInputs[i].x, (FLOAT)pInputs[i].y);
}
else if (pInputs[i].dwFlags & TOUCHEVENTF_MOVE)
{
g_pIManipProc->ProcessMove(pInputs[i].dwID, (FLOAT)pInputs[i].x, (FLOAT)pInputs[i].y);
}
else if (pInputs[i].dwFlags & TOUCHEVENTF_UP)
{
g_pIManipProc->ProcessUp(pInputs[i].dwID, (FLOAT)pInputs[i].x, (FLOAT)pInputs[i].y);
}
}
}
else
{
// error handling, presumably out of memory
ASSERT(FALSE && L"Error: failed to execute GetTouchInputInfo");
delete [] pInputs;
break;
}
if (!CloseTouchInputHandle((HTOUCHINPUT)lParam))
{
// error handling, presumably out of memory
ASSERT(FALSE && L"Error: failed to execute CloseTouchInputHandle");
delete [] pInputs;
break;
}
delete [] pInputs;
// Force redraw of the rectangle
InvalidateRect(hWnd, NULL, TRUE);
}
break;
次のコードは、イベント ハンドラーが操作デルタ イベントのオブジェクトの向きとサイズを更新する方法を示しています。
HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationDelta(
/* [in] */ FLOAT /* x */,
/* [in] */ FLOAT /* y */,
/* [in] */ FLOAT translationDeltaX,
/* [in] */ FLOAT translationDeltaY,
/* [in] */ FLOAT scaleDelta,
/* [in] */ FLOAT /* expansionDelta */,
/* [in] */ FLOAT rotationDelta,
/* [in] */ FLOAT /* cumulativeTranslationX */,
/* [in] */ FLOAT /* cumulativeTranslationY */,
/* [in] */ FLOAT /* cumulativeScale */,
/* [in] */ FLOAT /* cumulativeExpansion */,
/* [in] */ FLOAT /* cumulativeRotation */)
{
m_pcDrawingObject->ApplyManipulationDelta(translationDeltaX,translationDeltaY,scaleDelta,rotationDelta);
return S_OK;
}
次のコードは、CDrawingObject クラスでの ApplyManipulationDelta の実装です。
// This function is responsible for manipulation of the rectangle.
// It is called from CManipulationEventSink class.
// in:
// translationDeltaX - shift of the x-coordinate (1/100 of pixel units)
// translationDeltaY - shift of the y-coordinate (1/100 of pixel units)
// scaleDelta - scale factor (zoom in/out)
// rotationDelta - rotation angle in radians
void CDrawingObject::ApplyManipulationDelta(
const FLOAT translationDeltaX,
const FLOAT translationDeltaY,
const FLOAT scaleDelta,
const FLOAT rotationDelta
)
{
_ptCenter.x += (LONG) (translationDeltaX / 100.0);
_ptCenter.y += (LONG) (translationDeltaY / 100.0);
_dScalingFactor *= scaleDelta;
_dRotationAngle -= rotationDelta; // we are subtracting because Y-axis is down
}
CDrawingObject の中心点、スケール ファクター、回転角度が更新されると、オブジェクト自体が変換されて描画されます。