Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 77
Текст из файла (страница 77)
In such a case, the call to ActivateL() would not be made from within ConstructL(), but wouldbe called from elsewhere, once the necessary additional initialization iscomplete.The blank control class is instantiated as an application view from theapplication UI class.426CONTROLSvoid CMyApplicationAppUi::ConstructL(){BaseConstructL(EAknEnableSkin);iAppView = new(ELeave) CBlankAppView;iAppView->ConstructL(ClientRect());}Compound ControlsTo illustrate some of the differences between simple and compoundcontrols, we use the Noughts and Crosses example application thatwas introduced in Chapter 12. As a reminder, the appearance of theapplication is shown in Figure 15.1.The implementation uses an array of tiles, derived from CCoeControl,as component controls in the application view. The tiles supply all themechanisms for displaying their content and focusing them, using eitherthe cursor keys or the pointer.
In this chapter we concentrate on thosefeatures that are necessary to allow a control to include componentcontrols, rather than on the internal implementation of the tiles.Figure 15.1 The Noughts and Crosses applicationAs mentioned in Chapter 12, the view needs to be added to the controlstack so that it has the opportunity to process key presses. This is done bycalling AddToStackL(iAppView), normally from the ConstructL()function of the application UI class. We see the consequences later, duringthe discussion of the view itself.The control stack maintains a prioritized list of all controls that havean interest in processing key events and ensures that key events areCONTROL TYPES427offered to the controls in the order that they were added.
As well ascontrols from GUI applications, the list contains controls associatedwith any front-end processor (FEP), with the processing of debug keys(Ctrl, Alt, Shift, key combinations) and with active dialogs.If any view has been placed on the control stack, you must ensure itslater removal, using code of the form:iEikonEnv->RemoveFromStack(iAppView);The application’s view class is defined as follows: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);// 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 area428CONTROLSTRect iBorderRect; // Bounding rectangle for borderTInt iTileSide;// Tile dimension, allowing for// line widths and border};The second-stage constructor is implemented as follows:void COandXAppView::ConstructL(const TRect& aRect){// Create a window for this application viewCreateWindowL();for (TInt i = 0; i < KNumberOfTiles; i++){User::LeaveIfError(iTiles.Append(CreateTileL()));}ComponentControl(0)->SetFocus(ETrue);iStatusWin = COandXStatusWin::NewL(Window());// Set the window’s sizeSetRect(aRect); // needs to be after component creation - see// SizeChanged()// Activate the window, which makes it ready to be drawnActivateL();}Apart from the regular tasks of creating a window, setting its size andactivating it, the majority of the code is concerned with creating andinitializing the tile control and status window instances.Setting a component control to have keyboard focus normally has aneffect on the appearance of the component.
It usually highlights itself insome way and, if the component accepts text, may cause a text cursorto become visible. Setting and unsetting keyboard focus also has otherimplications, as discussed in the next section.Support for the component controls is supplied by implementations of two CCoeControl functions: CountComponentControls()and ComponentControl(). Since the view needs to respond to keypresses, we also need an implementation of CCoeControl’s OfferKeyEvent() function.Supplying implementations for CountComponentControls() andComponentControl() is mandatory for any compound control. Thetwo functions work as a pair and should always be coded to be consistentwith each other.
The control framework calls both functions in order toaccess the component controls, using code of the form:...for (TInt i=0; i<CountComponentControls(); i++){CCoeControl* component = ComponentControl(i);...}...CONTROL LAYOUT429Chapter 17 provides more information about these functions; theimplementations for the Noughts and Crosses game are fairly straightforward.TInt COandXAppView::CountComponentControls() const{return KNumberOfTiles +1;}CCoeControl* COandXAppView::ComponentControl(TInt aIndex) const{if (aIndex==KNumberOfTiles){return iStatusWin;}else{return const_cast<COandXTile*>(iTiles[aIndex]);}}15.3 Control LayoutA control is responsible for the layout of its content. If it is a compoundcontrol, it is also responsible for setting the position and size of itscomponent controls.
If a control’s size is set when it is created and doesnot subsequently change, it can usually set the position and size of anycomponents within its ConstructL() function. If a control’s size islikely to change during its lifetime, then this approach will not workand the control will need to rework its content layout accordingly. Thecontrol framework provides a SizeChanged() function specifically tomeet this need.
Its default implementation is empty.Although the view of the Noughts and Crosses application does notchange size after its creation, we’ve used SizeChanged() in thisapplication view to calculate and set the size and position of the bordersurrounding the playing area and the component tiles. It’s coded tocope with different screen sizes and alternative board layouts, containingdifferent numbers of tiles.Although it looks a little daunting at first sight, the SizeChanged()function consists of a small handful of sections, each of which performsrelatively simple geometrical calculations.void COandXAppView::SizeChanged(){// all component tiles must already exist__ASSERT_DEBUG(iTiles[KNumberOfTiles-1], Panic(EOandXNoTiles));TRect rect = Rect();rect.iTl.iY = rect.iBr.iY - KStatusWinHeight;430CONTROLSiStatusWin->SetRect(rect);rect = Rect();rect.iBr.iY -= KStatusWinHeight;TSize controlSize = rect.Size();TSize tileSize;tileSize.iWidth=2*((controlSize.iWidth-2*KBorderWidth-(KTilesPerRow-1)*KLineWidth)/(2*KTilesPerRow));tileSize.iHeight=2*((controlSize.iHeight-2*KBorderWidth-(KTilesPerCol-1)*KLineWidth)/(2*KTilesPerCol));iTileSide = tileSize.iWidth < tileSize.iHeight ?tileSize.iWidth :tileSize.iHeight;TSize boardSize;boardSize.iWidth = KTilesPerRow*iTileSide +(KTilesPerRow-1)*KLineWidth;boardSize.iHeight = KTilesPerCol*iTileSide +(KTilesPerCol-1)*KLineWidth;iBoardRect.iTl.iX = (controlSize.iWidth - boardSize.iWidth)/2;iBoardRect.iTl.iY = (controlSize.iHeight - boardSize.iHeight)/2;iBoardRect.iBr.iX = iBoardRect.iTl.iX + boardSize.iWidth;iBoardRect.iBr.iY = iBoardRect.iTl.iY + boardSize.iHeight;iBorderRect = iBoardRect;iBorderRect.Grow(KBorderWidth,KBorderWidth);for (TInt i=0; i<KNumberOfTiles; i++){TInt row = i / KTilesPerRow;TInt col = i % KTilesPerRow;TRect tileRect;tileRect.iTl.iX = iBoardRect.iTl.iX + col * (iTileSide + KLineWidth);tileRect.iTl.iY = iBoardRect.iTl.iY + row * (iTileSide + KLineWidth);tileRect.iBr.iX = tileRect.iTl.iX + iTileSide;tileRect.iBr.iY = tileRect.iTl.iY + iTileSide;ComponentControl(i)->SetRect(tileRect);}}The code first performs independent calculations of the tile width andheight that will enable the required number of tiles, and their borders, tofit in the available area, making sure that the tile dimensions are alwaysan even number of pixels.
It takes the smaller of the two dimensions anduses it for the side of the tile squares.It is then a fairly simple matter to calculate overall board and borderpositions, which are stored in member data for later use when drawingthe board. Finally, it calculates the tile positions and sets them by callingeach tile’s SetRect() function.In terms of designing an application to work with a variety of displaysizes, it is interesting to note the division of effort used in calculating thepositions and sizes of the various elements within the view.
As we’ve justseen, the majority of the work is done in the view’s SizeChanged()function. This is called relatively infrequently, when the view is initializedor when its size changes, and so it makes sense to use it to do as muchof the work as possible. In consequence, the view’s drawing function hasCONTROL LAYOUT431relatively little to do, other than actually drawing the various features ofthe board.SizeChanged() is called whenever one of the control’s sizechanging functions is called on the control. These include:• SetExtent()• SetSize()• SetRect()• SetCornerAndSize()• SetExtentToWholeScreen().In Noughts and Crosses, the call is triggered by the call to SetRect()in the view’s ConstructL() function:void COandXAppView::ConstructL(const TRect& aRect){// Create a window for this application viewCreateWindowL();for (TInt i = 0; i < KNumberOfTiles; i++){User::LeaveIfError(iTiles.Append(CreateTileL()));}ComponentControl(0)->SetFocus(ETrue);iStatusWin = COandXStatusWin::NewL(Window());// Set the window’s sizeSetRect(aRect); // needs to be after component creation - see// SizeChanged()// Activate the window, which makes it ready to be drawnActivateL();}All we have to do is to ensure that SetRect() is not called until afterthe tiles have been created.There is no system-wide mechanism for notifying controls of sizechanges, and SizeChanged() is called only as a result of calling oneof the five functions listed above.
In consequence, if resizing one controlaffects the size of other controls, it is the application’s responsibility toensure that it handles the resizing of all affected controls.There are three other functions that are associated with changes in thesize or position of a control:• SetSizeWithoutNotification() sets a control’s size withoutcalling SizeChanged().• SetPosition() sets the pixel position of the top-left corner ofthe control. If the control owns its containing window, the function432CONTROLSachieves this by setting the position of the window; otherwise theposition of the control is set relative to its containing window.