Gestione dell'inerzia nel codice non gestito

Questa sezione illustra come usare l'interfaccia IInertiaProcessor per la gestione dell'inerzia nel codice non gestito.

Panoramica

Per usare l'inerzia nel codice non gestito, è necessario implementare sink di eventi sia per il processore di manipolazione che per il processore di inerzia. Per iniziare, aggiungere il supporto per la manipolazione all'applicazione come descritto nella sezione Aggiunta del supporto per la manipolazione a codice non gestito. Si noti che il supporto per la manipolazione richiede l'uso di messaggi di tocco anziché messaggi di movimento per inserire i dati degli eventi al processore di manipolazione. Dopo aver eseguito la manipolazione, è necessario implementare anche un secondo sink di eventi per gli eventi che verranno generati dall'interfaccia IInertiaProcessor o sarà necessario modificare il sink di eventi esistente in modo da contenere entrambi gli eventi generati da IInertiaProcessor e dalle interfacce IManipulationProcessor . Ai fini di questo esempio, è più semplice iniziare dal sink di eventi creato per la sezione Aggiunta del supporto per la manipolazione a codice non gestito e aggiungere un secondo costruttore che funziona con l'inerzia del processore anziché con il processore di manipolazione. In questo modo, l'implementazione del sink di eventi può funzionare per il processore di manipolazione o l'inerzia del processore. Oltre ad aggiungere un secondo costruttore, il sink dell'evento avrà una variabile che indica se eseguirà le operazioni in base all'input inerziale anziché all'input di manipolazione.

Aggiungere il supporto dell'inerzia a un sink di eventi del processore di manipolazione

Il codice seguente mostra il nuovo costruttore sink di eventi, le nuove variabili membro per un'interfaccia IInertiaProcessor e un flag che indica se il sink estrapola l'inerzia.

    CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd);
    CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd);
    IInertiaProcessor*      m_pInert;
    BOOL fExtrapolating; 

Dopo che l'intestazione della classe ha i nuovi costruttori e un flag che indica se si esegue l'estrapolazione, è possibile implementare il sink di eventi per avere blocchi di gestione separati per gli eventi IManipulationProcessor e gli eventi IInertiaProcessor . Il costruttore che accetta un IManipulationProcessor e un IInertiaProcessor deve impostare il flag fExtrapolating su false, che indica che si tratta di un gestore eventi IManipulationProcessor . Nel codice seguente viene illustrato come implementare il costruttore per un sink di eventi che 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);
}

Nel codice seguente viene illustrato come implementare il costruttore per un sink di eventi che usa IInertiaProcessor . Questo costruttore imposta il flag fExtrapolating su true, a indicare che questa istanza della classe sink di evento eseguirà l'estrapolazione e eseguirà tutte le operazioni di spostamento eseguite in precedenza dagli eventi del processore di manipolazione.

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

L'implementazione della classe sink di evento dal sink di eventi del processore di manipolazione viene riutilizzata come sink di evento per il processore di inerzia.

 

Ora quando si costruisce questa classe, CManipulationEventSink, può essere costruita come sink di evento per un processore di manipolazione o come sink di eventi per un processore di inerzia. Quando viene costruito come sink di eventi del processore di inerzia, il flag fExtrapolating sarà impostato su true, a indicare che gli eventi di manipolazione devono essere estrapolati.

Nota

ManipulationStarted verrà generato dalle interfacce IManipulationProcessor e IInertiaProcessor .

 

All'avvio della manipolazione, vengono impostate le proprietà dell'interfaccia IInertiaProcessor . Il codice seguente illustra come viene gestito l'evento avviato.

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;
}

In questo esempio, i delta di manipolazione vengono usati per spostare la finestra. Il codice seguente illustra come viene gestito l'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;
}

In questo esempio, gli eventi di manipolazione completati avviano o arresteranno un timer che chiamerà Process sull'interfaccia IInertiaProcessor . Nel codice seguente viene illustrato come viene gestito l'evento di manipolazione completato.

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;
}

Il codice seguente illustra come interpretare WM_TIMER messaggi in WndProc per eseguire chiamate a Process nell'interfaccia IInertiaProcessor .

case WM_TIMER:       
  if (g_pIInertProc){
    BOOL b;       
    g_pIInertProc->Process(&b);        
  }
  break;

CoInitialize the Inertia Processor and Manipulation Processor and Initialize the Event Sinks

Dopo aver modificato il sink di eventi per supportare sia IManipulationProcessor che IInertiaProcessor, è possibile inizializzare i sink di eventi e configurarli per l'esecuzione dall'applicazione. Nel codice seguente viene illustrato come vengono allocati i puntatori all'interfaccia.

//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;

L'esempio di codice seguente illustra come creare un'istanza delle interfacce.

   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)
   );

L'esempio di codice seguente illustra come costruire i sink di eventi in base ai puntatori di interfaccia e registrare la finestra per l'input tocco.

   g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, g_pIInertProc, hWnd);
   g_pManipulationEventSink = new CManipulationEventSink(g_pIInertProc, hWnd);


   RegisterTouchWindow(hWnd, 0);  

Inerzia