Week 23 contribution of PDK documentation content. See release notes for details. Fixes bugs Bug 2714, Bug 462.
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. -->
<!-- This component and the accompanying materials are made available under the terms of the License
"Eclipse Public License v1.0" which accompanies this distribution,
and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". -->
<!-- Initial Contributors:
Nokia Corporation - initial contribution.
Contributors:
-->
<!DOCTYPE concept
PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
<concept id="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290" xml:lang="en"><title>How to
write controls</title><prolog><metadata><keywords/></metadata></prolog><conbody>
<p>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. </p>
<p>This section describes how to create controls and how to integrate them
in to the control framework. It is divided into the following sections: </p>
<p><xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-341017B2-8CF9-5124-8D20-C75A8A51F0B7">Creating
a control</xref> </p>
<p><xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-3F0E8223-2218-5C95-AFBC-D66AD1DB12A7">Window
owning or not?</xref> </p>
<p><xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-795EBF51-AD26-513E-9A82-A99C629CE779">Creating
a compound control</xref> </p>
<p><xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-795EBF51-AD26-513E-9A82-A99C629CE779">Size,
position and layout</xref> </p>
<p><xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-351911EE-87C7-5D11-8434-BA7FD3741745">Drawing
and refreshing</xref> </p>
<p> <xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-FF7DB067-24AD-50C3-BF52-952F836609B0">Drawing
backgrounds</xref> </p>
<p> <xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-54C6A39A-CBD0-52E5-8CD0-76BE22247A54">Drawing
text</xref> </p>
<p> <xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-D1AED2A8-94AC-54BB-9CEB-C8C3643AFBBD">Drawing
graphics</xref> </p>
<p><xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-AC723EE4-1482-59C5-9F13-CAE119C7800D">Handling
events</xref> </p>
<p><xref href="GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290.dita#GUID-B84FA223-3DFD-58C5-8CEF-C5AA73AA6290/GUID-FE221E89-1817-5A73-8FBA-212FBC030766">Implementing
the Object Provider (MOP) interface</xref> </p>
<section id="GUID-341017B2-8CF9-5124-8D20-C75A8A51F0B7"><title>Creating a
control</title> <p>A control is a class which derives from <xref href="GUID-B06F99BD-F032-3B87-AB26-5DD6EBE8C160.dita"><apiname>CCoeControl</apiname></xref>.
It should have a public constructor and, if any leaving function calls or
memory allocations are required during construction, a <codeph>ConstructL()</codeph> function.
The majority of re-useable and configurable controls have a <codeph>ConstructFromResourceL()</codeph> 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 <codeph>ActivateL()
function</codeph> may be overriden to perform last-minute configuration (but
must call the function in the base class). </p> <codeblock id="GUID-18D48E7E-9846-5CE0-BE1E-FAB723419B90" xml:space="preserve">
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
}</codeblock> </section>
<section id="GUID-3F0E8223-2218-5C95-AFBC-D66AD1DB12A7"><title>Window owning
or not? </title> <p>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 <xref href="GUID-B06F99BD-F032-3B87-AB26-5DD6EBE8C160.dita"><apiname>CCoeControl</apiname></xref>.
Unnecessary window-owning controls should be avoided as they require more
infrastructure, place greater demand on the Window Server and reduce performance. </p> <p>If
a control must be window owning its window must either be created in the <codeph>ConstructL()</codeph> function
or by the caller. The former is preferred. There are several overloads of
the <codeph>CreateWindowL()</codeph> and <codeph>CreateBackedUpWindowL()</codeph> functions.
Those which do not take a parent parameter create a top-level window which
is a child of the root window. </p> <p>If a control is not window owning its <codeph>SetContainerWindowL()</codeph> function
must be called when it is instantiated. </p> <p>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 <codeph>CreateWindowL()</codeph> or <codeph>SetContainerWindowL()</codeph> did
not provide a <xref href="GUID-B06F99BD-F032-3B87-AB26-5DD6EBE8C160.dita"><apiname>CCoeControl</apiname></xref>, the parent pointer (and MopParent)
may be set expicitly using <codeph>SetParent()</codeph> and <codeph>SetMopParent()</codeph>. </p> </section>
<section id="GUID-795EBF51-AD26-513E-9A82-A99C629CE779"><title>Creating a
compound control</title> <p>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 <codeph>ConstructL()</codeph> function. When it receives
commands itself, such as <codeph>ActivateL()</codeph> and <codeph>DrawNow()</codeph> 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. </p> <p>There are now two methods of creating and managing lodger
controls. The first method described is the one that should be used. </p> <codeblock id="GUID-F77661F7-6A68-58DB-B875-9D957A937617" xml:space="preserve">
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 ) ;
}</codeblock> <p>The return value of the insert and append methods is
a <xref href="GUID-2D8BFBA2-79AC-364D-875D-E863CD4A2FE1.dita#GUID-2D8BFBA2-79AC-364D-875D-E863CD4A2FE1/GUID-AD37D86C-F9FA-3514-BFD4-7139A0B8543F"><apiname>CCoeControlArray::TCursor</apiname></xref> 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. </p> <p>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. </p> <p>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. <xref href="GUID-2D8BFBA2-79AC-364D-875D-E863CD4A2FE1.dita"><apiname>CCoeControlArray</apiname></xref> , accessed
using <codeph>CCoeControl::Components()</codeph>, has a <codeph>ControlById()</codeph> method
to retrieve components using their IDs. </p> <p>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 <codeph>CCoeControlArray::SetControlsOwnedExternally()</codeph>.
The setting applies to all of the components. </p> <p>Controls may be removed
from the array using one of the <codeph>Remove()</codeph> methods. These do
not delete. </p> <codeblock id="GUID-9F9EC327-E7DE-59B8-838B-F5052E3750D3" xml:space="preserve">
class CCoeControlArray
...
public:
IMPORT_C TInt Remove(const CCoeControl* aControl);
IMPORT_C CCoeControl* Remove(TCursor aRemoveAt);
IMPORT_C CCoeControl* RemoveById(TInt aControlId);
...
</codeblock> <p>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 <codeph>CountComponentControls()</codeph> and <codeph>ComponentControl()</codeph> 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. </p> </section>
<section id="GUID-7E144310-9AF4-50F1-AD8A-9F9E05D554D1"><title>Size, position
and layout</title> <p>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. </p> <p>Each control is responsible for implementing its
own <codeph>Size()</codeph> function. </p> <p>Until Symbian OS version 9.1
it was normal to write layout code for simple and compound controls in the <codeph>SizeChanged()</codeph> 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 (<xref href="GUID-A622B8C7-60F4-38E8-B102-14883BCBA249.dita"><apiname>MCoeLayoutManager</apiname></xref>) and
the <codeph>SizeChanged()</codeph> function is now implemented in the base
class. (Note that if a control's position is changed, with no size change,
using <codeph>CCoeControl::SetPosition()</codeph> its <codeph>PositionChanged()</codeph> function
is called and that default implementation of <codeph>PositionChanged()</codeph> is
empty). </p> <codeblock id="GUID-53A10820-550A-52C2-AE80-D556FD77E8FA" xml:space="preserve">
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;
...
</codeblock> <p>A layout manager may be attached to a compound control. </p> <codeblock id="GUID-ACCD143C-8618-5606-B749-D7EFE5C83ACD" xml:space="preserve">
class CCoeControl
...
protected:
IMPORT_C MCoeLayoutManager* LayoutManager() const;
IMPORT_C virtual void SetLayoutManagerL(MCoeLayoutManager* aLayoutManager);
public:
IMPORT_C virtual TBool RequestRelayout(const CCoeControl* aChildCtrl);
...</codeblock> <p>The default implementations of <codeph>MinimumSize()</codeph> and <codeph>SizeChanged()</codeph> now
use the layout manager. </p> <codeblock id="GUID-88340A33-A58C-5856-AD47-C2E1637DD45F" xml:space="preserve">
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();
</codeblock> <p>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. </p> </section>
<section id="GUID-351911EE-87C7-5D11-8434-BA7FD3741745"><title> Drawing and
refreshing</title> <p>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. </p> <p>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 <codeph>Draw()</codeph> function will be called automatically
by the framework. For compound controls all of the components' <codeph>Draw()</codeph> functions
will also be called - unless the component lies completely outside the area
that requires redrawing. </p> <p>As a control writer you will probably have
to implement a <codeph>Draw()</codeph> function. </p> <p>Here is the signature
for <codeph>Draw()</codeph>: </p> <codeblock id="GUID-7A83E8B5-9673-5EBC-B628-674734A07E0F" xml:space="preserve">private:
void Draw( const TRect& aRect ) const ;</codeblock> <p>Note that it
is private, takes a <codeph>const TRect&</codeph> as a parameter, must
not leave and is <codeph>const</codeph>. </p> <p>It should only be called
by the framework. Application initiated redraws should be through calls to <codeph>DrawNow()</codeph>, <codeph>DrawDeferred()</codeph> or
custom functions for drawing smaller elements. </p> <p>The <codeph>aRect</codeph> parameter
is the part of the control that requires drawing (refreshing). </p> <p>The
function is <codeph>const</codeph> and non-leaving because it is intended
to support the decoupling of drawing actions from application state. </p> </section>
<section id="GUID-FF7DB067-24AD-50C3-BF52-952F836609B0"><title>Drawing backgrounds</title> <p>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'. </p> <p>Background
drawing should be done by a dedicated background drawer - i.e. an object which
implements the <xref href="GUID-88936D48-B801-3D9C-8A9D-3498807937CE.dita"><apiname>MCoeControlBackground</apiname></xref> interface. A background
can be attached to a <xref href="GUID-B06F99BD-F032-3B87-AB26-5DD6EBE8C160.dita"><apiname>CCoeControl</apiname></xref> using <codeph>SetBackground()</codeph> 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 <codeph>MCoeControlBackground::Draw()</codeph>. </p> <p>UI variant libraries
typically provide backgrounds. They are not owned by the controls to which
they are attached. </p> </section>
<section id="GUID-54C6A39A-CBD0-52E5-8CD0-76BE22247A54"><title>Drawing text</title> <p>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 <xref href="GUID-2280260C-3A4F-3C1E-ADF2-3219ED7FE0DE.dita"><apiname>XCoeTextDrawer</apiname></xref> class.
This is a smart pointer (note the use of the <codeph>-></codeph> operator
to access <xref href="GUID-C8C7B785-B3CF-3488-AEB1-BE0A70F6C1F2.dita"><apiname>CCoeTextDrawerBase</apiname></xref> functions) which ensures that
only one text drawer is allocated on the heap at a time. </p> <codeblock id="GUID-511A9CEE-F02C-5667-A334-3C61A2428F0F" xml:space="preserve">
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());
</codeblock> <p>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 <codeph>GetTextDrawer()</codeph>. </p> <p>Note
that the text drawer expects text to be passed as a <xref href="GUID-07D86324-ED54-3FCD-B301-53B7A6E04AE3.dita"><apiname>TBidiText</apiname></xref> rather
than a descriptor. Controls should store all display text in <xref href="GUID-07D86324-ED54-3FCD-B301-53B7A6E04AE3.dita"><apiname>TBidiText</apiname></xref> objects.
Application writers should consider the implications of right-to-left layouts
for languages such as Hebrew and Arabic. </p> <p>A control's <codeph>GetTextDrawer()</codeph> function
might look something like this. It checks on the current state of the control
(<codeph>IsDimmed()</codeph>) and passes the call on to a skin manager. </p> <codeblock id="GUID-155AD8EC-17DD-5155-BE20-23246A33695C" xml:space="preserve">
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);
}
</codeblock> <p>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 <xref href="GUID-2280260C-3A4F-3C1E-ADF2-3219ED7FE0DE.dita"><apiname>XCoeTextDrawer</apiname></xref> object
on the stack in its <codeph>Draw()</codeph> method and initiate it from the
skin that it is currently using to draw its graphics, using the <codeph>CSkinPatch::TextDrawer()</codeph> method,
like this: </p> <codeblock id="GUID-3DFDBF27-8744-5283-AC7B-EC512EEEBB7D" xml:space="preserve">
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);
</codeblock> <p>The example above also illustrates how to retrieve the correct
font. <xref href="GUID-2A12FE3B-47F2-3016-8161-A971CA506491.dita"><apiname>CFont</apiname></xref> objects must not be stored in control member
data as they must change when the control's zoom state changes. Instead, a <xref href="GUID-463C1928-878D-3B06-ABFD-178BE1BAD776.dita"><apiname>TCoeFont</apiname></xref> that
represents a font's size (logical or absolute in pixels) and style (plain,
bold, italic, subscript, or superscript) should be used. </p> <codeblock id="GUID-864A57DB-BCDA-50B6-B876-9EF62CFB27C6" xml:space="preserve">
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();
...</codeblock> <p>By creating a <xref href="GUID-463C1928-878D-3B06-ABFD-178BE1BAD776.dita"><apiname>TCoeFont</apiname></xref> object
describing the properties of the desired font, a <xref href="GUID-2A12FE3B-47F2-3016-8161-A971CA506491.dita"><apiname>CFont</apiname></xref> object
reference (needed to actually draw the text) can be fetched from the <xref href="GUID-372CDE49-2148-3A21-8EA3-8D4656548C23.dita"><apiname>CCoeFontProvider</apiname></xref>.
A font provider can be attached to any <xref href="GUID-B06F99BD-F032-3B87-AB26-5DD6EBE8C160.dita"><apiname>CCoeControl</apiname></xref> and
will keep information about the typeface used by that control and all controls
below. A default font provider is attached to the <xref href="GUID-12A9389D-363B-3F54-857F-89EE0EDCDF40.dita"><apiname>CCoeEnv</apiname></xref>. </p> <codeblock id="GUID-C46BEB52-33DB-564B-9A64-53763C7CD226" xml:space="preserve">
class CCoeControl
...
public:
IMPORT_C const CCoeFontProvider& FindFontProvider() const;
IMPORT_C void SetFontProviderL(const CCoeFontProvider& aFontProvider);
...
</codeblock> <p>To get hold of the <xref href="GUID-2A12FE3B-47F2-3016-8161-A971CA506491.dita"><apiname>CFont</apiname></xref> object
a <codeph>Draw()</codeph> method can be implemented like this: </p> <codeblock id="GUID-45292362-1E39-59B0-AC7F-14C98592F059" xml:space="preserve">
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);
}
</codeblock> <p>For convenience there’s a <codeph>CCoeControl::ScreenFont()</codeph> method
that locates the font provider and calls it with the control’s accumulated
zoom: </p> <codeblock id="GUID-5974F900-84B7-5262-8428-88797911F94A" xml:space="preserve">
class CCoeControl
...
protected:
IMPORT_C const CFont& ScreenFont(const TCoeFont& aFont) const;
...
</codeblock> </section>
<section id="GUID-D1AED2A8-94AC-54BB-9CEB-C8C3643AFBBD"><title>Drawing graphics</title><p>Controls
draw graphics objects - lines, rectangles, shapes and bitmaps to a <keyword>graphics
context</keyword>. 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 <codeph>CCoeControl::SystemGc()</codeph>. <codeph>CCoeControl::SystemGc()</codeph> 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. <i>Controls should not get their graphics context directly
from CCoeEnv as to do so would bypass the hierarchy</i>. </p> <codeblock id="GUID-DC2B7A84-BA93-5AE0-BABA-9CF6B156A54E" xml:space="preserve">
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 ) ;
}
}</codeblock> <p>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 <codeph>DrawNow()</codeph>, <codeph>DrawDeferred()</codeph> and <codeph>HandleRedrawEvent()</codeph> but
must be done explicitly for any other application initiated drawing by calling <codeph>ActivateGc()</codeph> and <codeph>DeactivateGc()</codeph>. </p> <p>Controls may implement partial drawing to speed up performance. The <codeph>Draw()</codeph> function
may be split into sub functions: <codeph>DrawThis()</codeph>, <codeph>DrawThat()</codeph>, <codeph>DrawTheOther()</codeph>.
Each of these requires a corresponding <codeph>DrawThisNow()</codeph> and/or <codeph>DrawThisDeferred()</codeph> function. </p> <codeblock id="GUID-EDC0D6F1-61BC-552F-B56D-C32148ADECA3" xml:space="preserve">
CMyControl::Draw()
{
DrawThis() ;
DrawThat() ;
DrawTheOther() ;
}</codeblock> <codeblock id="GUID-E026186D-7B55-5AB5-9391-8587E3510D6D" xml:space="preserve">
CMyControl::DrawThisNow()
{
ActivateGc() ;
DrawThis() ;
DeactivateGc() ;
}</codeblock> </section>
<section id="GUID-AC723EE4-1482-59C5-9F13-CAE119C7800D"><title>Handling events </title> <p>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. </p> <p><b>Handling
key events </b> </p> <p>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. </p> <p>To receive key events a control must implement <codeph>CCoeControl::OfferKeyEventL()</codeph>.
If it handles the event it must return <codeph>EKeyWasConsumed</codeph>: If
it doesn't it must return <codeph>EKeyWasNotConsumed</codeph> so that the
next control on the stack receives it. </p> <codeblock id="GUID-448E9DF8-9D63-5BA4-94A7-4FB4B96A6DEA" xml:space="preserve">
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 ;
}</codeblock> <p>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 <b>focus</b>, or <b>cursor</b>, 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). </p> <p><b>Handling pointer events </b> </p> <p>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 <codeph>ProcessPointerEventL()</codeph> and <codeph>HandlePointerEventL()</codeph> to
work down the hierarchy. The Framework also uses the <xref href="GUID-A2BF9AE8-CF42-3D94-8763-66DB11EDDA46.dita"><apiname>MCoeControlObserver</apiname></xref> and
focussing mechanisms to inform the observer of the controls that will be losing
and gaining the focus. </p> <p><b>Using the Control Observer Interface</b></p><p>The
Control Framework facilitates this type of relationship between a container
and its lodgers with the <codeph>MCoeControlObserver</codeph> 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 <codeph>MCoeControlObserver</codeph>:</p><codeblock xml:space="preserve">virtual void HandleControlEventL( CCoeControl *aControl, TCoeEvent aEventType ) = 0 ;</codeblock><p>and it is called when an observed control calls</p><codeblock xml:space="preserve">void CCoeControl::ReportEvent( MCoeControlObserver::TCoeEvent aEvent ) ;</codeblock><p>A control can have only one observer (or none) so <codeph>ReportEvent()</codeph> does
not need to specify an observer. An observer may observe any number of controls
so <codeph>HandleControlEventL()</codeph> takes the observed control as a
parameter. The other piece of information passed to the observer is a <codeph>TCoeEvent</codeph>.</p><codeblock xml:space="preserve">enum TCoeEvent
{
EEventRequestExit,
EEventRequestCancel,
EEventRequestFocus,
EEventPrepareFocusTransition,
EEventStateChanged,
EEventInteractionRefused
};</codeblock><p><codeph>CCoeControl</codeph> also provides <codeph>IsFocused()</codeph>, <codeph>SetFocused()</codeph> and <codeph>IsNonFocussing()</codeph>. 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.</p><codeblock xml:space="preserve">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;
...
}
}</codeblock> <p>Control developers may implement <codeph>HandlePointerEventL()</codeph>,
which is a virtual function, to perform pointer event functionality. The implementation
must, however, call the base class function. </p> <p>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. </p> <p>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. </p> <fig id="GUID-5C856964-8553-543A-B7E2-8D5DCA9BF52C">
<title> Hit-test region example </title>
<image href="GUID-CF34E1C9-48E5-5B91-A48E-C68E647116A0_d0e47970_href.png" placement="inline"/>
</fig> <codeblock id="GUID-D2AF9CEB-3072-5239-A157-D19852076CEF" xml:space="preserve">
class MCoeControlHitTest
...
public:
virtual TBool HitRegionContains( const TPoint& aPoint, const CCoeControl& aControl ) const = 0;
</codeblock> <p>A hit test is associated with a control using <codeph>CCoeControl::SetHitText()</codeph>.
The base class implementation of <codeph>HandlePointerEventL()</codeph> performs
the following test: </p> <codeblock id="GUID-35C26C5B-A19A-528A-B5BE-B8177F81B05D" xml:space="preserve">
...
const MCoeControlHitTest* hitTest = ctrl->HitTest() ;
if( hitTest )
{
if( hitTest->HitRegionContains( aPointerEvent.iPosition, *ctrl ) &&
ctrl->Rect().Contains( aPointerEvent.iPosition ) )</codeblock> <p>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 (<codeph>iPosition</codeph>)
the pointer event actually occurred. </p> <p>Pointer support includes dragging
& grabbing. See <xref href="GUID-1FFA0073-3D83-388E-A824-08C31F90CC54.dita"><apiname>TPointerEvent</apiname></xref>. </p> </section>
<section id="GUID-FE221E89-1817-5A73-8FBA-212FBC030766"><title>Implementing
the Object Provider (MOP) interface</title> <p>The <xref href="GUID-F32E2F00-B68F-59B2-AABA-181E16354C86.dita">Object
Provider mechanism</xref> 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 <codeph>MopGetObject()</codeph> specifying the interface containing
the function. It may also call <codeph>MopGetObjectNoChaining()</codeph> to
inquire of a specific object whether it supports the requested interface. </p> <p>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 <xref href="GUID-0DC77F9B-718A-31DF-B076-3C3F83378BF4.dita"><apiname>CEikAlignedControl</apiname></xref> implements and
supplies <codeph>MEikAlignedControl</codeph>: </p> <codeblock id="GUID-9926598F-163B-5ACF-B320-66B43D331E1A" xml:space="preserve">
class MEikAlignedControl
...
public:
DECLARE_TYPE_ID( 0x10A3D51B ) // Symbian allocated UID identifies this interface
...
</codeblock> <codeblock id="GUID-1C2FE28B-7A1F-5726-8690-50FBA8672A2C" xml:space="preserve">
class CEikAlignedControl : public CCoeControl, public MEikAlignedControl
{
...
private: //from CCoeControl
IMPORT_C TTypeUid::Ptr MopSupplyObject( TTypeUid aId ) ;
...
</codeblock> <codeblock id="GUID-03CD8613-9C1D-556C-94B7-5D9A1C23FF83" xml:space="preserve">
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!
}
</codeblock> <p>To get an interface from the object provider framework the
caller must use a pointer to the interface. </p> <codeblock id="GUID-CB641ADD-5AFE-5D72-A909-CD3F0C3BAA9A" xml:space="preserve">
...
MEikAlignedControl* alignedControl = NULL ;
MyControl->MopGetObject( alignedControl ) ;
if ( alignedControl )
{
... // etc.</codeblock> <p>To get an interface from a specific object
the caller may use the no-chaining function call. </p> <codeblock id="GUID-AEB5EB12-6289-5DCB-BF07-71B8F7A67E33" xml:space="preserve">
...
MEikAlignedControl* alignedControl = NULL ;
aControl->MopGetObjectNoChaining( alignedControl ) ;
if ( alignedControl )
{
... // etc.</codeblock> </section>
</conbody></concept>