Views and dialogs

From UIQ Books

Jump to: navigation, search

Contents

Views and Dialogs

Overview

Many applications follow a list and details model. Using this model, it is common for the list and details components to be in separate views. The list view is typically single page while detail views are frequently multi-page.

The UIQ 3 Contacts application follows this model. The list view has a single page:

Contacts application list view

Figure 1 Contacts application list view

When opening a contact, the most commonly used information is displayed on the first page. Other information is presented in pages two (address) and three (notes). Information that requires a lot of screen space is often best separated out. The user can easily switch pages to find the required information.

Contacts application detail view Contacts application detail view Contacts application detail view

Figure 2 Contacts application detail views

If all the contact information were presented in a single page the user would have to scroll down a long way to get to the notes.

The Contacts detail view is optimized for reading information and taking actions such as making phone calls. Empty fields are not displayed. To add or edit data, a separate view is used, called the edit view. The edit view is laid out in the same way as the details view, spreading the data over the same three pages. Empty fields are displayed so that data can be entered.

Contacts application edit view Contacts application edit view Contacts application edit view

Figure 3 Contacts application edit views

Working with Views

Views and Pages

Until now our example applications have only used a single QIK_VIEW_CONFIGURATIONS resource to display a view of the application data. While this may have contained multiple QIK_VIEW resources, one per supported configuration, the application is said to have a single view. The example applications have demonstrated both single and multi-page displays within a single view.

For multi-page views, the tab information is normally defined in a QIK_VIEW_PAGE application resource:

  QIK_VIEW_PAGE
    {
    page_id = EAppSpecificListViewPageId1;
    tab_bmpid = EMbmListview2Tab1;
    tab_bmpmaskid = EMbmListview2Tab1mask;
    page_content = r_list_view_page1_control;
    },
 
  QIK_VIEW_PAGE
    {
    page_id = EAppSpecificListViewPageId3;
    tab_caption = "Tab3";
    page_content = r_list_view_page3_control;
    }

The pages are represented as tabs in the application title bar’s view context area:

ListView2 application

Figure 4 ListView2 application showing screen layout

Within the application space, you display one or more views of your application data. Within a single application there are no specific rules about when to use multiple views compared to multiple pages. It is possible to use any combination. Most applications display a single data record per view. When there is a large amount of data associated with a single entity, this data is generally split across two or more pages.

If necessary, your application can extend outside of the application space by hiding the status bar, title bar or button bar/soft key bar:

void CAppSpecificListView::ViewConstructL()
  {
  TQikViewMode mode = ViewMode();
 
  // Normally only 1 of the following
  // would be used at a time.
 
  // Hides status bar.
  mode.SetStatusBar(EFalse);
 
  // Hides the title bar.
  mode.SetAppTitleBar(EFalse);
 
  // Hides button bar/soft key bar and status bar.
  mode.SetButtonOrSoftkeyBar(EFalse);
 
  // Applies the requested view mode.
  SetViewModeL(mode);
  }

You can also use the entire screen:

void CAppSpecificListView::ViewConstructL()
  {
  TQikViewMode mode = ViewMode();
  mode.SetFullscreen();
  SetViewModeL(mode);
  }

You must be careful if you extend outside of the normal application space. A consistent user interface is generally much easier to use. Varying the interface presented by your application compared to standard applications, for example, by hiding the status bar, may make it hard for the user to use the mobile phone as expected. Some UIQ 3 phones have a task switch and short cut icon in the status bar. Careful integration with the features and functions on a phone ensures that your application does not upset your customers.

Defining Multiple Views: MultiView1

If you want to design an application with multiple views, it is straightforward. As you might expect, there is one QIK_VIEW_CONFIGURATIONS and corresponding CQikViewBase or CQikMultiPageViewBase derived class for each view.

The MultiView1 example application presents a basic multi-view application. It implements the classic list and details model where one view is the list, and a second view presents the details.

There are two QIK_VIEW_CONFIGURATIONS defined in the resource file; their corresponding CQikViewBase derived objects are defined in MultiView1.cpp:

RESOURCE QIK_VIEW_CONFIGURATIONS r_list_view_configurations
  {
  configurations =
    {
    };
  }
 
class CAppSpecificListView : public CQikViewBase, public MQikListBoxObserver
  {
  };
 
// and ...
 
RESOURCE QIK_VIEW_CONFIGURATIONS r_details_view_configurations
  {
  configurations =
    {
    };
  }
 
class CAppSpecificDetailsView : public CQikViewBase
  {
  };
 

View Navigation

A very important aspect of views is the ability to navigate between them. The following diagram displays the typical view navigation on a UIQ 3 phone:

View navigation

Figure 5 View navigation

From the diagram you can see there is a parent-child like relationship between the views. For example, the parent to the application list view is the launcher view; its child view is the application details view.

So far, all example applications have had a single application view. Entering and exiting the view is handled automatically by the framework; our application did not appear to have to do anything for this to happen but our previous applications do perform two tasks. Firstly, the view constructor code passes KNullViewId to the base class:

CAppSpecificListView::CAppSpecificListView(CAppSpecificUi& aAppUi) : CQikMultiPageViewBase(aAppUi,KNullViewId)
  {
  }

Secondly our HandleCommandL() method performs an action on commands it does not otherwise handle. When the user presses the Back button the EQikCmdGoBack command is sent to the currently active view in the application.

void CAppSpecificListView::HandleCommandL(CQikCommand& aCommand)
  {
  switch (aCommand.Id())
    {
    default:              // For example the back button...
 
    CQikViewBase::HandleCommandL(aCommand);
    break;
    }
  }

The application framework processes the EQikCmdGoBack command by looking up which view has been defined as the parent to the currently active view. In MultiView1, as in previous examples, we have defined the parent as KNullViewId.

For the detail view, the object constructor code passes KViewIdListView to the base class:

CAppSpecificDetailsView::CAppSpecificDetailsView(CAppSpecificUi& aAppUi):CQikViewBase(aAppUi,KViewIdListView)
  {
  }

This defines the parent of the details view to be the list view, which is uniquely identified by KViewIdListView in our case.

So when the EQikCmdGoBack command is processed by the details view, it will navigate to the list view. When the list view processes the command, it uses the value KNullViewId as a shortcut to mean the application launcher.

Applications navigate between views by calling the ActivateViewL() method. In the MultiView1 example we display the details view when an item in the list view is selected:

void CAppSpecificListView::HandleListBoxEventL(CQikListBox* aListBox, TQikListBoxEvent aEventType, TInt aItemIndex, TInt aSlotId)
  {
  switch (aEventType)
    {
    case EEventItemConfirmed:
    case EEventItemTapped:
      {
      iQikAppUi.ActivateViewL(KViewIdDetailsView);
      break;
      }
    default:
 
    break;
    }
  }

View Activation

When an application attempts to switch between views, it is important to understand what actually happens. In simple terms, the following occurs:

void CCoeAppUi::ActivateViewL(const TVwsViewId& aViewId)
  {
 
  // Locate current view.
  MCoeView* oldView=GetCurrentView();
 
  // Locate view to activate.
  MCoeView* newView=GetViewToActivate(aViewId);
 
  // Activate new view and tell the view it is activated.
  newView ->ViewActivatedL();
 
  // Only now the new view has been activated will the previous view be deactivated.
  oldView->ViewDeactivated()
  }

UIQ adds a layer of functionality to the view server. The CQikViewBase class hides and handles the real view server invoked ViewActivatedL() call in CQikViewBaseProxy, it will then do an asynchronous call and return directly. The asynchronous call invokes the CQikViewBase::ViewActivatedL function which is overridden by your application. This means from a ViewServer perspective that view activations are always successful and hence the previous view will always be deactivated via its ViewDeactivated() method. This occurs before your new view receives its ViewActivatedL() request.

If your application leaves in the CQikViewBase::ViewActivatedL override, the UIQ framework will call the CQikViewBase::HandleErrorL() method of your view which you should override and handle the EViewActivatedL case. You should note that your previous view will have been deactivated by here, so rolling back to displaying that view may be difficult, for example, if you have run out of memory and cannot re-activate the previous view.

If your application leaves whilst in the CQikViewBase::HandleErrorL() method, the application will exit and the bring the previous application foreground.

You should note that this is different to the usage of the view server in other environments where the previous view is not deactivated until the new view is successfully activated.

Pro tip:

This is a very good example of why you need to understand the consequences of functions that can leave. Performing error handling and cleanup is only part of the task; the state your application is left in after a leave occurs is equally as important.

There are several ways you may wish to handle this particular problem:

  • In the MultiView1 application, the ViewActivatedL() method does not perform any actions that can leave, all set up is performed in the ViewConstructL() method.
  • You can TRAP any leave and perform some sensible actions such that the ViewActivatedL() method itself does not leave but the user is informed of a problem occurring. For example, a list view may be reset to contain zero entries and the user informed of the problem via an iEikonEnv->AlertWin().
  • Alternatively, you can simply allow the ViewActivatedL() to leave but ensure the application state is consistent should a leave occur.

Direct Navigation Links

In the MultiView1 application, we switch from the list view to the details view by calling the iQikAppUi.ActivateViewL(KViewIdDetailsView). While this successfully changes views, an important piece of information required by the details view, which item we are required to display, is not communicated.

There are several ways to achieve this; all have advantages and disadvantages.

The details view could have addressability to the list view’s list box, thus ask the list box which entry is the current item. Unfortunately, obtaining the list box handle is not that easy. Even if it were, this approach relies on the list view maintaining the list box even when the view is deactivated. This solution is probably the least desirable.

Alternatively, the list view’s current item index could be stored in a global variable (available in applications since the advent of Symbian OS 9), or preferably in an object common to both the list and details view, such as an engine.

Finally, the current item index can be passed from the list view to the details view by packaging the parameter into a descriptor and using the ActivateViewL(TVwsViewId&, TUid, TDesC8&) variant of the ActivateViewL() method.

The process of communicating parameters between views and causing the target view to be displayed has been given the name Direct Navigation Link (DNL). One view is said to DNL to another. Our application only switches between views in the same application but the underlying DNL mechanism allows applications to switch between any views in the system. For example, the detail view in Contacts can navigate directly to the New message view in messaging.

Using DNLs appears to be the most attractive option; however, we must take in to account that a view can be activated for a number of different reasons.

In the MultiView1 example application, moving from list view to detail view is not the only way in which the details view can be activated. In the emulator, if you click the Applications button you will jump directly to the application launcher. Similarly, on phones there are numerous ways in which you can jump out of the current application, for example using the task manager option. If you now come back to the MultiView1 application from the application launcher (or task manager on a phone) the details view is activated. Not surprisingly, the application launcher knows nothing about your application-specific parameters, so these are not passed to your application ViewActivatedL() method.

Sending Parameters to Detail Views

MultiView 2 Example

The MultiView2 application demonstrates how you can package and send a parameter to the details view. We define a simple class, TListDetailsDnlInfo, to contain the parameters we wish to send. In the MultiView2 example a single integer is sent.

class TListDetailsDnlInfo
  {
 
 public:
  TInt iItemIndex;
  };
 
const TUid KUidDisplaySpecificEntry = {0x00000001};
 
typedef TPckgBuf<TListDetailsDnlInfo>TListDetailsDnlInfoBuf;
 
void CAppSpecificListView::HandleListBoxEventL(CQikListBox* aListBox, TQikListBoxEvent aEventType, TInt aItemIndex, TInt aSlotId)
  {
  switch (aEventType)
    {
  case EEventItemConfirmed:
  case EEventItemTapped:
      {
      TListDetailsDnlInfo entry;
      entry.iItemIndex=aItemIndex;
      TListDetailsDnlInfoBuf bb(entry);
      iQikAppUi.ActivateViewL(KViewIdDetailsView, KUidDisplaySpecificEntry,bb);
      break;
      }
 
  default:
    break;
   }
  }
Pro tip:

The definition and usage of the TPckgBuf is really a piece of syntax to enable the software to convert between a class and a byte stream in a type safe way without needing to use casts or manually create the byte stream.

When the information is delivered to the details view, we need to convert the byte stream back into the original data structure and extract the parameters that have been packaged up. The following code demonstrates this happening:

void CAppSpecificDetailsView::ViewActivatedL(const TVwsViewId& aPrevViewId, const TUid aCustomMessageId, const TDesC8& aCustomMessage)
  {
  TBuf<128>buf;
 
  if (aCustomMessageId==KUidDisplaySpecificEntry)
    {
    TListDetailsDnlInfoBuf bb;
    bb.Copy(aCustomMessage);
    iEikonEnv->Format128(buf,R_STR_ACTIVATING, bb().iItemIndex);
    iEikonEnv->InfoMsg(buf);
    }
  else
    {
    iEikonEnv->Format128(buf,R_STR_UNKNOWN_MSGID, aCustomMessageId.iUid);
    iEikonEnv->InfoMsg(buf);
    }
  }

At its simplest level, this code fragment shows how to implement the ViewActivatedL() method and accommodate both ways in which it can be called.

Pro tip:

When called by the application launcher the aCustomMessageId value will be zero. For this reason you should not define any application-specific aCustomMessageId value to be zero; in our example we define it to be the value one.

In MultiView2, the example code does not perform any meaningful tasks with the passed parameter set: we do not set the details view to display anything other than the UI component default values. In a real application, any values displayed should be synchronized with the internal state of the item defined by the passed parameters.

If a view is not actually passed any parameters, it needs another mechanism to determine which item is being referenced. Unfortunately, the solution to this problem varies depending on individual circumstances.

If a view is displaying read-only information, you may allow each of the UI components in your view to retain state value. When such a view is ViewActivatedL() and is passed some parameters, it will set up the UI components. When it is not passed any parameters, no action is required since the UI components retain the correct state value. This approach assumes that you do not destroy components on a ViewDeactivated() and attempt to re-create on the ViewActivatedL(). Unfortunately, this solution is of limited value since it is rare that a view contains just read-only information.

If your view displays modifiable content, you need a way of knowing to which item the details belong. Otherwise, your application is not be able to associate any changes with the correct item. If the ViewActivatedL() method is called with some parameters, that information is normally provided in those parameters. However, when no parameters are passé, that information is missing. Therefore the view and/or an external object needs to record which item is being displayed.

Again, there are numerous solutions for this problem. For example, when parameters are passed to the view they could be stored. When no parameters are passed, the view would use the previously stored parameters.

MultView3 Example

We demonstrate using previously stored parameters in the ViewActivatedL() method of the MultiView3 example:

void CAppSpecificDetailsView::ViewActivatedL(const TVwsViewId& aPrevViewId, const TUid aCustomMessageId, const TDesC8& aCustomMessage)
  {
  TListDetailsDnlInfoBuf bb;
  if (aCustomMessageId==KUidDisplaySpecificEntry)
    {
    bb.Copy(aCustomMessage);
    iSavedParams.Copy(aCustomMessage);
    }
  else
    {
    bb.Copy(iSavedParams);
    }
  TBuf<128>buf;
  iEikonEnv->Format128(buf,R_STR_ACTIVATING, bb().iItemIndex);
  iEikonEnv->InfoMsg(buf);
  }

This solves the problem but it is not a particularly elegant solution for the majority of applications.

Pro tip:

If an application publishes the message IDs and parameters with the intention of allowing other applications to run the view, then it must have functionality similar to that described above. In contrast, if a view remains private to an application, then an alternative approach is possible.

MultiView4 Example

MultiView4 demonstrates an alternative approach that you can use if the detail view is private to your application. In MultiView4 an external object, the engine, stores which item from the list is selected. The view switch code reduces to the following code fragment:

void CAppSpecificListView::HandleListBoxEventL(CQikListBox* aListBox, TQikListBoxEvent aEventType, TInt aItemIndex, TInt aSlotId)
  {
  switch (aEventType)
    {
  case EEventItemConfirmed:
  case EEventItemTapped:
    iEngine->SetListViewIndex(aItemIndex);
    iQikAppUi.ActivateViewL(KViewIdDetailsView);
    break;
 
  default:
    break;
    }
  }

The equivalent ViewActivatedL() method of the details view would look something like this:

void CAppSpecificDetailsView::ViewActivatedL(const TVwsViewId& aPrevViewId, const TUid aCustomMessageId, const TDesC8& aCustomMessage)
  {
  TBuf<128>buf;
  iEikonEnv->Format128(buf,R_STR_ACTIVATING, iEngine->ListViewIndex());
  iEikonEnv->InfoMsg(buf);
  }

This has considerably simplified the application; we have removed one object, TListDetailsDnlInfo, and have dispensed with the packing and unpacking of that object. There are a number of other advantages with this approach. For example, since the engine knows which item is being referenced, we do not have to pass an index value to get at the details of the current entry. In our example this only saves a single parameter to a single function but when this pattern is repeated numerous times the cumulative effect can be quite substantial.

An additional benefit that may be utilized concerns the amount of memory required to maintain application state. For example, since we now store the selected item index within the engine, when the list view is ViewDeactivated() we can reset the list box and release all the memory used to store duplicate copies of the list entry names. When the list view is ViewActivatedL(), we can reconstruct the list box including setting the currently selected item, exactly as though the list box had remained untouched. The MultiView4 application takes advantage of this benefit as the time taken to rebuild the list box when the list view is ViewActivatedL() is negligible.

Pro tip:

The MultiView4 application is typical of many applications. An engine supplies the content to be displayed and the UI displays the content. In addition to the actual data, it is reasonable for the engine to contain functionality or information that assists a particular UI component, in this case a value representing the currently selected list item. Pushing functionality into the engine has numerous advantages. For example, it is much easier to write automatic test code to exercise the engine, and, should you choose to port your application, the more information and processing that you can remove from phone-specific U,I the easier the task will be.

Saving Modified Content

In views that allow a user to modify data, the user must be able to save or cancel those modifications. The view framework defines a virtual SaveL() method that should be implemented by views that wish to save any modifications.

The SaveL() method is called when a view is deactivated:

  • by switching to the application launcher or task manager.
  • by calling the CQkViewBase::ActivatePreviousViewL(ESave) method
  • by calling the CQkViewBase::SaveThenDnlToL() method
  • by a call to CCoeAppUi::ActivateViewL() method.

The SaveL() method is not called if the CQikViewBase::ActivatePreviousViewL(ECancel) method is called. While that method can be called directly, the framework processing of the EQikCmdGoBack command calls this method on your behalf. If you have unsaved data, your application is responsible for prompting the user about unsaved changes.

The MultiView4 example shows the SaveL() method in action:

void CAppSpecificDetailsView::SaveL()
  {
  TAppSpecificEntity entry(iEngine->CurrentListItem());
  CEikChoiceList* cl=LocateControlByUniqueHandle<CEikChoiceList>(EAppChoiceList1);
  entry.iState=(TAppEntityState)cl->CurrentItem();
 
  CQikNumberEditor* numEd = LocateControlByUniqueHandle<CQikNumberEditor>(EAppEdwin1);
  entry.iPoints=numEd->Value();
  CQikSlider* sl = LocateControlByUniqueHandle<CQikSlider>(EAppSlider1);
  entry.iOddsOfWinning=sl->CurrentValue();
 
  iEngine->UpdateCurrentListItem(entry);
  }

MultiView4 has a typical HandleCommandL() method:

void CAppSpecificDetailsView::HandleCommandL(CQikCommand& aCommand)
  {
  switch (aCommand.Id())
    {
  case EAppCmdSave:
    ActivatePreviousViewL(ESave);
    break;
 
  case EQikCmdGoBack:
    if (DetailsHaveChanged())
      {
      CQikSaveChangesDialog::TExitOption ret = CQikSaveChangesDialog::RunDlgLD();
      if (ret==CQikSaveChangesDialog::EShut)
        break;
      if (ret==CQikSaveChangesDialog::ESave)
        {
        ActivatePreviousViewL(ESave);
        break;
        }
      }
 
    // FALL through to default:
  default:     // For example the back button...
    CQikViewBase::HandleCommandL(aCommand);
    break;
    }
  }

The framework code provides a standard dialog, CQikSaveChangesDialog, which you can use to prompt the user to save or cancel changes. This dialog can return one of three values:

  • CQikSaveChangesDialog::EShut, the system closed the dialog, for example due to an incoming phone call. Your application should remain displaying the current view.
  • CQikSaveChangesDialog::ESave, the user wants to save any changes and continue with the view change.
  • CQikSaveChangesDialog::ECancel, the user does not want to save the changes; however, they do want to continue with the view change.
Pro tip:

The example code only deals with the first two cases above. The third case, CQikSaveChangesDialog::ECancel, is dealt with by the framework. We simply fall through the switch statement to the default case. This may be contrary to your preferred coding standards. It saves code but some argue that it is less maintainable. We use it often, provided that it is commented to clearly identify which case label the fall-through should go to. You can see this in the example code.

Views and Dialogs

In a classical desktop GUI application you would typically display views of application data and present dialogs to perform actions on the data. On a desktop screen, dialog boxes can be large and contain many fields and controls.

Mobile phones have small screens, so we often replace the dialog with a separate full screen view, a details view. You may think that there is little difference between a view and a full screen dialog; both present information to a user and both allow the data to be manipulated. To a large extent this is true but at a detailed level there are some differences between views and dialogs within UIQ 3.

Dialogs in UIQ

The CEikDialog class

Those familiar with other versions of Symbian OS, such as UIQ 2 have almost certainly used the CEikDialog class to present dialogs to the user. Although the CEikDialog class has been upgraded to support the UIQ 3 command processing framework, it is deprecated. It should not be used because it may be removed from UIQ 3 in the future. We therefore recommend that if possible you phase out the usage of this class from your applications.

If you use any of the standard system supplied dialogs that subclass CEikDialog we recommend that you continue to use these dialogs as opposed to implementing your own versions of these dialogs.

The CQikSimpleDialog class

Dialogs are classified as simple if they can display all of their controls on a single page and do not require tabs or scrolling.

Like regular views, simple dialogs can have different layouts and command sets for different screen modes. Your resource file defines the layouts and command sets.

Pro tip:

There are a number of issues with CQikSimpleDialog in UIQ 3 which have been resolved for UIQ 3.1. In particular, attempting to run a CQikSimpleDialog asynchronously is not possible in UIQ 3 as there is no mechanism to cancel the asynchronous dialog. In UIQ 3.1 a new method has been added to the CQikSimpleDialog class called CancelNonWaitingDialog() and this allows it to be cancelled. While deprecated, the CEikDialog does fully support asynchronous or non waiting dialogs.

The CQikViewDialog class

Dialogs that do not fit into the simple classification, say, ones that have a significant number of controls requiring the display to scroll or have a number of tabs worth of controls, need to be implemented with the CQikViewDialog class as opposed to the CQikSimpleDialog class.

Converting CEikDialog to CQikSimpleDialog

Provided your CEikDialog is not too complex, it is simple to convert it to a CQikSimpleDialog. For example, a CEikDialog derived About dialog may use the following resource:

RESOURCE QIK_COMMAND_LIST r_continue_command_list
  {
  items=
    {
    QIK_COMMAND
      {
      id = EAppCmdContinue;
      type = EQikCommandTypeDone;
      text = "Continue";
      }
    };
  }
 
RESOURCE DIALOG r_about_dialog
  {
  title="About Commands 1";
  flags=EEikDialogFlagWait;
  command_list=r_continue_command_list;
  items=
    {
    DLG_LINE
      {
      type=EEikCtLabel;
      id=1;
      control=LABEL
        {
        txt="";
        standard_font=EEikLabelFontAnnotation;
        };
      },
    DLG_LINE
      {
      type=EEikCtLabel;
      id=2;
      control=LABEL
        {
        txt="© ZingMagic Limited";
        standard_font=EEikLabelFontAnnotation;
        };
      },
    DLG_LINE
      {
      type=EEikCtLabel;
      id=3;
      control=LABEL
        {
        txt="www.zingmagic.com";
        standard_font=EEikLabelFontAnnotation;
        };
      }
    };
  }

The corresponding CQikSimpleDialog resource is:

RESOURCE QIK_DIALOG r_about_dialog
  {
  title = "About Commands 1";
  configurations =
    {
    QIK_DIALOG_CONFIGURATION
      {
      ui_config_mode = KQikPenStyleTouchPortrait;
      container = r_about_container;
      command_list = r_about_commands;
      },
    QIK_DIALOG_CONFIGURATION
      {
      ui_config_mode = KQikPenStyleTouchLandscape;
      container = r_about_container;
      command_list = r_about_commands;
      },
    QIK_DIALOG_CONFIGURATION
      {
      ui_config_mode = KQikSoftkeyStyleTouchPortrait;
      container = r_about_container;
      command_list = r_about_commands;
      },
    QIK_DIALOG_CONFIGURATION
      {
      ui_config_mode = KQikSoftkeyStylePortrait;
      container = r_about_container;
      command_list = r_about_commands;
      },
    QIK_DIALOG_CONFIGURATION
      {
      ui_config_mode = KQikSoftkeyStyleSmallPortrait;
      container = r_about_container;
      command_list = r_about_commands;
      }
    };
  }
 
RESOURCE QIK_COMMAND_LIST r_about_commands
  {
  items=
    {
    QIK_COMMAND
      {
      id = EAppCmdContinue;
      type = EQikCommandTypeDone;
      text = "Continue";
      }
    };
  }
 
RESOURCE QIK_SCROLLABLE_CONTAINER_SETTINGS r_about_container
  {
  controls =
    {
 
    QIK_CONTAINER_ITEM_CD_LI
      {
      type = EQikCtOnelineBuildingBlock;
      control = QIK_SYSTEM_BUILDING_BLOCK
        {
        content =
          {
          QIK_SLOT_CONTENT_DIRECT
            {
            slot_id = EQikItemSlot1;
            type = EEikCtLabel;
            unique_handle = EAppLabel1;
            control = LABEL
              {
              standard_font = EEikLabelFontAnnotation;
              horiz_align = EEikLabelAlignHCenter;
              txt = "";
              };
            }
          };
        };
      },
 
    QIK_CONTAINER_ITEM_CD_LI
      {
      type = EQikCtOnelineBuildingBlock;
      control = QIK_SYSTEM_BUILDING_BLOCK
        {
        content =
          {
          QIK_SLOT_CONTENT_DIRECT
            {
            slot_id = EQikItemSlot1;
            type = EEikCtLabel;
            control = LABEL
              {
              standard_font = EEikLabelFontAnnotation;
              horiz_align = EEikLabelAlignHCenter;
              txt = "© ZingMagic Limited";
              };
            }
          };
        };
      },
 
    QIK_CONTAINER_ITEM_CD_LI
      {
      type = EQikCtOnelineBuildingBlock;
      control = QIK_SYSTEM_BUILDING_BLOCK
        {
        content =
          {
          QIK_SLOT_CONTENT_DIRECT
            {
            slot_id = EQikItemSlot1;
            type = EEikCtLabel;
            control = LABEL
              {
              standard_font = EEikLabelFontAnnotation;
              horiz_align = EEikLabelAlignHCenter;
              txt = "www.zingmagic.com";
              };
            }
          };
        };
      }
    };
  }

The CEikDialog derived class used to present the About dialog is:

class CAboutDialog : public CEikDialog
  {
 protected:
  void PreLayoutDynInitL();
  };
 
const TInt KAppMajorVersion=1;
const TInt KAppMinorVersion=0;
const TInt KAppBuild=2;
 
void CAboutDialog::PreLayoutDynInitL(void)
  {
  TBuf<128>bb;
  iEikonEnv->Format128(bb,R_STR_VERSION, KAppMajorVersion,KAppMinorVersion,KAppBuild);
  static_cast<CEikLabel*>(Control(EAppLabel1))->SetTextL(bb);   
  }
 

Whereas the CQikSimpleDialog derived class used to present the About dialog is:

class CAboutDialog : public CQikSimpleDialog
  {
 protected:
  void PreLayoutDynInitL();
  };
 
const TInt KAppMajorVersion=1;
const TInt KAppMinorVersion=0;
const TInt KAppBuild=2;
 
void CAboutDialog::PreLayoutDynInitL()
  {
  TBuf<128>bb;
  iEikonEnv->Format128(bb,R_STR_VERSION, KAppMajorVersion,KAppMinorVersion,KAppBuild);
  CEikLabel* lbl = LocateControlByUniqueHandle<CEikLabel>(EAppLabel1);
  lbl->SetTextL(bb);
  }

There are a number of differences in the resource but the code is practically identical.

Views as Dialogs

One of the key differences between a modal dialog and a view is that the code execution is synchronous for a modal dialog and asynchronous for a view.

Comparing modal dialog execution with view execution

Figure 6 Comparing modal dialog execution with view execution

When a modal dialog is started, the calling code stops, the modal dialog runs to completion and then returns to the requesting code. Data can be passed back directly to the original calling code.

In contrast, when a view change request is processed, the code making the request continues to run. The requested view is displayed at some time in the future, meaning that code after the view change request will be run and there is no direct correspondence between the calling code and the new view. The new view has no direct means of returning data back to the original view. Such information has to be communicated through a component such as an engine. In essence, views act as modeless dialogs.

In pseudo-code, switching between views goes like this:

  CActiveScheduler::RunL()
  CCoeAppUi::HandleWsEventL()
  ListView:: CommandEntryPoint();
  ActivateViewL(DetailsView);
  ListView:: CommandExitPoint();
       …
  CActiveScheduler::RunL()
  CQikViewBase::HandleViewActivatedEvent()
  DetailsView::ViewActivatedL()

In contrast, when we display a modal dialog, code execution goes along the lines of:

  CActiveScheduler::RunL()
  CCoeAppUi::HandleWsEventL()
  ListView:: CommandEntryPoint();
    Dialog::ExecuteLD();
    Dialog::PreLayoutDynInit();
    Dialog::ExitDialog();
  ListView:: CommandExitPoint();

Recognizing that it is often much easier for application developers to handle the synchronous code flow allowed by the dialog type approach, UIQ 3 supports a hybrid view and dialog. View dialogs combine the ability to support multiple configurations (from views) with the convenience of synchronous code flow (from dialogs).

MultiView4 contains an example of such as view dialog:

class CDetailsAsAViewDialog : public CQikViewDialog
  {
 protected:
 
  // From CQikViewBase.
  void HandleCommandL(CQikCommand& aCommand);
  void ViewActivatedL(const TVwsViewId& aPrevViewId, const TUid aCustomMessageId, const TDesC8& aCustomMessage);
  void ViewConstructL();
  void SaveL();
 
 public:
  TInt RunDialogLD(CAppEngine* aEngine);
 
 protected:
  CAppEngine* iEngine;
  };

The class derives from a CQikViewDialog which in turn derives from the CQikMultiPageViewBase, thus ultimately CQikViewBase. Most of the methods should be familiar to you; the only new one is:

TInt CDetailsAsAViewDialog::RunDialogLD(CAppEngine* aEngine)
  {
  iEngine=aEngine;
  return(ExecuteLD());
  }

The resources used to define the CQikViewDialog are identical to those we use when we display the details in a separate view.

The primary difference between regular views and CQikViewDialog derived views relates to how they are displayed and removed.

A regular view is displayed and removed through a called to iQikAppUi.ActivateViewL(); with varying parameters. The CQikViewDialog is displayed with a call to CQikViewDialog::RunLD or CQikViewDialog::ExecuteLD, and is removed though a call to CQikViewDialog::CloseDialog().

The following code fragment shows the list view running a CQikViewDialog:

void CAppSpecificListView::HandleCommandL(CQikCommand& aCommand)
  {
  switch (aCommand.Id())
    {
    case EAppCmdDetails:  
      {
      CDetailsAsAViewDialog* q = new(ELeave)CDetailsAsAViewDialog;
      q->RunDialogLD(iEngine);
      break;
      }
 
    default:
      CQikViewBase::HandleCommandL(aCommand);
      break;
    }
  }

Whereas the following code performs a regular view switch:

void CAppSpecificListView::HandleListBoxEventL(
  CQikListBox* aListBox,
  TQikListBoxEvent aEventType,
  TInt aItemIndex,
  TInt aSlotId)
  {
  switch (aEventType)
    {
  case EEventItemConfirmed:
  case EEventItemTapped:
    iQikAppUi.ActivateViewL(KViewIdDetailsView);
    break;
  default:
    break;
    }
  }

If you run the MultiView4 application there are no discernible differences in the content displayed or interactions supported between a regular view and a CQikViewDialog.

Code

Code for this chapter

ViewsAndDialogs_code.zip
Personal tools
code download