Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 91
Текст из файла (страница 91)
Decide which it should be and commit to it.Only small parts of a control’s code are affected by the decision. So if youfind out later that your standalone application view control, for instance,now has a window to lodge in, then you should be able to modify itquite easily.For instance, in the example in Section 17.9, the CExampleHelloControl class adapts the CHelloWorldAppView class, turning itinto a lodger control. The class declaration changes from:class CHelloWorldAppView : public CCoeControl{public:static CHelloWorldAppView* NewL(const TRect& aRect);...void ConstructL(const TRect& /*aRect*/);...};to:class CExampleHelloControl : public CCoeControl{public:static CExampleHelloControl* NewL(const CCoeControl& aContainer,SHARING THE SCREEN507const TRect& aRect);...private:void ConstructL(const CCoeControl& aContainer, const TRect& aRect);...};The essential change is that we have to pass a CCoeControl& parameter to the control’s ConstructL() function, to tell it which CCoeControl to lodge in.
The construction changes from something like:void CHelloWorldAppView::ConstructL(const TRect& aRect){CreateWindowL();SetRectL(aRect);ActivateL();iHelloWorld = iEikonEnv->AllocReadResourceL(R_HELLOWORLD_TEXT_HELLO);}to:void CExampleHelloControl::ConstructL(const CCoeControl& aContainer,const TRect& aRect){SetContainerWindowL(aContainer);SetRect(aRect);iView = CExampleHelloView::NewL();iText = iEikonEnv->AllocReadResourceL(R_EXAMPLE_TEXT_HELLO_WORLD);iView->SetTextL(*iText);...ActivateL();}Instead of calling CreateWindowL() to create a window of the rightsize, we call SetContainerWindowL() to register the control as alodger of another window-owning control.Compound ControlsThere needs to be some structure in laying out lodger controls such asthose in the Noughts and Crosses application view.
As was mentionedin Chapter 15, that discipline is obtained by using a compound controlwhich, by definition, is a control that contains one or more componentcontrols.• A component control is contained entirely within the area of itsowning control.• All components of a control must have non-overlapping rectangles.508GRAPHICS FOR DISPLAY• A component control does not have to be a lodger control: it can alsobe window-owning. In the majority of cases, however, a componentcontrol is a lodger control.To indicate ownership of component controls to CONE’s framework, acompound control must implement two virtual functions from CCoeControl:• CountComponentControls() indicates how many components acontrol has – by default it has zero, but you can override this.• ComponentControl() returns a pointer to the n th component, withn ranging from zero to the count of components minus one.
By default,this function panics (because it should never be called if there areno components). If you override CountComponentControls(),you must also override this function to return a component for eachpossible value of n.If a control contains a fixed number of components, a convenientSymbian OS idiom is to supply an enumeration for the controls, such as:enum{EMyFirstControl,EMySecondControl,...EMyLastControl,ENumberOfControls}Your CountComponentControls() function can then simply returnENumberOfControls, which has the advantage that you do not forgetto change the return value if, over time, you add or remove controls:TInt anyExampleAppView::CountComponentControls() const{return ENumberOfControls;}If a control contains a variable number of controls then the return valuemust be evaluated dynamically.
If the control contains a fixed number ofcomponents, a simple implementation of ComponentControl() mightbe a switch statement, with each case hard coded to the address of theappropriate component control, as in the following example:CCoeControl* anyExampleAppView::ComponentControl(TInt aIndex) const{switch (aIndex){SHARING THE SCREENcasecase...case}return}509EMyFirstControl: return iMyFirstControl;EMySecondControl: return iMySecondControl;EMyLastControl: return iMyLastControl;NULL;A dialog is also a compound control with, typically, a single windowbut an unpredictable number of component controls. In this case, insteadof hard coding the answers to CountComponentControls() andComponentControl() as in the above example, CEikDialog uses avariable-sized array to store dialog lines and dynamically evaluates thereturn values for these functions.More on DrawingDrawing to a window is easy for programs but, as indicated byFigure 17.10, it involves complex processing by Symbian OS.RWindowCCoeControlCWindowGcRWsBufferDraw()CGraphicsContentClient sideServer sideScreenWsBufferWindowserverCFbsBitGcCGraphicsContextFigure 17.10 Symbian OS drawing architecture510GRAPHICS FOR DISPLAYOn the client side, an application uses a CWindowGc to draw toa window.
CWindowGc’s functions are implemented by encoding andstoring commands in the window server’s client-side buffer. When thebuffer is full, or when the client requests it, the instructions in the bufferare sent to the window server, which decodes and executes them bydrawing directly onto the screen, using a CFbsBitGc – a class derivedfrom CGraphicsContext for drawing onto bitmapped devices.
Priorto drawing, the window server sets up a clipping region to ensurethat only the correct region of the correct window can be changed,whatever the current state of overlapping windows on the screen. Thewindow server uses the BITGDI to ‘rasterize’ the drawing commands. Theclient-side buffer, which wraps several window-server commands into asingle client–server transaction, significantly speeds up system graphicsperformance.We can now explain the DrawOneTileNow() function that we sawearlier:void COandXStatusWin::DrawOneTileNow(TBool aLeft) const{...Window().Invalidate(symRect);ActivateGc();Window().BeginRedraw(symRect);DrawOneTile(SystemGc(), symRect, isCross);Window().EndRedraw();DeactivateGc();}This would be a member function of COandXStatusWin, which isderived from CCoeControl.
The central function is DrawOneTile()but this is bracketed by actions necessary to function correctly with thewindow server.Invalidating a regionFirst, we use an Invalidate() function to invalidate the region we areabout to draw. Remember that the window server keeps track of all invalidregions on each window, and clips drawing to the total invalid region.So before you do an application-initiated redraw, you must invalidate theregion you are about to redraw, otherwise nothing will appear (unless theregion happened to be invalid for some other reason).Activating the graphics contextAfter invalidating the region, the CONE system graphics context must beactivated.
CCoeControl::ActivateGc() is coded as:EXPORT_C void CCoeControl::ActivateGc() constSHARING THE SCREEN511{CWindowGc& gc = iCoeEnv->SystemGc();if(iContext)iContext->ActivateContext(gc, *iWin);elsegc.Activate(*iWin);}The usual case just executes gc.Activate() on the control’s window, telling the window server’s client interface to use the CONE systemgraphics context to start drawing to it. The function also resets the windowgraphics context to use default settings. We explain the other case lateron, in Section 17.7, when we discuss the control context.Beginning and ending the redrawImmediately before drawing, we tell the window server we are about tobegin redrawing a particular region. Immediately after redrawing, we tellthe window server that we have finished.
When the BeginRedraw()function is executed by the window server, it has two effects:• the window server sets a clipping region to the intersection of theinvalid region, the rectangle passed into BeginRedraw() (the wholewindow, if no rectangle is passed in), and the region of the windowthat is visible on the screen• the window server then marks the region specified by BeginRedraw() as valid (or, more accurately, it subtracts the rectanglepassed into BeginRedraw(), from its current invalid region).The application’s draw code must then cover every pixel of the regionspecified by BeginRedraw(). If the application’s draw code includesan explicit call to SetClippingRegion(), the region specified isintersected with the clipping region calculated in BeginRedraw().When the application has finished redrawing, it calls EndRedraw().This enables the window server to delete the region object that it allocatedduring BeginRedraw() processing.ConcurrencyYou are probably wondering why the window server marks the regionas valid in BeginRedraw() rather than in EndRedraw().
The reasonis that Symbian OS is a multitasking operating system. The followingtheoretical sequence of events shows why this protocol is needed:• application A issues BeginRedraw() and the affected region ismarked valid on application A’s window512GRAPHICS FOR DISPLAY• application A starts drawing• application B comes to the foreground and its window overwritesapplication A’s• application B terminates, so that application A’s window is againexposed• application A’s window is now invalid and the window server marksit as such• application A continues redrawing and issues EndRedraw() (andnone of this drawing appears on the screen).At the end of this sequence, the region of the screen covered bythe re-exposed region of application A’s window is in an arbitrarystate.