Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 67
Текст из файла (страница 67)
In the present case, the resource specifies a single page,without supplying any content:RESOURCE QIK_VIEW_PAGES r_oandx_layout_pages{pages ={QIK_VIEW_PAGE{page_id = EOandXViewPage;}};}Since the UIQ version of the view class does not have accessibleimplementations of ComponentControl() or CountComponentControls(), functions that, in an S60 application, call one or bothof these functions need a different implementation. In the Noughts andCrosses application, the functions that are affected are those associatedwith which tile has focus. In the S60 version, these are IdOfFocusControl() and MoveFocusTo():TInt COandXAppView::IdOfFocusControl(){TInt ret = -1;for (TInt i=0; i<KNumberOfTiles; i++){if (ComponentControl(i)->IsFocused()){ret = i;break;}}ASSERT_ALWAYS(ret>=0, Panic(EOandXNoTileWithFocus));return ret;}void COandXAppView::MoveFocusTo(const TInt index){TInt oldIndex = IdOfFocusControl();if (index != oldIndex){SwitchFocus(oldIndex, ComponentControl(index));}}360A SIMPLE GRAPHICAL APPLICATIONSwitchFocus() is implemented as:void COandXAppView::SwitchFocus(TInt aFromIndex, CCoeControl* aToControl){ComponentControl(aFromIndex)->SetFocus(EFalse, EDrawNow);aToControl->SetFocus(ETrue, EDrawNow);}In the UIQ version, the two corresponding functions are IdOfFocusedControl() and SetFocusByIdL(), whose basic implementations are:TInt COandXView::IdOfFocusedControl(){TInt i;for (i=0; i<KNumberOfTiles; i++){CCoeControl *control=ControlById<CCoeControl>(i);if (control->IsFocused())break;}return i;}void COandXView::SetFocusByIdL(TInt aControlId){RequestFocusL(ControlById<CCoeControl>(aControlId));}In the UIQ version, we changed the names of both functions to emphasize the differences in implementation.
Note, in particular, that SetFocusByIdL() is a leaving function, whereas MoveFocusTo() is not.Fortunately this does not give rise to any other complications in the currentapplication, since they are called only from within leaving functions.While on the subject of focus, there are two further differences thatneed mentioning. First, the default status of newly created controls in S60is for them to be capable of accepting focus, which is not the case in UIQ.In UIQ you need to call SetFocusing(ETrue) on each control thatneeds to accept focus (in this case, on each of the board’s tiles). In contrast,in S60, you need to call SetFocusing(EFalse) on each control, suchas the game’s status window, that you do not want to be able to acceptfocus. Secondly, UIQ 3 views provide automatic, intelligent key- orbutton-based navigation between any of the component controls that canaccept focus – that is, those controls for which SetFocusing(ETrue)has been called.
In consequence, the UIQ version has no need toimplement the view’s OfferKeyEventL() function.CommandsUIQ 3 views are primarily designed for use in applications with multipleviews, each with its own set of commands, so they are not seen at theirDIFFERENCES FOR UIQ 3361best in an application such as the Noughts and Crosses application, whichonly has a single view.
The application does, however, allow the basicdifferences to be illustrated.A major difference in UIQ is that commands are treated in a moreabstract way than in S60. A UIQ device may be set up to access itscommands by means of either a pointer device or softkeys, and thecommands themselves may or may not be displayed in a commandmenu. In addition, commands may be provided from a variety of sourceswithin the application, such as one or more individual controls – oreven from outside the application itself, for example, from a front-endprocessor.The UIQ application framework contains a command model that dealswith all these variations.
In consequence, the application programmerneed only be concerned with which commands should be available andnot with how they are presented or selected.The first change is to the resource script. As we saw earlier, an S60application uses an EIK_APP_INFO resource, to specify the menu barand the menu items it contains. In contrast, a UIQ 3 application must specify an empty EIK_APP_INFO resource, plus a QIK_VIEW_CONFIGURATIONS resource that lists the UI configurations it supports.
In the Noughtsand Crosses application, these resources are:RESOURCE EIK_APP_INFO { }RESOURCE QIK_VIEW_CONFIGURATIONS r_oandx_ui_configurations{configurations ={QIK_VIEW_CONFIGURATION{ui_config_mode = KQikPenStyleTouchPortrait;command_list = r_oandx_portrait_commands;view = r_oandx_layout;},QIK_VIEW_CONFIGURATION{ui_config_mode = KQikSoftkeyStylePortrait;command_list = r_oandx_portrait_commands;view = r_oandx_layout;}};}Each configuration specifies its list of available commands via aQIK_COMMAND_LIST resource:RESOURCE QIK_COMMAND_LIST r_oandx_portrait_commands{items ={362A SIMPLE GRAPHICAL APPLICATIONQIK_COMMAND{id = EEikCmdExit;type = EQikCommandTypeScreen; // Only visible in debugstateFlags = EQikCmdFlagDebugOnly;text = "Close (debug)";},QIK_COMMAND{id = EOandXNewGame;type = EQikCommandTypeScreen;text = "New game";},QIK_COMMAND{id = EOandXFirstPlayer;type = EQikCommandTypeScreen;text = "Noughts move first";}};}As in the S60 version, the commands are updated dynamically, butthe mechanism is different.
S60 applications update their menus via theDynInitMenuPaneL() function, which is called immediately before amenu is made visible.In UIQ, since a command may be selected by means of a softkey, orfrom a toolbar, there is no guarantee that a menu pane is displayed beforethe selection takes place. In consequence, you cannot use DynInitMenuPaneL() to update the available commands.
Indeed, since somecommands may be permanently visible, UIQ applications need to perform such updating each time the view’s state changes in a way thataffects the commands.In many cases, it would be appropriate to implement the view’sHandleControlEventL() function, which is called (with an EEventStateChanged event code) each time one of the view’s controls changesits state.
In the Noughts and Crosses application, all significant statechanges take place in the Controller, so we’ve taken the more direct optionof adding a SetStateL() function to the Controller, implemented as:void COandXController::SetStateL(TState aState){iState = aState;OandXAppUi()->UpdateCommandsL(IsNewGame(), IsCrossTurn());}This calls an UpdateCommandsL() function in the application UIclass, which simply calls a similarly named function in the view:void COandXView::UpdateCommandsL(TBool aIsNew, TBool aIsCrossTurn){DIFFERENCES FOR UIQ 3363iCommandManager.SetAvailable(*this, EOandXNewGame, !aIsNew);iCommandManager.SetAvailable(*this, EOandXFirstPlayer, aIsNew);iCommandManager.SetTextL(*this, EOandXFirstPlayer,aIsCrossTurn?R_OANDX_O_FIRST:R_OANDX_X_FIRST);}The following points are relevant when updating the menus:• While a game is in progress, there is no need to worry about whetherthe text for the ‘Who moves first’ command is updated correctly ornot, because that command is only available (and visible) before anew game is started.• When executing the ‘Who moves first’ command, the game is alwaysnew, so the only interest is in setting the correct text for the nextpotential use of that command.In addition, the controller’s Reset() function is renamed to ResetL()because it can now leave:void COandXController::ResetL(){Engine().Reset();if (IsCrossTurn()){SwitchTurn();}SetStateL(ENewGame);}ResetL() cannot be used to initialize the controller (and engine)because SetStateL() calls the view’s UpdateCommandsL() function, and the view does not exist at the time the controller is constructed.Instead, in the controller’s ConstructL(), we explicitly set the relevantdata:...Engine().Reset();iState = ENewGame;iCrossTurn = EFalse;...Since UIQ expects an application to have multiple views and eachview may have a different set of commands, command processing isimplemented within each individual view.
A UIQ application, therefore,does not supply a HandleCommandL() function in the application UIclass, but implements this function in each of its views. Note that theargument to this function is a reference to an instance of CQikCommand,rather than the command ID that is passed to this function in the364A SIMPLE GRAPHICAL APPLICATIONapplication UI class. Otherwise, the implementation is broadly similar tothat in the S60 version:void COandXView::HandleCommandL(CQikCommand& aCommand){switch(aCommand.Id()){case EOandXNewGame:Controller().ResetL();SetFocusByIdL(0);DrawNow();break;case EOandXFirstPlayer:Controller().SwitchTurn();UpdateCommandsL(Controller().IsNewGame(),Controller().IsCrossTurn());break;default:#ifdef _DEBUGOandXAppUi()->SaveGameStateL();#endif// The Go back and Exit commands are handled by CQikViewBase.CQikViewBase::HandleCommandL(aCommand);break;}}PersistenceIn UIQ, the default behavior is to allow applications to use persistentdata, so, unlike in S60, there is no need to supply an implementation ofthe document’s OpenFileL() function.UIQ applications do not normally have a Close command (except fortesting purposes, in debug builds).
Instead, system code may instruct anapplication to close at any time that memory needs to be freed, firstgiving it an opportunity to save its persistent data. In order to access thefunctionality more easily, we have added – but only in debug builds – acall to the SaveGameStateL() function of the application UI class inthe default case of the view’s HandleCommandL(). The implementationis very simple:void COandXAppUi::SaveGameStateL(){SaveL();}We have to supply this new function because the SaveL() functionof the application UI class is protected, and so cannot be called directlyfrom the view. With this call in place, the persistent data is saved eachtime the Close (Debug) command is selected.DIFFERENCES FOR UIQ 3365Since the application’s persistent data contains one item – the IDof the tile that currently has focus – from the view, there is one finalcomplication that we have to deal with.