diff -r 51a74ef9ed63 -r ae94777fff8f Symbian3/SDK/Source/GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290-GENID-1-8-1-6-1-1-4-1-6-1-9-1.dita --- a/Symbian3/SDK/Source/GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290-GENID-1-8-1-6-1-1-4-1-6-1-9-1.dita Wed Mar 31 11:11:55 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,552 +0,0 @@ - - - - - -How -to write controls -

Cone itself does not provide any concrete controls. Uikon and the UI variant -libraries provide a large number of 'stock' controls for application writers. -Application writers often need to supplement the standard set of controls -with application specific controls of their own. These may be completely new -controls or, more often, compound controls which contain a number of standard -controls.

-

This section describes how to create controls and how to integrate them -in to the control framework. It is divided into the following sections:

-

Creating -a control

-

Window -owning or not?

-

Creating -a compound control

-

Size, -position and layout

-

Drawing -and refreshing

-

Drawing -backgrounds

-

Drawing -text

-

Drawing -graphics

-

Handling -events

-

Implementing -the Object Provider (MOP) interface

-
Creating a -control

A control is a class which derives from CCoeControl. -It should have a public constructor and, if any leaving function calls or -memory allocations are required during construction, a ConstructL() function. -The majority of re-useable and configurable controls have a ConstructFromResourceL() function -which allows a specific instance of a control to be configured using an application's -resource file. Obviously any memory allocated must be freed in the destructor. -Before a control is drawn to the screen it must be activated. The ActivateL() -function may be overriden to perform last-minute configuration (but -must call the function in the base class).

-Class CMyControl : public CCoeControl - { - public: - CMyControl() ; - void ConstructL(...) ; - // from CCoeControl - void ConsructFromResourceL( TResourceReader& aReader ) ; - private: - ~CMyControl() ; - - // additional functions to handle events - // additional functions to draw the control - // additional functions to determine the size, layout and position the control - }
-
Window owning -or not?

The decision over whether to make a control window owning -or not is usually straightforward. Each view requires a window, so the top-level -control must be window-owning and a child of the AppUi. Below this a window -owning control is normally only necessary where a sense of layering is required: -for instance a pop-up window or a scrolling window. Dialogs and menus are -window owning controls but these are normally implemented in the Uikon and -UI variant libraries and do not require custom derivation from CCoeControl. -Unnecessary window-owning controls should be avoided as they require more -infrastructure, place greater demand on the Window Server and reduce performance.

If -a control must be window owning its window must either be created in the ConstructL() function -or by the caller. The former is preferred. There are several overloads of -the CreateWindowL() and CreateBackedUpWindowL() functions. -Those which do not take a parent parameter create a top-level window which -is a child of the root window.

If a control is not window owning its SetContainerWindowL() function -must be called when it is instantiated.

If it can, the Framework will -automatically set up the parent pointer when the window, or associated window -relationship is established. If it cannot do this, because CreateWindowL() or SetContainerWindowL() did -not provide a CCoeControl, the parent pointer (and MopParent) -may be set expicitly using SetParent() and SetMopParent().

-
Creating a -compound control

Most applications UIs are built from compound -controls. Many custom controls are built up from stock controls and are therefore -also compound controls. When a compound control is constructed it constructs -its components in its ConstructL() function. When it receives -commands itself, such as ActivateL() and DrawNow() it -passes them on to each of its components. In most cases the Framework does -much of the donkey work as long as the compound control has been constructed -correctly.

There are now two methods of creating and managing lodger -controls. The first method described is the one that should be used.

-void MyControl::ConstructL( ... ) - { - // initialise the component array. This must be called once (subsequent calls have no effect) - InitComponentArrayL() ; - - // construct each component control and add it to the component array. - CComponent* myComponent = new (ELeave) CComponent ; - Components().AppendLC( myComponent ) ; // or InsertLC or InsertAfterLC(). Places item on cleanup stack. - myComponent->ConstructL() ; - myComponent->SetThisAndThatL() ; - CleanupStack::Pop( myComponent ) ; - }

The return value of the insert and append methods is -a CCoeControlArray::TCursor object which works as an iterator. -It will remain valid when other items are inserted or deleted, or even if -the whole array is re-ordered.

The insert and append methods leave -the component on the Cleanup Stack using a dedicated Cleanup Item that protects -the parent's array as well as the component itself.

The insert and -append methods allow each component to be given an ID. The ID must be unique -only within the parent so typically a compound control will have an enum listing -each of its children's IDs. CCoeControlArray , accessed -using CCoeControl::Components(), has a ControlById() method -to retrieve components using their IDs.

Components in the array are, -by default, owned by the parent and will be deleted automatically when the -parent is deleted. The default may be overridden using CCoeControlArray::SetControlsOwnedExternally(). -The setting applies to all of the components.

Controls may be removed -from the array using one of the Remove() methods. These do -not delete.

-class CCoeControlArray - ... - public: - IMPORT_C TInt Remove(const CCoeControl* aControl); - IMPORT_C CCoeControl* Remove(TCursor aRemoveAt); - IMPORT_C CCoeControl* RemoveById(TInt aControlId); - ... -

Using the component array as described is now the approved -method of constructing and managing compound controls. In older versions of -Symbian OS a specific method of handling components was not provided and developers -were obliged to design their own. Bypassing the component array is still possible. -It is necessary to allocate and store the components (typically as member -data) and to implement the CountComponentControls() and ComponentControl() functions -to return the number of components and a specified component to the framework. -The new method offers significant advantages when controls are added and removed -dynamically or are dependant on run-time data. The new method is also integrated -with new layout managers.

-
Size, position -and layout

There are several factors which contribute to a control's -size and position. The control itself will require a certain size in order -to display itself (and its data) correctly. The control's container will be -responsible for positioning the control but is also likely to be responsible -for positioning other controls - each of which will have its own requirements. -Additionally there are the requirements of the UI's look and feel that must -be complied with.

Each control is responsible for implementing its -own Size() function.

Until Symbian OS version 9.1 -it was normal to write layout code for simple and compound controls in the SizeChanged() function. -This is called by the framework, as one might expect, when a control's size -(its 'extent') is changed. From 9.1, however, Symbian OS supports the use -of the layout manager interface (MCoeLayoutManager) and -the SizeChanged() function is now implemented in the base -class. (Note that if a control's position is changed, with no size change, -using CCoeControl::SetPosition() its PositionChanged() function -is called and that default implementation of PositionChanged() is -empty).

-class MCoeLayoutManager - ... - protected: - IMPORT_C MCoeLayoutManager(); - - public: - virtual TBool CanAttach() const = 0; - virtual void AttachL(CCoeControl& aCompoundControl) = 0; - virtual void Detach(CCoeControl& aCompoundControl) = 0; - virtual TSize CalcMinimumSize(const CCoeControl& aCompoundControl) const = 0; - virtual void PerformLayout() = 0; - virtual TInt CalcTextBaselineOffset(const CCoeControl& aCompoundControl, const TSize& aSize) const = 0; - virtual void SetTextBaselineSpacing(TInt aBaselineSpacing) = 0; - virtual TInt TextBaselineSpacing() const = 0; - virtual void HandleAddedControlL(const CCoeControl& aCompoundControl, const CCoeControl& aAddedControl) = 0; - virtual void HandleRemovedControl(const CCoeControl& aCompoundControl, const CCoeControl& aRemovedControl) = 0; - virtual TInt HandleControlReplaced(const CCoeControl& aOldControl, const CCoeControl& aNewControl) = 0; - ... -

A layout manager may be attached to a compound control.

-class CCoeControl - ... - protected: - IMPORT_C MCoeLayoutManager* LayoutManager() const; - IMPORT_C virtual void SetLayoutManagerL(MCoeLayoutManager* aLayoutManager); - - public: - IMPORT_C virtual TBool RequestRelayout(const CCoeControl* aChildCtrl); - ...

The default implementations of MinimumSize() and SizeChanged() now -use the layout manager.

-EXPORT_C TSize CCoeControl::MinimumSize() - { - const MCoeLayoutManager* layoutManager = LayoutManager(); - if (layoutManager) - return layoutManager->CalcMinimumSize(*this); - else - return iSize; - } - -EXPORT_C void CCoeControl::SizeChanged() - { - MCoeLayoutManager* layout = LayoutManager(); - if (layout) - layout->PerformLayout(); -

The layout manager is responsible for the size and position -of the component controls. In practice it's likely that the UI variant libraries -will provide concrete layout managers. Application developers should use these -as the basis for control-specific layout managers.

-
Drawing and -refreshing

A fundamental requirement of most controls is that they -are able to render themselves onto the screen. For most controls the drawing -process involves outputting text, painting backgrounds (either plain or from -a bitmap), drawing shapes (graphics objects) and drawing component controls.

Screen -drawing may be initiated by the application itself, following something within -the application changing, or by the Window Server, due to something else in -the system updating the screen while the application is visible. In both cases -the control's Draw() function will be called automatically -by the framework. For compound controls all of the components' Draw() functions -will also be called - unless the component lies completely outside the area -that requires redrawing.

As a control writer you will probably have -to implement a Draw() function.

Here is the signature -for Draw():

private: - void Draw( const TRect& aRect ) const ;

Note that it -is private, takes a const TRect& as a parameter, must -not leave and is const.

It should only be called -by the framework. Application initiated redraws should be through calls to DrawNow(), DrawDeferred() or -custom functions for drawing smaller elements.

The aRect parameter -is the part of the control that requires drawing (refreshing).

The -function is const and non-leaving because it is intended -to support the decoupling of drawing actions from application state.

-
Drawing backgrounds

A -control's background is typically determined by the current colour scheme -or skin. It may be a plain colour or a bitmap. It's also possible that a control -is to appear non-rectangular or transparent in which case some of the background -will be the control underneath. Prior to Symbian OS 9.1 controls were required -to clear and update their whole area and creating these effects was rather -complex. From 9.1 controls are drawn 'backmost first'.

Background -drawing should be done by a dedicated background drawer - i.e. an object which -implements the MCoeControlBackground interface. A background -can be attached to a CCoeControl using SetBackground() and -is used for that control and all of its children. When a control is drawn -the framework looks for the nearest background up the run-time hierarchy and -calls MCoeControlBackground::Draw().

UI variant libraries -typically provide backgrounds. They are not owned by the controls to which -they are attached.

-
Drawing text

Text -must be drawn with the correct color, font, size and direction. As with backgrounds, -these are determined at runtime according to UI customizations. This is achieved -by means of a Text Drawer. Note the use of the XCoeTextDrawer class. -This is a smart pointer (note the use of the -> operator -to access CCoeTextDrawerBase functions) which ensures that -only one text drawer is allocated on the heap at a time.

-XCoeTextDrawer textDrawer( TextDrawer() ); -textDrawer->SetAlignment(iAlignment); -textDrawer->SetMargins(iMargin); -textDrawer->SetLineGapInPixels(iGapBetweenLines); -textDrawer.SetClipRect(aRect); // have to use . [dot] operator for SetClipRect() as not CCoeTextDrawerBase function. - -textDrawer.DrawText(gc, *iTextToDraw, Rect(), *Font()); -

Text drawers are typically provided by the UI variant library -or skin manager. Controls within the run-time hierarchy can set the text drawer -for their children by overriding GetTextDrawer().

Note -that the text drawer expects text to be passed as a TBidiText rather -than a descriptor. Controls should store all display text in TBidiText objects. -Application writers should consider the implications of right-to-left layouts -for languages such as Hebrew and Arabic.

A control's GetTextDrawer() function -might look something like this. It checks on the current state of the control -(IsDimmed()) and passes the call on to a skin manager.

-EXPORT_C void CMyButtonControl::GetTextDrawer(CCoeTextDrawerBase*& aTextDrawer, const CCoeControl* aDrawingControl, TInt /*aKey*/) const - { - const TInt textDrawerIndex = (IsDimmed() ? EButtonTextDimmed : EButtonText); - - SkinManager::GetTextDrawer(aTextDrawer, KSkinUidButton, textDrawerIndex, aDrawingControl); - } -

If the control is drawing text on its own graphics (and does -not care about the text drawer of its parents) it can just create an XCoeTextDrawer object -on the stack in its Draw() method and initiate it from the -skin that it is currently using to draw its graphics, using the CSkinPatch::TextDrawer() method, -like this:

-const CSkinPatch& skin = SkinManager::SkinPatch(KSomeSkinUid, KSomeSkinIndex, this); - -skin.DrawBitmap(gc, Rect(), aRect); -XCoeTextDrawer textDrawer( skin.TextDrawer(KSomeSkinUid, ESomeSkinTextDimmed, this) ); - -const CFont& font = ScreenFont(TCoeFont::NormalFont); -textDrawer.DrawText(gc, iText, rect, font); -

The example above also illustrates how to retrieve the correct -font. CFont objects must not be stored in control member -data as they must change when the control's zoom state changes. Instead, a TCoeFont that -represents a font's size (logical or absolute in pixels) and style (plain, -bold, italic, subscript, or superscript) should be used.

-class TCoeFont - ... - public: - IMPORT_C TCoeFont(TLogicalSize aSize, TInt aStyle, TInt aFlags = ENoFlags); - IMPORT_C TCoeFont(TInt aHeightInPixels, TInt aStyle, TInt aFlags = ENoFlags); - IMPORT_C TCoeFont(const TCoeFont& aFont); - IMPORT_C TCoeFont(); - ...

By creating a TCoeFont object -describing the properties of the desired font, a CFont object -reference (needed to actually draw the text) can be fetched from the CCoeFontProvider. -A font provider can be attached to any CCoeControl and -will keep information about the typeface used by that control and all controls -below. A default font provider is attached to the CCoeEnv.

-class CCoeControl - ... - public: - IMPORT_C const CCoeFontProvider& FindFontProvider() const; - IMPORT_C void SetFontProviderL(const CCoeFontProvider& aFontProvider); - ... -

To get hold of the CFont object -a Draw() method can be implemented like this:

-void CMyControl::Draw(const TRect& aRect) - { - const CCoeFontProvider& fontProvider = FindFontProvider(); - const CFont& font = fontProvider.Font(TCoeFont::LegendFont(), AccumulatedZoom()); - - XCoeTextDrawer textDrawer( TextDrawer() ); - textDrawer->SetAlignment(EHCenterVCenter); - textDrawer.DrawText(gc, iText, rect, font); - } -

For convenience there’s a CCoeControl::ScreenFont() method -that locates the font provider and calls it with the control’s accumulated -zoom:

-class CCoeControl - ... - protected: - IMPORT_C const CFont& ScreenFont(const TCoeFont& aFont) const; - ... -
-
Drawing graphics

Controls -draw graphics objects - lines, rectangles, shapes and bitmaps to a graphics -context. The graphics context is provided by the Window Server and -represents a group of settings appropriate for the physical device that is -ultimately being drawn to. In most cases the device is a screen and a graphics -context should be obtained using CCoeControl::SystemGc(). CCoeControl::SystemGc() gets -the current graphics context from the run-time hierarchy. Controls in the -hierarchy may override graphics context settings which will then be passed -on to their children. Controls should not get their graphics context directly -from CCoeEnv as to do so would bypass the hierarchy.

-void CMyControl::Draw( const TRect& aRect ) - { - CWindowGc& gc = SystemGc() ; // get gc from run time hierarchy - TRect rect = TRect( Size() ) ; - if ( IsBlanked() ) - { - // blank out the entire control - gc.SetPenStyle( CGraphicsContext::ENullPen ) ; - gc.SetBrushStyle( CGraphicsContext::ESolidBrush ) ; - TRgb blankColor = BlankingColor() ; - gc.SetBrushColor( blankColor ) ; - gc.DrawRect( rect ) ; - } - else - { - // draw masked bitmap in the centre of the control - // The parent will draw the background - TInt id = BitMapId() ; - - TInt x = Size().iWidth - iBitmap[id]->SizeInPixels().iWidth ; - TInt y = Size().iHeight - iBitmap[id]->SizeInPixels().iHeight ; - - TPoint pos = Rect().iTl ; - pos.iX = pos.iX + ( x / 2 ) ; - pos.iY = pos.iY + ( y / 2 ) ; - - gc.BitBltMasked( pos, iBitmap[id], rect, iMaskBitmap, ETrue ) ; - } - }

Before a graphics context can be used it must be activated. -After use it must be deactivated. Activation and deactivation are done automatically -by the framework in DrawNow(), DrawDeferred() and HandleRedrawEvent() but -must be done explicitly for any other application initiated drawing by calling ActivateGc() and DeactivateGc().

Controls may implement partial drawing to speed up performance. The Draw() function -may be split into sub functions: DrawThis(), DrawThat(), DrawTheOther(). -Each of these requires a corresponding DrawThisNow() and/or DrawThisDeferred() function.

-CMyControl::Draw() - { - DrawThis() ; - DrawThat() ; - DrawTheOther() ; - } -CMyControl::DrawThisNow() - { - ActivateGc() ; - DrawThis() ; - DeactivateGc() ; - }
-
Handling events

The -Control Framework supports user interaction in two ways: key-press events -and pointer events. Both types of event arrive through the Window Server though -they each arrive in a slightly different way. Both are closely related to -the concept of 'focus' and the location of the cursor.

Handling -key events

Key events are delivered to the AppUi. The Window -Server channels them through the root window of its current window group which -maps to the AppUi foreground application. The AppUi offers each key event -to each of the controls on its control stack in priority order until one of -the controls 'consumes' it.

To receive key events a control must implement CCoeControl::OfferKeyEventL(). -If it handles the event it must return EKeyWasConsumed: If -it doesn't it must return EKeyWasNotConsumed so that the -next control on the stack receives it.

-TKeyResponse CMyControl::OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType) - { - TKeyResponse returnValue = EKeyWasConsumed ; - switch( aKeyEvent.iCode ) - { - case EKeyEnter : - // do stuff - break ; - case EKeyEscape : - // do stuff : - break ; - - ... - - default : - // did not recognise key event - returnValue = EKeyWasNotConsumed ; - break ; - } - return returnValue ; - }

The handling of key events will depend on the design -and purpose of the control itself. Compound controls might need to keep track -of an input focus, or cursor, and to pass key events amongst -its lodgers. Input into one lodger might have an effect on another - pressing -a navigation key might cause one control to lose the highlight and another -to gain it, pressing a number key might cause a text editor to grow which -might, in turn, require all of the components below it to shuffle downwards -and a scroll bar to become visible (which might also require some controls -to be laid out differently).

Handling pointer events

Pointer -events are slightly different as the position of the pointer, rather than -of the focus, is significant. The Window Server passes a pointer event to -the top-most visible window at the point of contact. The Framework uses the -functions ProcessPointerEventL() and HandlePointerEventL() to -work down the hierarchy. The Framework also uses the MCoeControlObserver and -focussing mechanisms to inform the observer of the controls that will be losing -and gaining the focus.

Using the Control Observer Interface

The -Control Framework facilitates this type of relationship between a container -and its lodgers with the MCoeControlObserver interface. Typically -the container implements the interface and becomes the observer for each lodger -that can receive user input (focus). There is only one function in MCoeControlObserver:

virtual void HandleControlEventL( CCoeControl *aControl, TCoeEvent aEventType ) = 0 ;

and it is called when an observed control calls

void CCoeControl::ReportEvent( MCoeControlObserver::TCoeEvent aEvent ) ;

A control can have only one observer (or none) so ReportEvent() does -not need to specify an observer. An observer may observe any number of controls -so HandleControlEventL() takes the observed control as a -parameter. The other piece of information passed to the observer is a TCoeEvent.

enum TCoeEvent - { - EEventRequestExit, - EEventRequestCancel, - EEventRequestFocus, - EEventPrepareFocusTransition, - EEventStateChanged, - EEventInteractionRefused - };

CCoeControl also provides IsFocused(), SetFocused() and IsNonFocussing(). Note that Framework does not attempt to ensure exclusivity of focus, nor -does it give any visible indication of focus. It is up to the application -developer to ensure that only one control has the focus at a time, that the -focus is correctly transferred between controls, that only appropriate controls -receive the focus and that the focus is visible at all times.

void CContainer::HandleControlEventL(CCoeControl* aControl, TCoeEvent aEventType) - { - switch (aEventType) - { - case EEventRequestFocus: - { - if( !(aControl->IsFocussed()) ) - { - aControl->SetFocus( ETrue ) ; - // remove focus from other controls - for ( Tint ii = 0 ; ii < CountComponentControls() ; ii++ ) - { - CCoeControl* ctl = ComponentControl( ii ) ; - if( ( ctl != aControl ) && !( ctl->IsNonFocussing() ) ) - { - aControl->SetFocus( EFalse ) ; - } - } - } - } - break; - ... - } - }

Control developers may implement HandlePointerEventL(), -which is a virtual function, to perform pointer event functionality. The implementation -must, however, call the base class function.

Controls may modify their -pointer area, possibly if they appear non-rectangular or overlap. To do so -requires the addition of a hit test which describes a hit-test region. A hit-test -region may cover all or part of one or more controls. A hit for a control -is registered in the area covered by both the control and its associated hit -test.

The diagram below represents three controls, each of which is -rectangular but which appears on the screen as a non-rectangular bitmap. Only -a hit on a bitmap area should register. This could be achieved by defining -a single hit-test region in the shape (and position) of the three blue areas -and associating it with each of the controls. The class that implements the -hit-test region must implement the MCoeControlHitTest interface.

- Hit-test region example - - -class MCoeControlHitTest - ... - public: - virtual TBool HitRegionContains( const TPoint& aPoint, const CCoeControl& aControl ) const = 0; -

A hit test is associated with a control using CCoeControl::SetHitText(). -The base class implementation of HandlePointerEventL() performs -the following test:

- ... - const MCoeControlHitTest* hitTest = ctrl->HitTest() ; - if( hitTest ) - { - if( hitTest->HitRegionContains( aPointerEvent.iPosition, *ctrl ) && - ctrl->Rect().Contains( aPointerEvent.iPosition ) )

Note -that this is performed by a container when deciding which lodger to pass the -event onto. This snippet also illustrates how a control can find where (iPosition) -the pointer event actually occurred.

Pointer support includes dragging -& grabbing. See TPointerEvent.

-
Implementing -the Object Provider (MOP) interface

The Object -Provider mechanism exists to allow a control to call a function on -another control in the hierarchy for which it does not have a reference. It -simply calls MopGetObject() specifying the interface containing -the function. It may also call MopGetObjectNoChaining() to -inquire of a specific object whether it supports the requested interface.

Only -controls which wish to supply an interface require customisation. In order -to be identifiable an interface must have an associated UID. The following -code samples show how CEikAlignedControl implements and -supplies MEikAlignedControl:

-class MEikAlignedControl - ... - public: - DECLARE_TYPE_ID( 0x10A3D51B ) // Symbian allocated UID identifies this interface - ... - -class CEikAlignedControl : public CCoeControl, public MEikAlignedControl - { - ... - private: //from CCoeControl - IMPORT_C TTypeUid::Ptr MopSupplyObject( TTypeUid aId ) ; - ... - -EXPORT_C TTypeUid::Ptr CEikAlignedControl::MopSupplyObject( TTypeUid aId ) - { - if( aId.iUid == MEikAlignedControl::ETypeId ) - return aId.MakePtr( static_cast<MEikAlignedControl*>( this ) ) ; - - return CCoeControl::MopSupplyObject( aId ) ; // must call base class! - } -

To get an interface from the object provider framework the -caller must use a pointer to the interface.

- ... - MEikAlignedControl* alignedControl = NULL ; - MyControl->MopGetObject( alignedControl ) ; - if ( alignedControl ) - { - ... // etc.

To get an interface from a specific object -the caller may use the no-chaining function call.

- ... - MEikAlignedControl* alignedControl = NULL ; - aControl->MopGetObjectNoChaining( alignedControl ) ; - if ( alignedControl ) - { - ... // etc.
-
\ No newline at end of file