Control de la inercia en código no administrado
En esta sección se explica cómo usar la interfaz IInertiaProcessor para controlar la inercia en código no administrado.
Información general
Para usar la inercia en código no administrado, debe implementar receptores de eventos tanto para el procesador de manipulación como para el procesador de inercia. Para empezar, agregue compatibilidad con la manipulación a la aplicación, tal y como se describe en la sección Agregar compatibilidad con la manipulación al código no administrado. Tenga en cuenta que la compatibilidad con la manipulación requiere que use mensajes táctiles en lugar de mensajes de gestos para alimentar los datos de eventos al procesador de manipulación. Una vez que haya funcionado la manipulación, también debe implementar un segundo receptor de eventos para los eventos que la interfaz IInertiaProcessor va a generar o tendrá que modificar el receptor de eventos existente para acomodar los eventos generados por las interfaces IInertiaProcessor y IManipulationProcessor . Para los fines de este ejemplo, es más fácil empezar desde el receptor de eventos creado para la sección Agregar compatibilidad de manipulación a código no administrado y agregar un segundo constructor que funciona con el procesador de inercia en lugar del procesador de manipulación. De este modo, la implementación del receptor de eventos puede funcionar para el procesador de manipulación o para el procesador de inercia. Además de agregar un segundo constructor, el receptor de eventos tendrá una variable que indica si realizará las operaciones en función de la entrada de inercia en lugar de la entrada de manipulación.
Agregar compatibilidad con la inercia a un receptor de eventos del procesador de manipulación
En el código siguiente se muestra el nuevo constructor receptor de eventos, las nuevas variables miembro para una interfaz IInertiaProcessor y una marca que indica si el receptor está extrapolando para la inercia.
CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd);
CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd);
IInertiaProcessor* m_pInert;
BOOL fExtrapolating;
Una vez que el encabezado de clase tiene los nuevos constructores y una marca que indica si está extrapolando, puede implementar el receptor de eventos para tener bloques de control independientes para los eventos IManipulationProcessor e IInertiaProcessor . El constructor que acepta un IManipulationProcessor y IInertiaProcessor debe establecer la marca fExtrapolating en false, lo que indica que se trata de un controlador de eventos IManipulationProcessor . En el código siguiente se muestra cómo se podría implementar el constructor de un receptor de eventos que usa 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);
}
En el código siguiente se muestra cómo se podría implementar el constructor de un receptor de eventos que usa IInertiaProcessor . Este constructor establece la marca fExtrapolating en true, lo que indica que esta instancia de la clase receptora de eventos realizará la extrapolación y realizará las operaciones de movimiento realizadas anteriormente por los eventos del procesador de manipulación.
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);
}
Nota
La implementación de la clase receptora de eventos del receptor de eventos del procesador de manipulación se reutiliza como receptor de eventos para el procesador de inercia.
Ahora, al construir esta clase, CManipulationEventSink, se puede construir como receptor de eventos para un procesador de manipulación o como receptor de eventos para un procesador de inercia. Cuando se construye como receptor de eventos de procesador de inercia, tendrá la marca fExtrapolating establecida en true, lo que indica que se deben extrapolar los eventos de manipulación.
Nota
ManipulationStarted se generará mediante las interfaces IManipulationProcessor e IInertiaProcessor .
Cuando se inicia la manipulación, se establecen las propiedades de la interfaz IInertiaProcessor . En el código siguiente se muestra cómo se controla el evento iniciado.
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;
}
En este ejemplo, se usan deltas de manipulación para mover la ventana. En el código siguiente se muestra cómo se controla el evento 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;
}
En este ejemplo, los eventos completados de manipulación inician o detienen un temporizador que llamará a Process en la interfaz IInertiaProcessor . En el código siguiente se muestra cómo se controla el evento de manipulación completado.
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;
}
En el código siguiente se muestra cómo se puede interpretar WM_TIMER mensajes en WndProc para realizar llamadas a Process en la interfaz IInertiaProcessor .
case WM_TIMER:
if (g_pIInertProc){
BOOL b;
g_pIInertProc->Process(&b);
}
break;
Inicializar el procesador de inercia y el procesador de manipulación e inicializar los receptores de eventos
Una vez modificado el receptor de eventos para admitir IManipulationProcessor y IInertiaProcessor, está listo para inicializar los receptores de eventos y configurarlos para que se ejecuten desde la aplicación. En el código siguiente se muestra cómo se asignan los punteros de interfaz.
//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;
En el ejemplo de código siguiente se muestra cómo crear instancias de las interfaces.
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)
);
En el ejemplo de código siguiente se muestra cómo construir los receptores de eventos según los punteros de interfaz y registrar la ventana para la entrada táctil.
g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, g_pIInertProc, hWnd);
g_pManipulationEventSink = new CManipulationEventSink(g_pIInertProc, hWnd);
RegisterTouchWindow(hWnd, 0);
Temas relacionados