Advanced Pointer Tutorial

This tutorial provides step-by-step instructions and sample code to help you write applications using advanced pointers.

Variant: ScreenPlay. Target audience: Application developers.

Required background

This topic builds on the material in the following topics:

Enabling multiple pointers in a window

  1. [[[ERROR: [NOKX000E] Unable to find definition for key reference 'TPointerEventTAdvancedPointerEvent']]]RWindow provides a handle to a standard window. Call RWindow to create an instance of a window.

    RWindow window(ws);
    
    User::LeaveIfError(window.Construct(wg, reinterpret_cast<TUint32>(&wg) + 1));
    
  2. Call RWindow::EnableAdvancedPointers() to enable advanced pointers. Then call RWindowBase::Activate() to display the window and enable the window to receive events. RWindow inherits from RWindowBase, so you can call the Activate() function on RWindow.

    window.EnableAdvancedPointers();
    window.Activate();
    

    When an application needs to receive advanced pointer events in a window, it must call RWindowBase::EnableAdvancedPointers() for the window before it is activated.

    If advanced pointers are not enabled for a window, it receives only standard TPointerEvent information from a single pointer with no pressure and proximity data. The single pointer environment rules describe the way in which pointer events coming from many pointers in the multiple pointer model are transformed into events of one pointer. They are necessary to ensure that old single-pointer applications work in a multiple pointer environment intuitively and as expected by the user.

    However, the new TPointerEvent types, EEnterCloseProximity, EExitCloseProximity, EEnterHighPressure and EExitHighPressure, are delivered to all windows, even to those that do not enable advanced pointers.

Getting Z coordinates from TPointerEvent

  1. Call TPointerEvent::AdvancedPointerEvent() on a TPointerEvent to return a pointer to a TAdvancedPointerEvent.

    TZType aZType;
    TPointerEvent& aPointerEvent;
    TInt* aZ; 
    TInt* aPoint3D;
    
    TAdvancedPointerEvent *advancedP = aPointerEvent.AdvancedPointerEvent();
    

    TPointerEvent is a struct that contains details of a pointer event. TZType is a struct provided by the programmer containing members to hold proximity, pressure, and "proximity and pressure" data.

  2. Now we need to test whether the pointer event contains advanced pointer data. If it is not an advanced pointer, the code leaves.

    If it is an advanced pointer, we call functions to detect proximity, pressure, "proximity and pressure" data and coordinates.

    if(!advancedP)
        {
        // The TPointerEvent isn't an instance of TAdvancedPointerEvent
        User::Leave(KErrArgument);
        }
    
    switch(aZType)
        {
        case EZTypeProximity:
            aZ = advancedP->Proximity();
            aPoint3D = advancedP->Proximity3D(); 
            break;
        case EZTypePressure:
            aZ = advancedP->Pressure();
            aPoint3D = advancedP->Pressure3D();
            break;
        case EZTypeProximityAndPressure:
            aZ = advancedP->ProximityAndPressure();
            aPoint3D = advancedP->ProximityAndPressure3D();
            break;
        default: 
            User::Leave(KErrArgument);
            break;
        }
    

    Proximity is always negative and pressure is always positive. Internally they are combined together as a Z coordinate. When Z > 0, the proximity is 0 and the Z value represents the pressure. When Z < 0, the pressure is 0 and the Z value represents the proximity. Some APIs use only a Z coordinate (such as the threshold getters and setters and TAdvancedPointerEvent::ProximityAndPressure()). In these, the Z coordinate is interpreted in terms of pressure and proximity.

Figure: Relationships between the pointer proximity, pressure and Z coordinate

Pinch zooming

This example shows an easy way to pinch zoom an image when the screen receives pointer events from two pointers. There are two functions in this code that must be implemented by the programmer: BitmapCoordinates() and MoveBitmap(). They are not included in the example because they involve complex calculations that are not related to advanced pointers.

The high-level steps to perform pinch zooming are:

  1. Define the coordinates, equivalent to the given on-screen coordinates. In the code example, this is done using the function BitmapCoordinates().

  2. Define the ID of the pointer by using TAdvancedPointerEvent::PointerNumber(). If the device can handle two pointers (two fingers) at the same time, their numbers are 0 and 1. The pointer number enables you to distinguish a given pointer from other pointers.

  3. For each pointer assign its coordinates to a local variable. We assume there are only two pointers handled by the system here.

  4. Use the MoveBitmap() function to achieve the zoom effect.

    /**
    Receives pointer events from two pointers to perform Pinch Zoom of the image.
    Function will finish when EButton1Up is received for any of the pointers.
    @param aPointer1    Coordinates of pointer number 1 when zoom is started
    @param aPointer2    Coordinates of pointer number 2 when zoom is started
    */
    
    void PinchZoom(TPoint aPointer1, TPoint aPointer2)
        {
        TPoint actualP1 = aPointer1;
        TPoint actualP2 = aPointer2;
        
        // translate on-screen pointer coordinates to coordinates of displayed bitmap
        TPoint bitmapCatching1 = BitmapCoordinates(aPointer1);
        TPoint bitmapCatching2 = BitmapCoordinates(aPointer2);
        
        TBool repaint = EFalse;
                                    
        while (ETrue)
            {
            TAdvancedPointerEvent event = GetNextPointerEvent();
            
            if (event.iType == TPointerEvent::EDrag)
                { 
                if (event.PointerNumber() == 1)
                    { 
                    actualP1 = event.iPosition;
                    repaint = ETrue;
                    }
                else if (event.PointerNumber() == 2)
                    {
                    actualP2 = event.iPosition;
                    repaint = ETrue;
                    }
                }
            else if (event.iType == TPointerEvent::EButton1Up)
                {
                break;
                }
            
            if (repaint)
                {
                // move bitmap on the screen in the way that
                // bitmapCatching1 point of the bitmap will be displayed at actualP1 screen coordinate,
                // bitmapCatching2 point of the bitmap will be displayed at actualP2 screen coordinate.
                MoveBitmap(bitmapCatching1, actualP1, bitmapCatching2, actualP2);
                repaint = EFalse;
                }
            }
        }
    

Related concepts