Gestion de l’inertie dans le code non managé

Cette section explique comment utiliser l’interface IInertiaProcessor pour gérer l’inertie dans du code non managé.

Vue d’ensemble

Pour utiliser l’inertie dans du code non managé, vous devez implémenter des récepteurs d’événements pour le processeur de manipulation et le processeur d’inertie. Commencez par ajouter la prise en charge de la manipulation à votre application, comme décrit dans la section Ajout de la prise en charge de la manipulation au code non managé. Notez que la prise en charge de la manipulation nécessite que vous utilisiez des messages tactiles plutôt que des messages de mouvement pour fournir des données d’événement au processeur de manipulation. Une fois la manipulation effectuée, vous devez également implémenter un deuxième récepteur d’événements pour les événements générés par l’interface IInertiaProcessor ou modifier votre récepteur d’événements existant pour prendre en charge les événements générés par les interfaces IInertiaProcessor et IManipulationProcessor . Pour les besoins de cet exemple, il est plus facile de démarrer à partir du récepteur d’événements créé pour la section Ajout de la prise en charge de la manipulation au code non managé et d’ajouter un deuxième constructeur qui fonctionne avec le processeur d’inertie au lieu du processeur de manipulation. De cette façon, l’implémentation du récepteur d’événements peut fonctionner pour le processeur de manipulation ou le processeur d’inertie. En plus d’ajouter un deuxième constructeur, le récepteur d’événements aura une variable indiquant s’il effectuera les opérations en fonction de l’entrée d’inertie plutôt que de l’entrée de manipulation.

Ajouter la prise en charge de l’inertie à un récepteur d’événements de processeur de manipulation

Le code suivant montre le nouveau constructeur de récepteur d’événements, les nouvelles variables membres pour une interface IInertiaProcessor et un indicateur indiquant si le récepteur extrapole pour l’inertie.

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

Une fois que votre en-tête de classe contient les nouveaux constructeurs et un indicateur indiquant si vous extrapolez, vous pouvez implémenter votre récepteur d’événements pour avoir des blocs de gestion distincts pour les événements IManipulationProcessor et les événements IInertiaProcessor . Le constructeur qui accepte un IManipulationProcessor et un IInertiaProcessor doit définir l’indicateur fExtrapolating sur false, ce qui indique qu’il s’agit d’un gestionnaire d’événements IManipulationProcessor . Le code suivant montre comment le constructeur d’un récepteur d’événements qui utilise IManipulationProcessor peut être implémenté.

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

Le code suivant montre comment le constructeur d’un récepteur d’événements qui utilise IInertiaProcessor peut être implémenté. Ce constructeur définit l’indicateur fExtrapolating sur true, ce qui indique que cette instance de la classe de récepteur d’événements effectuera une extrapolation et effectuera toutes les opérations de déplacement effectuées précédemment par les événements du processeur de manipulation.

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

Notes

L’implémentation de la classe de récepteur d’événements du processeur de manipulation est réutilisée en tant que récepteur d’événements pour le processeur d’inertie.

 

Désormais, lorsque vous construisez cette classe, CManipulationEventSink, elle peut être construite en tant que récepteur d’événements pour un processeur de manipulation ou en tant que récepteur d’événements pour un processeur d’inertie. Lorsqu’il est construit en tant que récepteur d’événements de processeur d’inertie, l’indicateur fExtrapolating est défini sur true, ce qui indique que les événements de manipulation doivent être extrapolés.

Notes

ManipulationStarted sera déclenché par les interfaces IManipulationProcessor et IInertiaProcessor .

 

Lorsque la manipulation démarre, les propriétés de l’interface IInertiaProcessor sont définies. Le code suivant montre comment l’événement démarré est géré.

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

Dans cet exemple, les deltas de manipulation sont utilisés pour déplacer la fenêtre. Le code suivant montre comment l’événement delta est géré.

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

Dans cet exemple, les événements de manipulation terminée démarrent ou arrêtent un minuteur qui appelle Process sur l’interface IInertiaProcessor . Le code suivant montre comment l’événement de manipulation terminé est géré.

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

Le code suivant montre comment interpréter WM_TIMER messages dans WndProc pour effectuer des appels à Process sur l’interface IInertiaProcessor .

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

CoInitialiser le processeur d’inertie et le processeur de manipulation et initialiser les récepteurs d’événements

Une fois que votre récepteur d’événements a été modifié pour prendre en charge iManipulationProcessor et IInertiaProcessor, vous êtes prêt à initialiser les récepteurs d’événements et à les configurer pour qu’ils s’exécutent à partir de votre application. Le code suivant montre comment les pointeurs d’interface sont alloués.

//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’exemple de code suivant montre comment instancier vos 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)
   );

L’exemple de code suivant montre comment construire vos récepteurs d’événements en fonction des pointeurs d’interface et inscrire la fenêtre pour l’entrée tactile.

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


   RegisterTouchWindow(hWnd, 0);  

Inertie