Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 65
Текст из файла (страница 65)
The tile which currently has focus is drawn with a yellowbackground; the others are drawn with the default brush color, which iswhite.During the game, a player places a nought or a cross in a particular tile either by tapping on it or by using cursor keys to move thehighlight to that tile and pressing the phone’s Selection or Action key.A pointer event is handled by the tile’s HandlePointerEventL()function:void COandXTile::HandlePointerEventL (const TPointerEvent& aPointerEvent){if (aPointerEvent.iType == TPointerEvent::EButton1Down){TryHitL();}}A key event is handled by OfferKeyEventL():TKeyResponse COandXTile::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType){TKeyResponse keyResponse = EKeyWasNotConsumed;if (aType!=EEventKey){return keyResponse;}switch (aKeyEvent.iCode){case EKeyOK:IMPLEMENTING THE GAME ON S60349TryHitL();keyResponse = EKeyWasConsumed;break;default:keyResponse = EKeyWasNotConsumed;break;}return keyResponse;}In both cases, this results in a call to the tile’s TryHitL() function,which is implemented as:void COandXTile::TryHitL(){if (iCmdHandler->TryHitSquareL(this)){DrawDeferred();}}TryHitL() calls the TryHitSquareL() function of the view class,using the same mechanism that is also used to call TileStatus().The COandXAppView class definition has 19 member functions, notcounting those that it inherits from its base classes, but only the twomentioned above are called from a tile.
As an encapsulation aid, we havechosen to declare these two functions, as pure virtual functions, in anMViewCmdHandler interface class:class MViewCmdHandler{public:virtual TBool TryHitSquareL(const COandXTile* aControl) = 0;virtual TTileState TileStatus(const COandXTile* aControl) const = 0;};The view class inherits from this interface and provides the concreteimplementations, as indicated by the bold portions of the COandXAppView class definition:class COandXAppView : public CCoeControl, public MCoeControlObserver,public MViewCmdHandler{public:static COandXAppView* NewL(const TRect& aRect);virtual ∼COandXAppView();// From CCoeControlTKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType);350A SIMPLE GRAPHICAL APPLICATION// new functionsvoid MoveFocusTo(const TInt aIndex);TInt IdOfFocusControl();void ShowTurn();void ResetView();private:COandXAppView();void ConstructL(const TRect& aRect);void SwitchFocus(TInt aFromIndex, CCoeControl* aToControl);void DrawComps(TRect& aRect) const;COandXTile * CreateTileL();// From CCoeControlvoid Draw(const TRect& aRect) const;void SizeChanged();TInt CountComponentControls() const;CCoeControl* ComponentControl(TInt aIndex) const;// From MCoeControlObservervoid HandleControlEventL(CCoeControl* aControl, TCoeEvent aEventType);// From MViewCmdHandlerTBool TryHitSquareL(const COandXTile* aControl);TTileState TileStatus(const COandXTile* aControl) const;private:RPointerArray<COandXTile> iTiles; // View owns the tilesCOandXStatusWin* iStatusWin;// and its own status window.TRect iBoardRect; // Board areaTRect iBorderRect; // Bounding rectangle for borderTInt iTileSide;// Tile dimension, allowing for line widths// and border};Note that the functions are declared public in the interface class,but private within COandXAppView.
By providing each tile with anMViewCmdHandler* pointer to the enclosing view, we expose onlythose two functions (they are not even available via a COandXAppView*pointer).The interface class is a useful way of encapsulating the interactionbetween the control and the view or, indeed, the rest of the program.
Thecontrol does not care about any of the view’s internal details, providedthat it correctly handles the request to play in a particular square and thequerying of a square’s content. Similarly, the view does not care whetherthe command originated with a key event, a pointer event, or some otherkind of event – it just requires these functions to be called at the righttime with the right parameters.This is a useful technique, which is used frequently within a varietyof Symbian OS APIs, and it is a good one to adopt for your programs,especially if they are more complicated than the Noughts and Crossesapplication. As a further illustration, think about the way commandsIMPLEMENTING THE GAME ON S60351reach HandleCommandL().
The menu bar uses an MEikMenuObserver for this and does not otherwise care about the large API of theCEikAppUi class. The button bar uses the MEikCommandObserverinterface. Similarly, command buttons use an MCoeControlObserverinterface, which the button bar implements by converting button eventsinto application commands.TryHitSquareL() simply calls the controller’s HitSquareL()function, to determine whether the attempt is successful:TBool COandXAppView::TryHitSquareL(const COandXTile* aControl){return Controller().HitSquareL(Index(aControl));}It returns ETrue only if the square was previously empty and nowcontains a symbol, otherwise it returns EFalse.As was mentioned earlier, TileStatus() is called from each tilewhen it needs to draw itself, in order to determine which, if any, symbolshould be drawn in the tile.
The function’s action is simply to request theinformation from the engine:TTileState COandXAppView::TileStatus (const COandXTile* aControl) const{return Engine().TileStatus(Index(aControl));}The majority of the view’s functions are described in Chapter 15,where they are used to illustrate the general behavior of controls. Here,we mention only ShowTurn(), which is called from the application UIclass and the controller whenever there is a change in whose turn it is toplay. It simply requests the status window to redraw itself:void COandXAppView::ShowTurn(){iStatusWin->DrawDeferred();}How DrawDeferred() works is explained in Chapter 17; here it issufficient to know that it results in a call to the status window’s Draw()function.
As we saw earlier, there is no need for ShowTurn() to pass aparameter indicating whose turn it is, since the status window’s Draw()function queries the controller directly for the symbol it needs to draw.Command MenusThe application uses only two commands: one to specify whether theNought player or the Cross player should make the first move, and one352A SIMPLE GRAPHICAL APPLICATIONto abandon the current game. The first of these is only relevant before agame starts and the second is only used while a game is in progress.
Inconsequence, we’ve chosen to use a dynamic command menu, whichshows only those commands that can sensibly be used, given the currentstate of the game. We’ve implemented the menu in a way that illustratessome of the options that are available for setting up and changing thecontent of a command menu. Although at least one of the commands,that to select the first player, could reasonably be implemented via adialog, we’ve chosen not to do so, leaving the discussion of dialog use toChapter 16.For S60, the specification of the command menu starts in the resourcescript, with the definition of an EIK_APP_INFO resource:RESOURCE EIK_APP_INFO{menubar = r_oandx_menubar;cba = R_AVKON_SOFTKEYS_OPTIONS_EXIT;}This resource contains a pointer to the resource that contains theapplication’s menu bar and a Control Button Array that defines thesoftkey labels – in this case, Options and Exit – that are to appear in thecontrol pane.The menu bar resource, in turn, specifies one or more MENU_PANEresources that list the menu items they contain.
As in this case, an S60menu bar normally contains only one menu pane. If you specify morethan one menu pane, the S60 UI concatenates their contents into a singlelist.RESOURCE MENU_BAR r_oandx_menubar{titles ={MENU_TITLE{menu_pane = r_oandx_menu;}};}For this application, the menu pane specifies only one menu item; thesecond menu item is constructed dynamically.RESOURCE MENU_PANE r_oandx_menu{items ={MENU_ITEMIMPLEMENTING THE GAME ON S60353{command = EOandXNewGame;txt = "New game";}};}Each menu item specifies the text to be displayed and a non-zero value,unique within the application, used to identify the command.
The command IDs are normally defined via an enumeration in the application’sHRH file; for this application, they are defined, in oandx.hrh, as:enum TOandXIds{EOandXNewGame = 0x1000,EOandXSwitchTurn};// start value must not be 0The resource file specifies only the command ID and text for the Newgame command. The other command is added dynamically according tocontext, using one or other of the following two resources for its text:RESOURCE TBUF r_oandx_o_moves_first{buf = "Noughts move first";}RESOURCE TBUF r_oandx_x_moves_first{buf = "Crosses move first";}It is worth noting here that a truly commercial application would readthe text for these resources – and most other text within the resourcescript – from a separate file, in order to facilitate translation into otherlanguages.