Resources | developer.brewmp.com Resources | developer.brewmp.com

Developer

resources

Creating a Custom Touch Controller that Uses Virtual Input

Base version:

Brew® MP 1.0.2

Tested version:

Brew MP 1.0.2

Phone tested:

No

Objective

This document describes how to create a custom touch controller to add virtual input support to a widget.

The sample code uses the standard DateTimeWidget and adds the touch controller logic necessary to initiate a virtual input request and retrieve user data.

Requirements

Virtual input is a flexible graphical interface that represents some sort of input device, such as a twelve key keypad or a full keyboard. However, not all widgets should use virtual input, since most do not require any user input beyond basic touch functionality. The best candidates for widgets that can support virtual input are ones that require either numerical or text input, such as TextWidget.

In this example, a custom touch controller is created for the DateTimeWidget, which will allow users to enter a date or a time using the default virtual keypad. This touch controller acts an an extension to the default DateTimeTouchController to add virtual input support to this widget.

There are two requirements for virtual input to work:

  • The application must enable touch on the root container.
  • The application must enable virtual input on the root container.
Refer to the C/C++ API Reference for more information.

Example code location

ZIP filename

Location

Run app

c_virtualinputdatetimewidget_extension

c_virtualinputdatetimewidget_app

Brew MP Resources

  • Download and extract the ZIP file.

  • Compile the extension.

  • Compile the app.

  • Run it on the Brew MP Simulator.

Initialization

This include is needed to create a touch controller that uses virtual input.

#include "AEEVirtualInputHelpers.h"

The PROPEX_VIRTUALINPUT_ENABLE property must be handled by the touch controller, which is sent to enable/disable virtual input automatically when the widget is inserted within a virtual input-enabled root container.

When disabling virtual input, all listeners must be properly cancelled, and any state enabled during initialization must be reset.

static boolean VirtualInputDateTimeTC_HandleSetPropEx(VirtualInputDateTimeTC *pMe,
            ControllerPropEx* pPropEx )   
{   
...   
      case PROPEX_VIRTUALINPUT_ENABLE:   
         if (pPropEx->nSize == (int)sizeof(boolean)) {   
            if (TRUE == *(boolean*)pPropEx->pUser) {   
               VirtualInputDateTimeTC_InitVirtualInputMgr(pMe);   
            }   
            else{   
               LISTENER_Cancel(&pMe->mlRCViewModel);   
               VirtualInputDateTimeTC_Detach(pMe);   
               RELEASEIF(pMe->picVirtualInputMgr);   
            }   
            bHandled = TRUE;   
         }   
         break;   
...   
}

The following function is used when virtual input is first initialized for the DateTimeWidget.

static __inline void VirtualInputDateTimeTC_InitVirtualInputMgr(VirtualInputDateTimeTC *pMe){   
...

A listener must be attached to the root container's view model to receive notifications in case the current Virtual Input Manager is changed.

...   
   //Attach listener to the RC's view model   
   ERR_TRY( IWIDGET_GetRootContainer(pMe->piwChild, &piwRoot) );   
   ERR_TRY( IWidget_GetViewModel(piwRoot, &pimRCViewModel) );   
      
   LISTENER_Cancel(&pMe->mlRCViewModel);   
   ERR_TRY( IModel_AddListenerEx((IModel*)pimRCViewModel, &pMe->mlRCViewModel,  
   (PFNLISTENER)VirtualInputDateTimeTC_RootViewModelListener, pMe) );   

To ensure no orphans, all references to the previous Virtual Input Manager are released and the listeners are cancelled.

   //Detach previous virtual input manager, if set   
   if (pMe->bVirtualInputMode && pMe->picVirtualInputMgr) {   
      LISTENER_Cancel(&pMe->mlVirtualInputStatus);   
      (void)IController_SetVirtualInputTargetWidget(pMe->picVirtualInputMgr, NULL);   
   }   
   RELEASEIF(pMe->picVirtualInputMgr);

Retrieve the current Virtual Input Manager from the root container for future reference.

   //Grab the virtual input manager   
   ERR_TRY(IWidget_GetVirtualInputMgr(piwRoot, &pMe->picVirtualInputMgr) );   
...   
}

Showing the Virtual Keypad

Initiate a virtual input request on pointer events, namely EVT_POINTER_UP.

static boolean VirtualInputDateTimeTC_HandleEvent(void *pCxt, AEEEvent eCode,
            uint16 wParam, uint32 dwParam){   
...   
   case EVT_POINTER_UP:   
      //If we're virtual input enabled and we're not currently already   
      //awaiting input from the virtual keypad, initiate a virtual input query   
      if (NULL != pMe->picVirtualInputMgr && !pMe->bVirtualInputMode) {   
         VirtualInputDateTimeTC_StartVirtualInputRequest(pMe);   
      }   
      bHandled = TRUE;   
      break;   
...

The following function is called to set up and display the virtual keypad.

static __inline void VirtualInputDateTimeTC_StartVirtualInputRequest(VirtualInputDateTimeTC *pMe) {   
...   
   //Set self as target   
   ERR_TRY( IController_SetVirtualInputTargetWidget(pMe->picVirtualInputMgr,
               pMe->piwChild) );   
      
   //Attach model   
   ERR_TRY( IController_GetVirtualInputStatusModel(pMe->picVirtualInputMgr,
               &pimStatusModel) );   
   ERR_TRY( IModel_AddListenerEx(pimStatusModel, &pMe->mlVirtualInputStatus,  
   (PFNLISTENER)VirtualInputDateTimeTC_VirtualInputStatusListener, (void*)pMe) );   
   //Update input mode of keypad widget to give us numbers.   
   ERR_TRY( IController_SetVirtualInputLayoutMode(pMe->picVirtualInputMgr, FALSE,
               AEE_TLANG_NUMBERS) );   
      
   //Update virtual input flag to active   
   pMe->bVirtualInputMode = TRUE;   
   ERR_TRY( IController_ShowVirtualInputFrame(pMe->picVirtualInputMgr) );   
...

Closing the Virtual Keypad

When the user presses the finish button, dismiss button, or clicks on a different widget, the status model of the Virtual Input Manager will send a notification. The touch controller must deal appropriately with each notification to ensure a proper and constant behavior.

The function must first ensure that the event is meant for this particular widget.

static void VirtualInputDateTimeTC_VirtualInputStatusListener(VirtualInputDateTimeTC *pMe,
            ModelEvent *pe)   
{   
...      
   //If we're not currently waiting for input data and we get this notification,   
   //we should detach ourselves.   
   if (!pMe->bVirtualInputMode) {   
      LISTENER_Cancel(&pMe->mlVirtualInputStatus);   
      return;   
   }   
   
   //We only care out status changes   
   if (EVT_MDL_VIRTUALINPUT_STATUS_CHANGE != pe->evCode) {   
      return;   
   }   
      
   //Disable listener if we are not the current target for the virtual input   
   nErr = IController_GetVirtualInputTargetWidget(pMe->picVirtualInputMgr, &piwTarget);   
   if (SUCCESS != nErr || pMe->piwChild != piwTarget) {   
      LISTENER_Cancel(&pMe->mlVirtualInputStatus);   
      pMe->bVirtualInputMode = FALSE;   
      RELEASEIF(piwTarget);   
      return;   
   }   
   RELEASEIF(piwTarget);

The touch controller must take different actions based on which status is received.

If VIRTUALINPUT_STATUS_ACCEPT is received, it means the user is finished entering data, and the keypad should be hidden.

   switch (ev->nStatus) {   
      case VIRTUALINPUT_STATUS_ACCEPT:   
         //On accept close the window.   
         VirtualInputDateTimeTC_Detach(pMe);   
         break;   
            

The status VIRTUALINPUT_STATUS_MOVE is sent when another widget initiates a virtual input request due to user interaction. The touch controller must respond by cancelling the listener but should not try to hide the keypad since another widget requires it.

      case VIRTUALINPUT_STATUS_MOVE:   
         //On move, only cancel the listener.   
         LISTENER_Cancel(&pMe->mlVirtualInputStatus);   
         pMe->bVirtualInputMode = FALSE;   
         break;

When a VIRTUALINPUT_STATUS_CANCEL status is received, the virtual keypad is dismissed. If a buffer is available, the touch controller ignores it's content.

      case VIRTUALINPUT_STATUS_CANCEL:   
         //On cacel, close the window   
         VirtualInputDateTimeTC_Detach(pMe);   
         break;   
...   

Dtor

Dtor is called when the base DateTimeTouchController reaches a reference count of zero. All references are released and listeners canceled.

static void VirtualInputDateTimeTC_Dtor(void *pCxt)   
{   
   VirtualInputDateTimeTC* pMe = (VirtualInputDateTimeTC*) pCxt;   
      
   //Relase all references, cancel all listeners   
   VirtualInputDateTimeTC_Detach(pMe);   
   LISTENER_Cancel(&pMe->mlRCViewModel);   
   HANDLERDESC_FREE(&pMe->hdBase);   
   RELEASEIF(pMe->picVirtualInputMgr);   
   RELEASEIF(pMe->piEnv);   
}

Related information

For more information, see

  • The Widgets Technology Guide
  • The Widgets Touchscreen Technology Guide