Обработка инерции в неуправляемом коде
В этом разделе объясняется, как использовать интерфейс IInertiaProcessor для обработки инерции в неуправляемом коде.
Общие сведения
Чтобы использовать инерцию в неуправляемом коде, необходимо реализовать приемники событий как для обработчика манипуляций, так и для обработчика инерции. Начните с добавления поддержки манипуляций в приложение, как описано в разделе Добавление поддержки манипуляций в неуправляемый код. Обратите внимание, что для поддержки манипуляции требуется использовать сенсорные сообщения, а не жесты для передачи данных о событиях обработчику манипуляций. После выполнения манипуляции необходимо также реализовать второй приемник событий для событий, создаваемых интерфейсом IInertiaProcessor , или потребуется изменить существующий приемник событий, чтобы вместить как события , созданные интерфейсами IInertiaProcessor , так и IManipulationProcessor . В этом примере проще начать с приемника событий, созданного для раздела Добавление поддержки манипуляций в неуправляемый код, и добавить второй конструктор, который работает с обработчиком инерции, а не с обработчиком манипуляций. Таким образом, реализация приемника событий может функционировать как для обработчика манипуляций, так и для обработчика инерции. Помимо добавления второго конструктора, приемник событий будет иметь переменную, указывающую, будет ли он выполнять операции на основе входных данных инерции, а не на вводе манипуляции.
Добавление поддержки инерции в приемник событий обработчика манипуляций
В следующем коде показан новый конструктор приемника событий, новые переменные-члены для интерфейса IInertiaProcessor и флаг, указывающий, экстраполирует ли приемник инерцию.
CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd);
CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd);
IInertiaProcessor* m_pInert;
BOOL fExtrapolating;
После того как заголовок класса содержит новые конструкторы и флаг, указывающий, выполняется ли экстраполяция, вы можете реализовать приемник событий, чтобы иметь отдельные блоки обработки для событий IManipulationProcessor и IInertiaProcessor . Конструктор, который принимает IManipulationProcessor и IInertiaProcessor , должен установить для флага fExtrapolating значение false, указывающее, что это обработчик событий IManipulationProcessor . В следующем коде показано, как можно реализовать конструктор для приемника событий, использующего IManipulationProcessor .
CManipulationEventSink::CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd)
{
m_hWnd = hWnd;
//Set initial ref count to 1.
m_cRefCount = 1;
fExtrapolating=FALSE;
m_pManip = pManip;
m_pInert = pInert;
m_pManip->put_PivotRadius(-1);
m_cStartedEventCount = 0;
m_cDeltaEventCount = 0;
m_cCompletedEventCount = 0;
HRESULT hr = S_OK;
//Get the container with the connection points.
IConnectionPointContainer* spConnectionContainer;
hr = pManip->QueryInterface(
IID_IConnectionPointContainer,
(LPVOID*) &spConnectionContainer
);
//hr = manip->QueryInterface(&spConnectionContainer);
if (spConnectionContainer == NULL){
// something went wrong, try to gracefully quit
}
//Get a connection point.
hr = spConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnPoint);
if (m_pConnPoint == NULL){
// something went wrong, try to gracefully quit
}
DWORD dwCookie;
//Advise.
hr = m_pConnPoint->Advise(this, &dwCookie);
}
В следующем коде показано, как можно реализовать конструктор для приемника событий, использующего IInertiaProcessor . Этот конструктор устанавливает для флага fExtrapolating значение true, указывая, что этот экземпляр класса приемника событий будет выполнять экстраполяции и выполнять все операции перемещения, которые ранее выполнялись событиями обработчика манипуляций.
CManipulationEventSink::CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd)
{
m_hWnd = hWnd;
m_pInert = pInert;
//Set initial ref count to 1.
m_cRefCount = 1;
fExtrapolating=TRUE;
m_cStartedEventCount = 0;
m_cDeltaEventCount = 0;
m_cCompletedEventCount = 0;
HRESULT hr = S_OK;
//Get the container with the connection points.
IConnectionPointContainer* spConnectionContainer;
hr = pInert->QueryInterface(
IID_IConnectionPointContainer,
(LPVOID*) &spConnectionContainer
);
//hr = manip->QueryInterface(&spConnectionContainer);
if (spConnectionContainer == NULL){
// something went wrong, try to gracefully quit
}
//Get a connection point.
hr = spConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnPoint);
if (m_pConnPoint == NULL){
// something went wrong, try to gracefully quit
}
DWORD dwCookie;
//Advise.
hr = m_pConnPoint->Advise(this, &dwCookie);
}
Примечание
Реализация класса приемника событий из приемника событий обработчика манипуляций повторно используется в качестве приемника событий для обработчика инерции.
Теперь при построении этого класса CManipulationEventSink его можно создать в качестве приемника событий для обработчика манипуляций или приемника событий для обработчика инерции. При создании в качестве приемника событий обработчика инерции флаг fExtrapolating будет иметь значение true, указывающее, что события манипуляции должны быть экстраполированы.
Примечание
ManipulationStarted будет вызываться интерфейсами IManipulationProcessor и IInertiaProcessor .
При запуске манипуляции задаются свойства интерфейса IInertiaProcessor . В следующем коде показано, как обрабатывается событие started.
HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationStarted(
/* [in] */ FLOAT x,
/* [in] */ FLOAT y)
{
m_cStartedEventCount ++;
// set origins in manipulation processor
m_pInert->put_InitialOriginX(x);
m_pInert->put_InitialOriginY(y);
RECT screenRect;
HWND desktop = GetDesktopWindow();
GetClientRect(desktop, &screenRect);
// physics settings
// deceleration is units per square millisecond
m_pInert->put_DesiredDeceleration(.1f);
// set the boundaries
screenRect.left-= 1024;
m_pInert->put_BoundaryLeft ( static_cast<float>(screenRect.left * 100));
m_pInert->put_BoundaryTop ( static_cast<float>(screenRect.top * 100));
m_pInert->put_BoundaryRight ( static_cast<float>(screenRect.right * 100));
m_pInert->put_BoundaryBottom( static_cast<float>(screenRect.bottom * 100));
// Elastic boundaries - I set these to 90% of the screen
// so... 5% at left, 95% right, 5% top, 95% bottom
// Values are whole numbers because units are in centipixels
m_pInert->put_ElasticMarginLeft (static_cast<float>(screenRect.left * 5));
m_pInert->put_ElasticMarginTop (static_cast<float>(screenRect.top * 5));
m_pInert->put_ElasticMarginRight (static_cast<float>(screenRect.right * 95));
m_pInert->put_ElasticMarginBottom(static_cast<float>(screenRect.bottom * 95));
return S_OK;
}
В этом примере для перемещения окна используются дельты манипуляции. В следующем коде показано, как обрабатывается событие delta.
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_cDeltaEventCount ++;
RECT rect;
GetWindowRect(m_hWnd, &rect);
int oldWidth = rect.right-rect.left;
int oldHeight = rect.bottom-rect.top;
// scale and translate the window size / position
MoveWindow(m_hWnd, // the window to move
static_cast<int>(rect.left + (translationDeltaX / 100.0f)), // the x position
static_cast<int>(rect.top + (translationDeltaY/100.0f)), // the y position
static_cast<int>(oldWidth * scaleDelta), // width
static_cast<int>(oldHeight * scaleDelta), // height
TRUE); // redraw
return S_OK;
}
В этом примере события завершения манипуляции запускают или останавливают таймер, который вызывает Process в интерфейсе IInertiaProcessor . В следующем коде показано, как обрабатывается завершенное событие манипуляции.
HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationCompleted(
/* [in] */ FLOAT x,
/* [in] */ FLOAT y,
/* [in] */ FLOAT cumulativeTranslationX,
/* [in] */ FLOAT cumulativeTranslationY,
/* [in] */ FLOAT cumulativeScale,
/* [in] */ FLOAT cumulativeExpansion,
/* [in] */ FLOAT cumulativeRotation)
{
m_cCompletedEventCount ++;
m_fX = x;
m_fY = y;
// place your code handler here to do any operations based on the manipulation
if (fExtrapolating){
//Inertia Complete, stop the timer used for processing
KillTimer(m_hWnd,0);
}else{
// setup velocities for inertia processor
float vX = 0.0f;
float vY = 0.0f;
float vA = 0.0f;
m_pManip->GetVelocityX(&vX);
m_pManip->GetVelocityY(&vY);
m_pManip->GetAngularVelocity(&vA);
// complete any previous processing
m_pInert->Complete();
// Reset sets the initial timestamp
m_pInert->Reset();
//
m_pInert->put_InitialVelocityX(vX);
m_pInert->put_InitialVelocityY(vY);
m_pInert->put_InitialOriginX(x);
m_pInert->put_InitialOriginY(y);
// Start a timer
SetTimer(m_hWnd,0, 50, 0);
}
return S_OK;
}
В следующем коде показано, как можно интерпретировать WM_TIMER сообщения в WndProc для выполнения вызовов Process в интерфейсе IInertiaProcessor .
case WM_TIMER:
if (g_pIInertProc){
BOOL b;
g_pIInertProc->Process(&b);
}
break;
Совместная инициализация обработчика инерции и обработчика манипуляций и инициализация приемников событий
После изменения приемника событий для поддержки IManipulationProcessor и IInertiaProcessor вы можете инициализировать приемники событий и настроить их для запуска из приложения. В следующем коде показано, как выделяются указатели интерфейса.
//Include windows.h for touch events
#include "windows.h"
// Manipulation implementation file
#include <manipulations_i.c>
// Smart Pointer to a global reference of a manipulation processor, event sink
IManipulationProcessor* g_pIManipProc;
IInertiaProcessor* g_pIInertProc;
В следующем примере кода показано, как создать экземпляры интерфейсов.
HRESULT hr = CoInitialize(0);
hr = CoCreateInstance(CLSID_ManipulationProcessor,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(VOID**)(&g_pIManipProc)
);
hr = CoCreateInstance(CLSID_InertiaProcessor,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(VOID**)(&g_pIInertProc)
);
В следующем примере кода показано, как создать приемники событий с учетом указателей интерфейса и зарегистрировать окно для сенсорного ввода.
g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, g_pIInertProc, hWnd);
g_pManipulationEventSink = new CManipulationEventSink(g_pIInertProc, hWnd);
RegisterTouchWindow(hWnd, 0);
Связанные темы