Commands and categories

From UIQ Books

Jump to: navigation, search

Contents

Commands & Categories

The UIQ 3 Command Processing Framework (CPF) maps your application’s commands to hardware keys, softkeys and menus. In this chapter we show you how to add commands to your application. The Commands1 example application demonstrates how to use the CPF in your application.

Categories are used to subdivide data such as Work and Personal appointments in the Agenda application. Categories are linked to commands because category selection and management are handled via the menu. The Categories example application shows how you can start to use categories in your application.

Commands Overview

The functional specification for your application should define the set of commands that the user can perform, for example, the commands Add entry and Delete entry. Your specification may go further and define how the user interacts with the application to perform these commands. You might specify that a menu option entitled Add entry should be placed within the Edit menu.

Mobile phones have very particular interaction styles based on the hardware, operating system and manufacturer preferences. This has an especially large impact on command processing because commands are actioned by pressing buttons and, if available, by interaction with a touchscreen.

UIQ 3 supports a variety of different interaction styles and allows applications to support the whole range of styles from a single executable. You must therefore avoid trying to define a single way for the user to interact with your application. UIQ 3 has a CPF that is designed to map application commands on to the available hard and softkeys and the menu.

The concepts behind the CPF are described in the Chapter UIQ 3 basics.

We can break command handling into two parts:

  • presenting a command to the user via some kind of UI component
  • processing the command.

When processing a command, we do not need to worry about how the command was invoked but only that it was invoked. A command identifier, usually in the form of an integer value, is delivered from the command presentation code to the command processing code.

On the other hand, presenting commands to a user has become somewhat harder since what is correct for one UI style is not always correct or even possible for a different UI style.

When we present commands to the user, we must also consider the following factors:

  • is the command valid given the current application state?
  • how does the user cause the command to occur?
  • how frequently is the command likely to be required?
  • how important is the command?

For the purpose of presenting commands, UIQ 3 phones are classified as:

  • Pen Style, there is a menu available and optionally a button bar that can contain commands. A limited set of other keys may also be available. The screen is touch sensitive.
  • Softkey Style, there is a menu available and softkey area normally displayed directly above a set of two or three keys, usually at the bottom of the screen. A limited set of other keys may also be available. The screen is not touch sensitive.
  • Softkey Style Touch, which presents commands in the same way as Softkey Style, but also allows them to be selected using the touchscreen. In practice, this is the same as Softkey Style when you are defining commands in your application.

For an optimal user interface, a mechanism of dynamically allocating commands to the menu, buttons, softkeys and other keys available on a particular phone is required, dependent on the phone type. On the other hand a professional quality application needs to maintain a degree of control of where commands are positioned, how they are grouped together, how they are separated and in what order they are presented to the user to aid in application usability. For example, if your application supported the Cut, Copy, Paste and Exit commands but these were presented in the order Copy, Exit, Paste and Cut then users familiar with such commands would find the application more difficult to operate.

The UIQ 3 solution to these conflicting requirements is to associate a set of commands with each view configuration you define. Each command is given a set of attributes that inform the framework how to present the command. Different attributes can be associated with a particular command dependent on the configuration.

The following section describes how UIQ 3 implements commands by way of an example application.

Commands1 Example Application

Commands1 is a two-page application in which each page contains a single list box. The list boxes are single line, text only. To demonstrate using commands, we implement the ability to add, delete and sort entries.

The example application also demonstrates numerous aspects of the CPF including:

  • defining commands on a view wide basis with resources
  • defining commands on a page specific basis with resources
  • adding and removing commands in software instead of using resources
  • processing commands within an application
  • configuring command state dependent on application state.

Page Independent Commands

Commands1.rss shows a set of view configurations. Here is one of them:

QIK_VIEW_CONFIGURATION
  {
  ui_config_mode = KQikPenStyleTouchPortrait;
  command_list = r_list_view_generic_commands;
  view = r_list_view_view;
  }

We specify r_list_view_generic_commands to define a set of commands that are available to the user irrespective of the page that is displayed within a particular view configuration.

The r_list_view_generic_commands resource looks like this:

RESOURCE QIK_COMMAND_LIST r_list_view_generic_commands
  {
  items=
    {
    QIK_COMMAND
      {
      id = EAppCmdHelp;
      type = EQikCommandTypeHelp;
      groupId = EAppCmdMiscGroup;
      priority = EAppCmdHelpPriority;
      text = "Help";
      },
 
    QIK_COMMAND
      {
      id = EAppCmdAbout;
      type = EQikCommandTypeScreen;
      groupId = EAppCmdMiscGroup;
      priority = EAppCmdAboutPriority;
      text = "About";
      },
 
    QIK_COMMAND
      {
      id = EAppCmdDebugTest;
      type = EQikCommandTypeScreen;
      stateFlags = EQikCmdFlagDebugOnly;
      groupId = EAppCmdMiscGroup;
      priority = EAppCmdDebugTestPriority;
      text = "Test";
      },
 
    QIK_COMMAND
      {
      id = EAppCmdDelete;
      type = EQikCommandTypeDelete;
      text = "";
      }
    };
  }

When we run Commands1, we see that the Help, About and Test commands have been added to the menu in both Softkey Style Touch and Pen Style UI configurations.

Commands1 Page one in Softkey Style Touch configuration Commands1 Page one in Pen Style UI configuration

Figure 1 Commands1 Page one in Softkey Style Touch and Pen Style UI configurations

The Delete command, EAppCmdDelete, is allocated to the Delete key.

Page Specific Commands

When we look at a page definition resource we can see the following piece of code:

QIK_VIEW_PAGE
  {
  page_id = EAppSpecificListViewPageId2;
  tab_bmpid = EMbmCommands1Tab2;
  tab_bmpmaskid = EMbmCommands1Tab2mask;
  page_content = r_list_view_page2_control;
  command_list = r_page2_specific_commands;
  }

As suggested by the name r_page2_specific_commands, we are able to define a set of commands that are only available when a particular page is selected; no external software is required, only a resource element.

RESOURCE QIK_COMMAND_LIST r_page2_specific_commands
  {
  items=
    {
    QIK_COMMAND
      {
      id = EAppCmdAdd;
      type = EQikCommandTypeScreen;
      groupId = EAppCmdEditGroup;
      priority = EAppCmdAddPriority;
      text = "Add";
      },
 
    QIK_COMMAND
      {
      id = EAppCmdDelete2;
      type = EQikCommandTypeScreen;
      groupId = EAppCmdEditGroup;
      priority = EAppCmdDeletePriority;
      text = "Delete";
      }
    };
  }

If you run the application, you can observe how the generic and page specific commands are combined to produce the application command set for each page. No application-specific software is required to achieve this. All the work is performed by the framework code.

In page two of Commands1, we can now see the Add and Delete commands in the menu:

Commands1 Page two in Softkey Style Touch configuration Commands1 Page two in Pen Style configuration

Figure 2 Commands1 Page two in Softkey Style Touch and Pen Style configurations

The command EAppCmdDelete2 is adding a second delete command delivered by a menu (see page two). In contrast EAppCmdDelete is delivered by the Delete key press.

Your application may not need to distinguish between how a user selects a specific command. Our example application simply demonstrates how you can achieve this if required.

Item-Specific Commands

This type of command is associated with the control that currently has focus. For example, if a phone number is highlighted, the first choice action may be make a call while a second action could be send an SMS.

In 3SK&B mode, the CPF attempts to place these commands like this:

  • Center softkey
  • Left softkey (if no Done command)
  • Right softkey
  • in the menu (no more softkeys available).

Command Definition in Detail

A QIK_COMMAND_LIST comprises a set of QIK_COMMAND elements. An individual command is defined by the QIK_COMMAND resource:

STRUCT QIK_COMMAND
  {
  BYTE version=3;
  LONG id=EQikCmdUnassigned;
  LONG type;
  LONG groupId=0;
  LONG namedGroupLinkId=0;
  LONG namedGroupId=0;
  LONG priority=0;
  LTEXT text;
  LTEXT shortText="";
  LLINK icon=0;
  LONG stateFlags=0;
  LONG cpfFlags=0;
  }

Version

This is internal to UIQ 3 and should not be changed by the developer.

ID

Set this field to contain the command identifier that you wish to deliver to the command processing code. Command identifiers are usually defined as a set of enums within the application specific HRH file. For the Commands1 example, the following enums are defined:

enum TAppCommandIds
  {
  EAppCmdAbout=0x1000,     // Below 0x1000 reserved for UIQ 3.
  EAppCmdHelp,
  EAppCmdContinue,
  EAppCmdAdd,
  EAppCmdAdd2,
  EAppCmdDelete,
  EAppCmdDelete2,
  EAppCmdDelete3,
  EAppCmdSortCascade,
  EAppCmdSortType1,
  EAppCmdSortType2,
  EAppCmdSortType3,
  EAppCmdSeparator,
  EAppCmdSortOrder,
  EAppCmdSortAltType1,
  EAppCmdSortAltType2,
  EAppCmdDebugTest,
 
  EAppCmdLastItem
  };

You may note that some commands appear to be duplicated, such as EAppCmdAdd and EAppCmdAdd2. It is possible to define more than one way in which a user may request a particular operation; for example, a command might exist both in a menu and on a key press. Even though the same functionality is requested by the user, the CPF needs to distinguish between each QIK_COMMAND. As an alternative to duplicating commands the CPF flag EQikCpfFlagDuplicateInMenuPane, described later, may be more appropriate.

Type

This is perhaps the most important field and is the most influential one in determining where the command is displayed within a particular UI style. This field categorizes a command into one of the following types:

enum TQikCommandType
  {
  EQikCommandTypeScreen,
  EQikCommandTypeItem,
  EQikCommandTypeYes,
  EQikCommandTypeNo,
  EQikCommandTypeDone,
  EQikCommandTypeDelete,
  EQikCommandTypeCancel,
  EQikCommandTypeHelp,
  EQikCommandTypeSystem,
  EQikCommandTypeOperator,
  EQikCommandTypeFep,
  EQikCommandTypeCategory
  };

Since the objective is to allow individual phones to assign commands to the range of controls available on a specific phone, you specify your commands in abstract terms rather than stating where they appear in the UI. The descriptions below apply to generic UIQ 3.0 3SK&B as presented by the emulator. Real mobile phones may display different behavior: however, as long as you follow the guidelines and describe your commands using the UIQ command definitions, you do not need to worry too much about where they are displayed because the CPF will take care of that.

EQikCommandTypeScreen

Perhaps the most common type, this is used to define commands that are applicable to the entire view or dialog. This is invariably used when the command does not belong to any other command type. Such commands are typically displayed in a menu.

EQikCommandTypeItem

This defines commands that are only available for the control that has focus. For example, if focus is placed on a text string that represents a phone number, then item commands Call and SMS could be displayed. If the next item represents an email address, then a Send Mail command could be displayed.

Controls add and remove their commands in their PrepareForFocusGainL and PrepareForFocusLoss methods. The CQikCommandManager uses CCoeControl::Parent() to find out which command list the control’s commands should be added to.

EQikCommandTypeYes

This indicates that the command is a positive response to a question presented by the UI. This command type is normally used in conjunction with EQikCommandTypeNo. When both command types are present, the CPF attempts to present them within the same container in the UI, with the EQikCommandTypeYes to the left and EQikCommandTypeNo to the right.

Confirmation dialog example

Figure 3 Confirmation dialog example

EQikCommandTypeNo

This indicates that the command is a negative response to a question presented by the UI. This command type is normally used in conjunction with EQikCommandTypeYes as described above.

EQikCommandTypeDone

This command type represents the positive completion of an action. Positive actions are usually placed towards the left. For example, in a typical softkey style phone, the command with type EQikCommandTypeDone is placed on the Left softkey. If the Center softkey is empty, the command is moved there. You can use the EQikCpfFlagDoneCommandPreferToBePlacedOnPositiveSoftkey flag, described later, to indicate you prefer the command to be placed on the Left softkey.

Commands of type EQikCommandTypeDone and EQikCommandTypeCancel are frequently used as a pair. Similarly, commands of type EQikCommandTypeYes and EQikCommandTypeNo are frequently used as a pair. You should not mix the two command pairings.

EQikCommandTypeCancel

This indicates that the command represents the cancelling of an action. There is typically only a single command of type EQikCommandTypeCancel per view or dialog. If a specific phone has a Back key, the first command of type EQikCommandTypeCancel is assigned to that key. On Pen Style phones this command is usually placed to the right of any EQikCommandTypeDone.

One feature of the CPF is that if a view or dialog does not define a command of type EQikCommandTypeCancel then the CPF will automatically add one, irrespective of whether the application wants a Cancel command. Applications need to explicitly remove the command type if it is not required. We show how to do this later on.

EQikCommandTypeDelete

The Delete type causes a backwards or a delete type action to occur. Commands such as Undo, Take back, Clear, Remove and Delete would usually be assigned this type. The first command of this type is associated with any Clear key available on the phone. Subsequent commands of this type are placed in the menu.

QIK_COMMAND
  {
  id = EAppCmdDelete;
  type = EQikCommandTypeDelete;
  text = "";
  }

We have only defined a single command of type EQikCommandTypeDelete in our command set. This is assigned to the Clear key in the emulator and currently available that phones but, in general, it is not advisable to assume this would happen on every UIQ 3 phone. In a production quality application you should ensure there is at least one other mechanism that performs this command. You can see this in Commands1: page one has one Delete command assigned to the Clear key, page two adds an alternative Delete command which is placed in the menu.

EQikCommandTypeHelp

This indicates that the command is used to display some context sensitive help. Commands of this type only differ from EQikCommandTypeScreen commands in the position the framework assigns the command within a menu. The framework will place commands of this type in a consistent position across all applications.

QIK_COMMAND
  {
  id = EAppCmdHelp;
  type = EQikCommandTypeHelp;
  groupId = EAppCmdMiscGroup;
  priority = EAppCmdHelpPriority;
  text = "Help";
  }

You should note that command types act as a first order sort key for commands. The priority acts as a second order sort key.

EQikCommandTypeSystem

Occasionally commands need to be added by the system to perform certain actions. For example, on some Sony Ericsson phones, a task manager command is added to the set of commands presented by the menu bar. In general applications should not use commands of this type.

EQikCommandTypeOperator

Commands can be added when an application is interacting with various operator services. Applications should not use commands of this type.

EQikCommandTypeFep

Commands can be added by the current front end processor (FEP) to allow for configuration of the FEP. Applications should not use commands of this type.

EQikCommandTypeCategory

Commands of this type should be associated with the category management presented by the application. On Softkey Style phones these commands are presented in a cascade menu item; on Pen- Style phone these commands are placed in the category drop-down menu. We discuss categories in more detail later on.

Category menu in Softkey Style Touch configuration Category menu in Softkey Pen Style UI configuration

Figure 4 Category menu in Softkey Style Touch and Pen Style UI configurations

Command types offer a high degree of flexibility when constructing your UI. With flexibility comes responsibility. The framework will not prevent you defining all your commands as one particular type should you wish, however such an action is unlikely to result in an acceptable UI for your application across all phones.

Pro tip:

We recommend that you determine which commands should be assigned to the single controls such as the Back and Clear keys, then which commands should be associated with any softkey area or button bar. The remaining commands form the menu and are therefore assigned the EQikCommandTypeScreen type.

groupId

Often a set of commands is related to each other. To indicate this you can assign commands the same group ID from an application-specific enum list.

In our example we have defined the following groups:

enum TAppCommandGroupIds
  {
  EAppCmdEditGroup,
  EAppCmdSortGroup,
  EAppCmdSortCmdsLink,
  EAppCmdMiscGroup,
  EAppCmdLastGroupId
  };

We have identified three logical groups of commands: those associated with editing, those associated with sorting and a miscellaneous group containing the remaining commands.

The CPF will display those items with the same group together. If there are other commands in the menu, the group is surrounded by dividers. A groupId value defines the order in which groups of commands are presented. We have not yet specified any order for the commands within the group; we use the priority field to do that.

There are two types of groups, so called unnamed (or anonymous) and named. The groupId field represents unnamed groups.

For example, in Commands1 page two we define three groups of commands:

EAppCmdEditGroup EAppCmdSortGroup EAppCmdMiscGroup
Add Sort type 1 About
Delete Sort type 2

These are displayed in a menu like this:

Commands1 page two menu

Figure 5 Commands1 page two menu

namedGroupId

We use namedGroupId to create a named group. This field contains the named group ID value. In contrast to unnamed groups, which are only displayed within menu panes, named groups can be presented in sub-menus, on the softkeys or on button bars depending on other command attributes. In our example we use the namedGroupId to display the sort commands within a cascade sub-menu:

Commands1 Page one sort sub-menu

Figure 6 Commands1 Page one sort sub-menu

namedGroupLinkId

Commands belonging to a named group need to be linked back to a command from an unnamed group. For example, if a named group is displayed as a sub-menu the namedGroupLinkId field of the command would be set to the same value as the namedGroupId. The following resource fragment helps explain this further.

    QIK_COMMAND
       {
       namedGroupLinkId = EAppCmdSortCmdsLink;
       priority = EAppCmdSortCascadePriority;
       text = "Sort";
       },
 
    QIK_COMMAND
       {
       namedGroupId = EAppCmdSortCmdsLink;
       priority = EAppCmdSortType1Priority;
       text = "Sort by name";
       },
 
    QIK_COMMAND
       {
       namedGroupId = EAppCmdSortCmdsLink;
       priority = EAppCmdSortType2Priority;
       text = "Sort by 2nd letter";
       },
 
    // Remaining commmands removed for clarity.

The Sort command belongs to an unnamed group. The Sort by name and Sort by 2nd letter commands belong to a named group, EAppCmdSortCmdsLink, which forms the cascade sub-menu.

Using namedGroupId to link a sub-menu

Figure 7 Using namedGroupId to link a sub-menu

Since the Sort command contains a namedGroupLinkId value equal to the namedGroupId (EAppCmdSortCmdsLink), a link between the two sets of commands is established. When the link is followed, a cascade style sub-menu is presented.

While this may seem complicated, it means that you can code once and your menu tree is displayed correctly in each UI configuration.

Pro tip:

You should note that the order in which commands are defined within a resource and any indentation are purely cosmetic. We only lay out the commands in this way so as to document our intentions for the end result. It is the values assigned to the group and priority fields that influence the presentation order.

priority

When you have commands of the same type, it is often important to ensure that they are grouped together and displayed in a specific order. For example, Cut, Copy and Paste commands should always be displayed in that order for consistency with other applications. We have already seen how we can group commands together using unnamed or named groups. Within a group, the priority field defines the order of the commands.

Our example defines the current enum list:

enum TAppCommandPriorities
  {
  EAppCmdHelpPriority,
  EAppCmdDebugTestPriority,
  EAppCmdAddPriority,
  EAppCmdDeletePriority,
  EAppCmdSortCascadePriority,
  EAppCmdSortType1Priority,
  EAppCmdSortType2Priority,
  EAppCmdSortType3Priority,
  EAppCmdSeparatorPriority,
  EAppCmdSortOrderPriority,
  EAppCmdAboutPriority,
 
  EAppCmdLastPriority
  };
Sort sub-menu

If you compare the priority list with the sort sub-menu image, you should be able to see that EAppCmdSortType1Priority has a lower value and hence higher priority than EAppCmdSortType2Priority. Therefore the Sort by name command is displayed before Sort by 2nd letter within the named group EAppCmdSortCmdsLink.

Pro tip:

You should also consult the UIQ 3 Style Guide document in the UIQ 3 SDK to ensure applications use the group and priority attributes to present commands in the recommended groups and order within menus.

text

If you want the command to be displayed as text, supply the text content here.

shortText

If a text command is to be displayed on a UI component that has restricted display space, such as a softkey or on the button bar, the CPF will choose the shortText if the text field is too long.

Icon

As an alternative to displaying commands with text, commands can be displayed as icons or as a combination of icon and text. If the command is placed within a menu, the icon is displayed beside the text. If the command is placed in a button bar the icon is preferred over any text.

Icons in menu

Icons in button bar

Figure 8 Icons in menu and button bar

stateFlags

At any point in time commands can have an associated state, for example they may be unavailable. The state may be visually represented differently in each UI mode

The following state flags are defined:

EQikCmdFlagDimmed

The command is currently dimmed. A dimmed command is displayed to the user but it cannot be activated. Your application should not use this flag directly. Rather it would make the command unavailable. The phone specific configuration would then apply its rules as to whether the configuration dims or hides unavailable commands.

EQikCmdFlagInvisible

The command is not displayed. As with EQikCmdFlagDimmed an application should not use this flag directly.

EQikCmdFlagUnavailable

This indicates that a command is unavailable. On Softkey Style phones this usually makes the command invisible; in Pen Style phones it usually dims the command. In our example we have chosen to restrict the number of items displayed within two bounds. To achieve this we make the commands to create or delete entries unavailable at those boundaries.

  CQikCommandManager& cm=CQikCommandManager::Static();
  CQikListBox* listbox = LocateControlByUniqueHandle<CQikListBox>(EAppSpecificListViewListId1);
  TBool avail=EFalse;
  if (listbox->ItemCount()>=KCommand1Items)
    avail=ETrue;
  cm.SetAvailable(*this,EAppCmdDelete3,avail);
 
  avail=EFalse;
  if (listbox->ItemCount()<KCommand1Items)
    avail=ETrue;
  cm.SetAvailable(*this,EAppCmdAdd2,avail);
Commands1 page one menu Dimmed command
Commands1 page one menu

Figure 9 Commands1 page one menu in Softkey Style Touch and Pen Style configurations

EQikCmdFlagCheckBox

This indicates that the command is shown with an associated check box.

QIK_COMMAND
  {
  id = EAppCmdSortOrder;
  type = EQikCommandTypeScreen;
  stateFlags = EQikCmdFlagCheckBox;
  namedGroupId = EAppCmdSortCmdsLink;
  priority = EAppCmdSortOrderPriority;
  text = "Ascending";
  }

This state flag is usually set within a QIK_COMMAND resource and not manipulated by the application. In contrast the application will normally determine the actual value to be presented at runtime.

Menu command with checkbox

Figure 10 Menu command with checkbox

EQikCmdFlagRadioStart

This indicates that the command is the first in a group of radio buttons. This state flag is usually set within a QIK_COMMAND resource and not manipulated by the application. The application usually determines the value to be presented at runtime.

QIK_COMMAND
  {
  id = EAppCmdSortType1;
  type = EQikCommandTypeScreen;
  stateFlags = EQikCmdFlagRadioStart;
  namedGroupId = EAppCmdSortCmdsLink;
  priority = EAppCmdSortType1Priority;
  text = "Sort by name";
  },

Menu commands with radio buttons

Figure 11 Menu commands with radio buttons

EQikCmdFlagRadioMiddle

This indicates that the command belongs to a group of radio buttons; however, it is neither the first or last in the list. This state flag is usually set within a QIK_COMMAND resource like EQikCmdFlagRadioStart

QIK_COMMAND
  {
  id = EAppCmdSortType2;
  type = EQikCommandTypeScreen;
  stateFlags = EQikCmdFlagRadioMiddle;
  namedGroupId = EAppCmdSortCmdsLink;
  priority = EAppCmdSortType2Priority;
  text = "Sort by 2nd letter";
  },

EQikCmdFlagRadioEnd

This indicates that the command is the last in a group of radio buttons. This state flag is usually set within a QIK_COMMAND resource such as EQikCmdFlagRadioStart.

QIK_COMMAND
  {
  id = EAppCmdSortType3;
  type = EQikCommandTypeScreen;
  stateFlags = EQikCmdFlagRadioEnd;
  namedGroupId = EAppCmdSortCmdsLink;
  priority = EAppCmdSortType3Priority;
  text = "Sort by 3rd letter";
  },

EQikCmdFlagSymbolOn

For a checkbox command, the box will be checked. For a group of radio buttons this indicates the currently chosen option. While the application can set this within a QIK_COMMAND resource, it is more likely the application software needs to set or clear this flag to match any internal meaning. When commands are added to a view the DynInitOrDeleteCommandL() method is called for each command. In that method you would perform something like:

CQikCommand* CAppSpecificListView::DynInitOrDeleteCommandL(
 CQikCommand* aCommand,
 const CCoeControl& aControlAddingCommands)
  {
  TBool val=EFalse;
  switch (aCommand->Id())
    {
 
   // Determine which radio buttons should be checked.
  case EAppCmdSortType1:
    if (iEngine->ListViewSortType()==ESortType1)
      val=ETrue;
    aCommand->SetChecked(val);
    break;
 
    // Determine whether the check box should be checked.
  case EAppCmdSortOrder:
    if (iEngine->ListViewSortOrder() == EAscending)
      val=ETrue;
    aCommand->SetChecked(val);
    break;
 
  default:
    break;
    }
  return(aCommand);
  }

EQikCmdFlagInlinePane

The command is displayed as a section divider containing text.

QIK_COMMAND
  {
  id = EAppCmdSeparator;
  type = EQikCommandTypeScreen;
  stateFlags = EQikCmdFlagInlinePane;
  namedGroupId = EAppCmdSortCmdsLink;
  priority = EAppCmdSeparatorPriority;
  text = "Sort order";
  },

The section divider is not selectable and does not deliver a command. To display section dividers without text, use the group facility.

Section divider with text

Figure 12 Section divider with text

EQikCmdFlagSortAlphabetic

If set indicates commands are sorted alphabetically instead of by command priority.

EQikCmdFlagDebugOnly

This indicates that the command should only be presented in debug builds. UIQ 3 developers recognized there is a requirement to allow application developers to have commands within a UI that would not normally be present in the final application. Prior to UIQ 3, application developers needed to perform some kind of automatic or manual pre-processing of a resource file to include or exclude such commands. UIQ 3 formalizes this process.

cpfFlags

A second set of flags are associated with a command, known as the CPF flags. In general these flags give hints to the framework as to how to prioritize the positioning of commands.

The following flags are defined:

EQikCpfFlagReplaceContainerPopoutDone

Setting this flag prevents any CQikContainerPopouts automatically adding a Done command. This flag is used by custom controls that wish to replace a Done command in any container or pop-out they may be placed within.

EQikCpfFlagPreferToBePlacedInButtonbar

This informs the framework that the command should be placed in any available button bar rather than in the menu.

EQikCpfFlagDuplicateInMenuPane

This informs the framework that the command should be added in the menu pane as well as being present in the softkey or button bar region. It may be preferable to use this flag rather than duplicate a command manually through the use of separate QIK_COMMANDs.

EQikCpfFlagOkToExclude

On mobile phones such as the Sony Ericsson P990i that support two different screen sizes, this flag informs the CPF that it is acceptable to remove this command from the smaller screens. You will need to carefully consider the subset of features that you make available. While this flag allows a single command for multiple view configurations, there is a trade-off between the complexity and maintainability introduced by attempting to run a single command list for all view configurations, compared to a separate command list, one for each view configuration.

EQikCpfFlagIsDefault

This informs the framework that the command is the primary action for the dialog or view and should be mapped to any Confirm key. You should ensure that only a single command has this attribute. When the command is displayed within a button bar, that item will be emphasized.

EQikCpfFlagDoneCommandPreferToBePlacedOnPositiveSoftkey

This informs the CPF that a command should be placed on the Left softkey as opposed to the Center softkey should the Left softkey be unused. This applies only to commands of type EQikCommandTypeDone.

EQikCpfFlagHardwarekeyOnly

This informs the CPF that the command should only be placed on hardware buttons.

EQikCpfFlagTouchscreenOnly

This informs the CPF that the command should be made available only if the current configuration has a touchscreen. As with EQikCpfFlagOkToExclude there is a trade-off between maintaining a single command list and having separate lists one for each view configuration.

EQikCpfFlagNoTouchscreenOnly

This informs the CPF that the command should only be made available if the current configuration does not have a touchscreen.

EQikCpfFlagInteractionMenubarOnly

This informs the CPF that the command should only be made available if the current configuration has a menu bar. This is only applicable when the mobile phone is in a Pen Style UI configuration.

EQikCpfFlagInteractionSoftkeysOnly

This informs the CPF that the command should only be made available if the current configuration has a softkey region. This is the opposite of EQikCpfFlagInteractionMenubarOnly, and only applies for mobile phones in a Softkey Style UI configuration.

EQikCpfFlagPortraitOnly

This informs the CPF that the command should only be made available if the current configuration is in portrait mode.

EQikCpfFlagLandscapeOnly

This informs the CPF that the command should only be made available if the current configuration is in landscape mode.

EQikCpfFlagExecuteRepeat

This requests the CPF repeatedly to issue the command ID while the key to which command is allocated remains depressed.

Summary

The CPF is rich in functionality and command handling can be influenced by using the large number of available parameters. Getting to know these parameters and learning to read QIK_COMMAND resources are the keys to utilizing the functionality.

QIK_COMMAND
  {
  id = EAppCmdDebugTest;
  type = EQikCommandTypeScreen;
  stateFlags = EQikCmdFlagDebugOnly;
  groupId = EAppCmdMiscGroup;
  priority = EAppCmdDebugTestPriority;
  text = "Test";
  },

In this resource definition we have have created a command that:

  • Delivers a command with ID EAppCmdDebugTest to the command processing code.
  • Has a type of EQikCommandTypeScreen so will most likely be displayed in a menu.
  • Is only visible in debug builds due to the EQikCmdFlagDebugOnly stateFlags.
  • Belongs to the unnamed EAppCmdMiscGroup and is therefore displayed with any other commands belonging to that group. In this application the Help and About options also belong to that group of commands.
  • Has been given a priority of EAppCmdDebugTestPriority .
  • Displays the text ‘Test’ to the user.
Pro tip:

If you get a QIKON-PANIC: 39 when attempting to display a view or perhaps running your application, the most likely cause is that you have defined more than one QIK_COMMAND with the same ID value.

Processing Commands

Commands are delivered to the HandleCommandL() method of a view. To process them, you simply override that method and perform the actions required by the indicated command. A typical HandleCommandL() will look like:

void CAppSpecificListView::HandleCommandL(
 CQikCommand& aCommand)
  {
  switch (aCommand.Id())
    {
    case EAppCmdAbout:
     (new(ELeave)CAboutDialog)->ExecuteLD(R_ABOUT_DIALOG);
     break;
 
    case EAppCmdHelp:
     iEikonEnv->InfoWinL(R_HELP_TITLE,R_HELP_TEXT);
     break;
 
    case EAppCmdSortType1:
     iEngine->SetListViewSortType(ESortType1);
     SortListViewL();
     break;
 
    case EAppCmdSortType2:
     iEngine->SetListViewSortType(ESortType2);
     SortListViewL();
     break;
 
    default:           // For example . the back button...
     CQikViewBase::HandleCommandL(aCommand);
     break;
    }
  }

As can be seen from the function prototype, an object of type CQikCommand is delivered to the HandleCommandL() method. This object is the C++ equivalent of the QIK_COMMAND resource. If you view the class definition in QikCommand.h you can see object property that maps directly onto the resource fields.

Command Lifetime

Commands defined in a QIK_VIEW_CONFIGURATION are created at view construction time and remain loaded until the view is deleted, usually when the application terminates. In contrast, commands defined within a QIK_VIEW or QIK_VIEW_PAGE are loaded when the view or page becomes foreground and unloaded when it switches to background. Item type commands are loaded and unloaded as focus moves to and away from a control. Any state information stored with a command will be lost when commands are unloaded.

Applications should present commands that are consistent with their internal data. By defining commands within the QIK_VIEW_CONFIGURATION, it is possible to allow the command framework to maintain any state information. For example, if we define the sort commands as part of the QIK_VIEW_CONFIGURATION command set, we may be able to let the command framework store the sort order information for the lifetime of the application. However, we have chosen to define the sort commands within a QIK_VIEW_PAGE. When we swap pages, the commands are unloaded and the state information is lost. If we return to the page of sorted items, it is desirable that the menu options are consistent with sort order of the displayed list box items. To achieve this, we record the sort order state information when it is changed:

void CAppSpecificListView::HandleCommandL(
 CQikCommand& aCommand)
  {
  switch (aCommand.Id())
    {
    case EAppCmdSortType1:
     iEngine->SetListViewSortType(ESortType1);
     SortList1L();
     break;
 
    default:            // For example  the back button...
     CQikViewBase::HandleCommandL(aCommand);
     break;
    }
  }

The command state is then set when commands are loaded:

CQikCommand* CAppSpecificListView::DynInitOrDeleteCommandL(
 CQikCommand* aCommand,
  const CCoeControl& aControlAddingCommands)
  {
  TBool val=EFalse;
  switch (aCommand->Id())
    {
 
    // Determine which radio buttons should be checked.
  case EAppCmdSortType1:
    if (iEngine->ListViewSortType()==ESortType1)
      val=ETrue;
    aCommand->SetChecked(val);
    break;
 
  default:
    break;
    }
  return(aCommand);
  }

Commands and Menus

If you compare UIQ 3 to other systems, it does not offer the opportunity for applications to modify command state immediately prior to menus being displayed. The main reason for this is that applications do not explicitly define menus, only a set of commands for the UI to present to a user. Within a particular UI configuration, some commands may be presented within a drop-down menu; within other phone configurations it is possible they are displayed permanently on screen. If commands are displayed permanently on screen, the command state should accurately represent the underlying data state.

Command State and HandleCommandL()

As previously noted, commands in UIQ 3 maintain their own state information within a CQikCommand object. When an application attempts to process a command, it may fail. The HandleCommandL() method is designed to be able to handle error conditions; in particular, the method leaving. However, simply leaving is often not sufficient, particularly if state information is maintained in more than one place. In general, applications should perform some kind of rollback when errors occur. A common requirement is to ensure that the application state is the same after the failed operation as it was before the operation was attempted.

Consider the following piece of code:

       iEngine->SetListViewSortType(ESortType1);
       SortListViewL();

You can see that the engine state is being updated before the sort method, which can leave is called. If the SortListViewL() leaves, we must assume that the sort has failed. The sort order is not the value we have now stored in the engine. The software should roll back to the previous value for the engine and list to remain synchronized.

You could try to solve the issue with this code change:

       SortListViewL(ESortType1);
       iEngine->SetListViewSortType(ESortType1);

However, we have a third component to keep in synchronization and that component is the command state.

If you observe the Commands1 application closely, you may note that the radio button controls associated with the sort commands are updated to the new state before the command is delivered to the HandleCommandL() method. If the sort operation fails and we simply leave, the radio buttons will display one state and the list box a different state. We need to roll back the command state such that all components remain synchronized.

The following code from our example ensures all components remain synchronized:

  case EAppCmdSortType3:
   TAppSortTypes oldType=iEngine->ListViewSortType();
 
    iEngine->SetListViewSortType(ESortType3);
    TRAPD(err,SortListViewL(););
    if (err!=KErrNone)
      {
 
      // Set engine back to its previous state.
      iEngine->SetListViewSortType(oldType);
 
      // Now roll back the UI.
      CQikCommandManager& cm = CQikCommandManager::Static();
      TInt commandId=EAppCmdSortType1;
      if (oldType==ESortType2)
        commandId=EAppCmdSortType2;
      else if (oldType==ESortType3)
        commandId=EAppCmdSortType3;
      cm.SetChecked(*this,EAppCmdSortType3,EFalse);
      cm.SetChecked(*this,commandId,ETrue);
      User::Leave(err); // inform user
      }
    break;

It also shows how to implement rollback such that if the sort operation fails, all constituent parts of the system remain in synchronization.

CQikCommandManager

The example code above introduces the CQikCommandManager object. Each application will have a single CQikCommandManager created on its behalf by the UI framework and is responsible for maintaining the command set presented by your application. As shown above, the CQikCommandManager also allows you to access individual commands to update their state. In an application it is sometimes the case that processing one command affects the availability of another command. For example, if you have a Delete command it may be that no items remain after the deletion, so you would disable the Delete command and probably others too. The opposite may be true with View or Edit commands.

Adding or Removing Commands Programmatically

Our previous discussion has centered on the use of resources to define automatically what commands are available within a given view of your application. If you look at the CQikCommandManager object definition, you should observe various methods for creating, adding and deleting commands. Should the functionality provided automatically by resource and the framework be inadequate for your particular application you can add or remove commands programmatically.

In our example, we add the commands defined by the R_ALTERNATE_COMMANDS resource when page one is activated and remove them otherwise.

void CAppSpecificListView::TabActivatedL(TInt aTabId)
  {
  CQikCommandManager& cm=CQikCommandManager::Static();
  if (aTabId==EAppSpecificListViewPageId1)
    {
      
    // Page one adds a Create and erase command.
    cm.InsertIntoCommandListL(*this,*this, R_ALTERNATE_COMMANDS);
 
    cm.DeleteCommand(*this,EAppCmdSortAltType1);
    cm.DeleteCommand(*this,EAppCmdSortAltType2);
    }
  else
    {
 
    // Remove any create and erase commands
    // added in page one.
    cm.DeleteFromCommandList(*this,R_ALTERNATE_COMMANDS);
 
    // Manually add a couple of sort commands.
    TBuf<64>bb;
    CQikCommand* q = CQikCommand::NewLC(EAppCmdSortAltType1);
    q->SetType(EQikCommandTypeScreen);
    q->SetPriority(EAppCmdSortType1Priority);
    q->SetGroupId(EAppCmdSortGroup);
    q->SetIcon(KMbmFile, EMbmCommands1Icon0, EMbmCommands1Icon0mask);
    iEikonEnv->ReadResourceL(bb,R_STR_SORT_TYPE1);
    q->SetTextL(bb);
    q->SetHandler(this);
    cm.InsertCommandL(*this,q);
    CleanupStack::Pop(q);
 
    q=CQikCommand::NewLC(EAppCmdSortAltType2);
    q->SetType(EQikCommandTypeScreen);
    q->SetPriority(EAppCmdSortType1Priority);
    q->SetGroupId(EAppCmdSortGroup);
    q->SetIcon(KMbmFile, EMbmCommands1Icon1, EMbmCommands1Icon1mask);
    iEikonEnv->ReadResourceL(bb,R_STR_SORT_TYPE2);
    q->SetTextL(bb);
    q->SetHandler(this);        // We are MQikCommandHandler
    cm.InsertCommandL(*this,q);
    CleanupStack::Pop(q);
    }
 
  CQikMultiPageViewBase::TabActivatedL(aTabId);
  }
 

If we do not want to use resources at all, it is possible to construct CQikCommand or sub-classes thereof by hand and add these to the available command set.

The following code performs such a task:

  CQikCommand* q=CQikCommand::NewLC(EAppCmdSortAltType1);
  q->SetType(EQikCommandTypeScreen);
  q->SetPriority(EAppCmdSortType1Priority);
  q->SetGroupId(EAppCmdSortGroup);
  q->SetIcon(KMbmFile, EMbmCommands1Icon0, EMbmCommands1Icon0mask);
  iEikonEnv->ReadResourceL(bb,R_STR_SORT_TYPE1);
  q->SetTextL(bb);
  q->SetHandler(this);
  cm.InsertCommandL(*this,q);
  CleanupStack::Pop(q);

It has performed the equivalent of:

QIK_COMMAND
  {
  id = EAppCmdSortAltType1;
  type = EQikCommandTypeScreen;
  priority = EAppCmdSortType1Priority;
  groupId = EAppCmdSortGroup;
  icon = r_command_icon;
  text = "Sort by name";
  },

Categories

Overview

UIQ 3 applications often use a list view to present data, and a detail view to read and work with specific content. To make it easier for users to work with lists views, UIQ 3 applications can use the category paradigm. Each data record is associated with a category, often the Unfiled category by default. The list view normally displays a list of all data records using category All. The list can be filtered by category to make it easier to work with subsets of data; this is a useful technique to help the user organize and find data on a mobile phone.

For example, in the Agenda application the user can classify appointments using categories. Here, we have created categories Personal and Work using the New Category menu option. The 8.00 am appointment is set as a Work meeting. In the monthly (list) view, we can change from the default of All Agenda Categories to view only Work appointments.

Category allocation in Agenda Category filtering in Agenda

Figure 13 Category allocation and filtering in Agenda

Category support is embedded within the view architecture; the Categories example application shows you how to implement category support in your application.

Categories example application

The Categories example demonstrates how to:

  • add categories to your application
  • support the creation of new categories
  • handle deletion of existing categories
  • support renaming of categories
  • change the currently active category and ensure any displayed list is kept up to date.

Firstly, we define a default set of categories using a resource:

RESOURCE ARRAY r_app_categories
  {
  items=
    {
    QIK_CATEGORY
      {
      name = "All";
      flags = EQikCategoryCantBeRenamed | EQikCategoryCantBeDeleted | EQikCategoryAll;
      handle = EAppCategoryAll;
      },
 
    QIK_CATEGORY
      {
      name = "Unfiled";
      flags = EQikCategoryCantBeRenamed | EQikCategoryCantBeDeleted | EQikCategoryUnfiled;
      handle = EAppCategoryUnfiled;
      },
 
    // This app chooses to define the following category  
    // as one that cannot be deleted.
 
    QIK_CATEGORY
      {
      name = "Australasian";
      flags = EQikCategoryCantBeDeleted;
      handle = EAppCategoryAustralasian;
      },
 
    // This app chooses to define the following category
    // as one that cannot be renamed.
 
    QIK_CATEGORY
      {
      name = "Asian";
      flags = EQikCategoryCantBeRenamed;
      handle = EAppCategoryAsian;
      },
 
    QIK_CATEGORY
      {
      name = "European";
      flags = 0;
      handle = EAppCategoryEuropean;
      }
    };
  }

When you have categories within your application, two standard categories should be defined: the All and Unfiled categories.

Usually the All category is displayed by default and, as its name suggests, when the All category is selected, all entries should be listed.

The Unfiled category is for entries that have not been assigned any other category. The All and Unfiled categories need to be available at all times and must not be renamed or deleted.

Our application has chosen to define three other categories, corresponding to the continents in which the countries are located. They have been given different properties (flags) merely to demonstrate that this is possible. You should note that only one entry should be defined for each of the EQikCategoryAll and EQikCategoryUnfiled flags.

The handle field is set from an application specific enum set:

enum TAppCategoryIds
  {
  EAppCategoryAll,
  EAppCategoryUnfiled,
 
  // We have three app specific pre-defined categories.
  EAppCategoryAustralasian,
  EAppCategoryAsian, 
  EAppCategoryEuropean,
 
  EAppCategoryLastItem
  };

Adding the categories to an application is usually performed within the ViewConstructL() method of the view with which they are associated:

void CAppSpecificListView::ViewConstructL()
  {
  ViewConstructFromResourceL(R_LIST_VIEW_CONFIGURATIONS);
 
  // Create the category list    
  CQikCategoryModel* categories = QikCategoryUtils::ConstructCategoriesLC(R_APP_CATEGORIES);
  SetCategoryModel(categories);
  CleanupStack::Pop(categories);
 
  // By default we view the 'All' category.
  SelectCategoryL(EAppCategoryAll);
 
  // Cause the category picker to be visible.
  SetCategoryModelAsCommandsL();
  }

In our demonstration example, we do not save any user changes when the application exits or restore the changes when the application is run next time. Though unrealistic for a commercial quality application, this example is intended to demonstrate the principles involved. The Chapter Building an application will add the required load and save support for categories.

From the code fragment, you should be able to observe a CQikCategoryModel object being created and initialized with the default category set; this handle is given to the view via the SetCategoryModel() method and an initial category is selected via SelectCategoryL().

As a final step, we request a set of commands to be generated with the SetCategoryModelAsCommandsL() method, so the category information can be displayed on screen as a set of commands. On Pen Style phones a drop-down icon is displayed top right; tapping on the icon displays the category commands:

Categories application in Pen Style UI configuration

Figure 14 Categories application in Pen Style UI configuration

On a Softkey Style phone a named group is created and added to the menu:

Categories application in Softkey Style UI configuration

Figure 15 Categories application in Softkey Style UI configuration

The Edit categories command appears due to the content of our QIK_COMMAND_LIST for this view:

RESOURCE QIK_COMMAND_LIST r_list_view_commands
  {
    items=
    {
 
    QIK_COMMAND
      {
      id = EAppCmdAbout;
      type = EQikCommandTypeScreen;
      groupId = EAppCmdMiscGroup;
      priority = EAppCmdAboutPriority;
      text = "About";
      },
 
    QIK_COMMAND
      {
      id = EAppCmdEditCategories;
      type = EQikCommandTypeCategory;
      groupId =EQikCommandGroupIdAfterCategoryCommands;
      text = "Edit categories";
      }
    };
  }

Category commands are delivered to our HandleCommandL() method in the same way as any other command. However, rather than only process by command ID we have to handle category commands slightly differently:

void CAppSpecificListView::HandleCommandL(
                    CQikCommand& aCommand)
  {
  if (aCommand.Type()==EQikCommandTypeCategory)
    {
    if (aCommand.Id()==EAppCmdEditCategories)
      CQikEditCategoriesDialog::RunDlgLD(CategoryModel(),this);
    else
      {
      SelectCategoryL(aCommand.CategoryHandle());
      UpdateListBoxL();
      }
    }
  else
    {
    switch (aCommand.Id())
      {
      case EAppCmdAbout:
      (new(ELeave)CAboutDialog)->ExecuteLD(R_ABOUT_DIALOG);
      break;
      
      default:       // For example the back button...
 
      CQikViewBase::HandleCommandL(aCommand);
      break;
      }
    }
  }

As you can see, we first check to see if the command type is a category command. If it is, then we either run the system category editor or update the list box due to selection of a different category.

The system supplied category editor requires us to pass an object of type MQikEditCategoryObserver to process various user actions. In our example the CAppSpecificListView implements that interface.

The MQikEditCategoryObserver interface comprises nine methods, one of which is shown below:

TBool CAppSpecificListView::DoDeleteCategoryL(TInt aHandle)
  {
  iEngine->DeleteCategory(aHandle);
      
  if (aHandle==CurrentCategoryHandle())
    {
    SelectCategoryL(EAppCategoryAll);
    UpdateListBoxL();
    }
  return(ETrue);
  }

This method is called back to implement the deletion of a category from the underlying model; in our example the engine runs that model. The user presentation is updated by the framework code. The code checks to see if the category that is being deleted is the one currently being displayed by the list box. If so, an alternative category is chosen, usually the All category, and the list box is updated.

Updating the List Box

In our example we present a very basic approach to updating the list box when a category changes. We simply remove all the entries and rebuild from the beginning. An alternative approach would be to run though all the existing entries, checking whether the entries within the list box should remain there, and adding any missing entries.

Whichever approach you adopt, do not forget that some kind of rollback should be incorporated, in order to ensure that any selected category and displayed list remain synchronized. We cover rollback in more detail in the Chapter Building an application.

Changing Entry Categories

The Categories application does not allow you to change the category that is allocated to each data record. You cannot change the Australasia category that is allocated to Australia, for example.

Setting the category is normally done in a detail view; UIQ 3 applications like Agenda and Contacts typically place a Category drop-down below the data presented in the detail view:

Allocating and creating categories in Agenda Allocating and creating categories in Agenda
Allocating and creating categories in Agenda

Figure 16 Allocating and creating categories in Agenda. (Pen Style UI configuration)

Clicking on the category drop-down opens a menu in which the user can select an existing category or create a new one.

Before we can add this functionality to our application, we must first describe how to create views. We will return to categories and add the ability to allocate category to a data record in the SignedAppPhase2 example in the Chapter Building an application.

Further information

You can find more information on the Command Processing Framework in the Programmer’s Guide to New Features and UIQ Style Guide.

Code

Code for this chapter

CommandsAndCategories_code.zip
Personal tools
code download