|
|
|
UIQ 3 introduces an extensive set of changes over previous releases of UIQ. This document is intended to explain the motives behind introducing these changes, as well as to provide developers with an informative guide to migrating from previous UIQ releases to UIQ 3
Please observe that the target audience of this document is intended to have previous experience of working with developing applications for the UIQ user interface (UI) platform. Hence, we assume that the reader has basic knowledge of the terminology, the UI framework and controls as they exist in previous UIQ releases.
The main objectives behind creating this new release of UIQ can be summarized as follows:
Ensure that UIQ is a viable option for mid-tier phones by enabling keypad input as well as touch-screen input,
Increase the flexibility of UIQ further to enable a wider variety of UIQ phones to be created from a single code line by, for example, supporting a range of screen sizes,
Increase and enhance the customization features of UIQ to ensure simpler and faster branding and differentiation of UIQ 3 devices,
Improve public APIs to make it easier for developers to create applications for UIQ 3.
The UIQ product has been updated with the above objectives in mind and the areas mainly affected have been the Application Framework and User Interface Controls. A number of new components have also been introduced. These include the Command Processing Framework, the Layout Management Framework, and support for dynamic specification of user interface configurations.
Changes to existing components as well as the newly introduced components will be presented in this document.
Before you can use the new UIQ 3 framework, you must ensure that your application is designed in a way that the framework supports.
Each application has a single AppUi which creates one or more views to handle drawing and screen interaction.
Each application has its own unique UID allocated from Symbian.
Each view has a view ID (TVwsViewId) that uniquely identifies that view. A view ID includes the application UID and a second UID for the particular view. The second UID only needs to be unique within the application.
The only new classes in the block diagram below are CQikViewBase and CQikMultiPageViewBase. CQikViewBase enforces the use of unique view IDs, which is a prerequisite for Dynamic Navigational Links.
Block Diagram
Some uikon components have now been moved to QEikStd.
The former QikCtl has been split into QikCtl and QikCore. Now, QikCtl contains only UI controls while everything else, such as CQikAppUi and CQikApplication, has been put in QikCore.
QikLbx is a new component in Qikon.
Component changes overview
In this section we describe the improvements and additions that have been made to the Symbian and UIQ application frameworks.
The CONE framework, which is the foundation for all UI widgets and is owned by Symbian, has been extensively improved. This section describes the changes in detail and their consequences on legacy code.
To simplify construction of compound controls, a new CCoeControlArray member has been added to the CCoeControl class. This array can be used to hold pointers to all the control’s component controls, also known as child controls, and is accessed through the new CCoeControl::Components() method. Before you can start using the CCoeControlArray API, the array must be created by calling CCoeControl::InitComponentArrayL() once. Calling it a second time has no effect.
To add a child control the array, call one of these three methods:
class CCoeControlArray
public:
IMPORT_C TCursor AppendLC(CCoeControl* aControl, TInt aControlId = KCoeNoControlId);
IMPORT_C TCursor InsertAfterLC(TInt aInsertAfterId, CCoeControl* aControl, TInt aControlId = KCoeNoControlId);
IMPORT_C TCursor InsertLC(TCursor& aInsertAt, CCoeControl* aControl, TInt aControlId = KCoeNoControlId);
Calling one of these 1) pushes the child to the cleanup stack, using a special cleanup item, and leave it there, 2) adds the child to the array, 3) sets the control as the container window to the child, 4) sets the control as the parent to the child, and 5) adds the child to the control’s layout manager, if one exists. This way, control construction will always follow the pattern:
InitComponentArrayL(); // call once
CMyChildControl* component = new (ELeave) CMyChildControl;
Components().AppendLC(component); // or InsertAfterLC() or InsertLC()
component->ConstructL();
component->SetThisAndThatL();
CleanupStack::Pop(component);
The return value of the insert/append methods is a CCoeControlArray::TCursor object, which works like a normal STL iterator, but is more robust. This means that the cursor will still be valid after you have inserted new items before or after it, or even re-sorted the whole array. Note that the InsertLC() method will insert the new control just before the cursor.
Adding a control to the array using one of the Insert/AppendLC methods leaves the component control on the Cleanup Stack using a special Cleanup Item that will ensure that the object is deleted if anything in the construction leaves, but also that it is removed from the parent’s array. This protects the application against partially constructed controls.
Note that although the child’s container window is automatically set to the parent’s window when the control is added to the array, there is nothing preventing a control from creating its own window simply by calling CreateWindowL() in its ConstructL(). The construction pattern shown above can still be used.
The append/insert methods also take an optional control ID as an argument, by which the control can later be fetched from the array. The ID of the child must only be unique within that child’s parent, so typically the compound control will keep an enumeration listing all the children’s IDs. To fetch the child control using the ID, use the CCoeControlArray::ControlById() method like this:
CMyContol* child = Components().ControlById<CMyContol>(EIdOfChild);
Once added to the CCoeControlArray, the child controls are by default owned by the parent, and will automatically be deleted when the parent is deleted, unless the CCoeControlArray::SetControlsOwnedExternally() has been called. To remove a child from a compound control, use one of the CCoeControlArray::Remove() methods:
class CCoeControlArray
public:
IMPORT_C TInt Remove(const CCoeControl* aControl);
IMPORT_C CCoeControl* Remove(TCursor aRemoveAt);
IMPORT_C CCoeControl* RemoveById(TInt aControlId);
These will remove the child from the array but not delete it.
However, a lodger, which is a non-window-owning control, can now have a hit-test associated with it. This can decline a pointer event, causing it to be offered instead to the next peer or container control. This allows correct pointer event handling for non-rectangular or even overlapping controls.
The mix-in MCoeControlHitTest can be attached to a control using the CCoeControl::SetHitTest() method.
Hit-tests are not owned by the controls that they are attached to. They can be shared between multiple controls because the control to be tested is a parameter of the MCoeControlHitTest
HitRegionContains() method.
Controls have previously always been drawn by the CEikonEnv::SystemGc(), usually retrieved via the CCoeControl::SystemGc() method inside the CCoeControl::Draw() method.
It is now possible to associate a custom graphics context with a particular control and its children. There are many scenarios where system components, such as transition engines, use this mechanism to capture the graphical appearance of a control.
The CCoeControl::SystemGc() method returns the correct GC in these cases. It is therefore unsafe, for example, to use the CEikonEnv::SystemGc() directly in a custom deferred partial update scheme.
The new CCoeControl::DrawNow(const TRect& aRect) const method allows for partially redrawing a control on demand, and custom deferred partial update schemes should use this mechanism instead.
Before UIQ 3, all controls were required to clear and update their own entire rectangle. If a child control did not want to have a rectangular appearance, or if it had a perceived size that was smaller than its actual rectangle, it still had to clear the area between its visual parts and the border of its rectangle. To make the control blend in with its background, the brush color used when clearing the surrounding area had to match that of its parent. To do this, the MCoeControlContext API was used to set the style and the brush color, and sometimes the pen color, before each call to the child’s Draw() method. Only a solid background color was could be used.
To allow for a graphically rich UI that supports non-rectangular controls and bitmapped control backgrounds, the draw mechanism of CONE has been changed, and the MCoeControlContext API has been deprecated.
When calling DrawNow() on a control, the framework will now find the control's closest window-owning parent and start the redraw from there.
This means that children are not required to clear their entire rectangle any more, but can leave that to their parent. Compound controls that previously did not clear the area behind their children to avoid flickering are now required to do so. The flickering that was previously the result of drawing the same pixels more than once is now eliminated through a kind of double-buffered drawing in the Window Server. Still, unnecessary redraws should be avoided for performance reasons.
The following changes need to be made to existing code:
Remove all code referring to the CCoeControl::iContext variable,
Remove all calls to CCoeControl’s SetControlContext(), CopyControlContextFrom(), and ControlContext(),
Remove all implementations of the MCoeControlContext from all custom controls, that is, remove all ActivateContext(), ResetContext() and PrepareContext() implementations from custom controls,
Remove all use of the CCoeBrushAndPenContext and MCoeControlBrushContext APIs,
Remove all calls to MCoeControlContext’s ActivateContext(), ResetContext() and PrepareContext(),
Remove all code that clears the area surrounding the controls’ actual graphical representation,
Change the draw code of compound controls so that the parent of a child correctly updates the area behind the child.
The procedure described above will not work for controls that have their own window because the Window Server will prevent the parent from drawing behind the child’s window. To resolve this, the MCoeControlBackground interface has been introduced, and the CONE framework has been change as follows:
For all window-owning controls, the CCoeControl draw code now looks for a MCoeControlBackground object up the chain of parents, and calls Draw() on it if found.
If a control itself has a MCoeControlBackground object, the CCoeControl draw code now calls Draw() on the MCoeControlBackground object before the control's Draw() is called.
The default implementation of the CCoeControl::Draw() method now looks for a MCoeControlBackground object up the chain of parents and, if found, ignores the IsBlank flag.
A MCoeControlBackground object can be attached to any CCoeControl using the new CCoeControl::SetBackground() method. This background will be used by the control and all its children, unless a child has its own background set. Note that the background object is not owned by the control to which it is attached.
The following UIQ specific backgrounds are in QikControlBackground.h:
CQikBackgroundDecorator: This is the base class for all other UIQ backgrounds. It implements the decorator pattern and allows backgrounds to be chained together.
CQikSkinnedBackground: This is a generic skinned background, taking skin IDs as parameters to its construction.
CQikScreenBackground: This is a special background that draws the background relative to the top left of the screen. It currently always draws the screen skin, which is a single bitmap the size of the screen. It allows the title bar, application view, status bar, and softkeys to share a common background.
CQikWsBitmapBackground: This background allows any image in a CWsBitmap to be used as a control background.
Often, the color of a text drawn by a child control depends on the state and/or properties of the child’s parent. The most common example is the CEikLabel, which is used, for example, by the CEikCommandButton. The color of the text on the button depends on whether the button is pressed, not pressed, or dimmed.
Previously, the correct way to achieve this was to make the button implement the MCoeControlContext interface with the PrepareContext() method, which was called just before all CCoeControl::Draw() was called, and there set the pen color to be used when the label draws its text.
Unfortunately, this mechanism was complicated to use and the MCoeControlContext interface has been deprecated. A new mechanism was needed.
The replacement consists of the new XCoeTextDrawer, the CCoeTextDrawerBase class and two new CCoeControl methods:
class CCoeControl
protected:
IMPORT_C CCoeTextDrawerBase& TextDrawer(TInt aKey = KErrNotFound) const;
private:
IMPORT_C virtual void GetTextDrawer(CCoeTextDrawerBase*& aTextDrawer, const CCoeControl* aDrawingControl, TInt aKey) const;
The CCoeTextDrawerBase class is the base class for all of the various text drawers. That is, there is a default CCoePlainTextDrawer, but there might also be other text drawers that draw, for example, outlined or shadowed text. The text drawer is always allocated on the heap, which means that its allocation can fail. To reduce the risk of this happening, it can optionally be allocated once and reused, rather than allocated and deleted each time it is used. To handle this, the CCoeTextDrawerBase text drawer must be used through the XCoeTextDrawer class that acts as a smart-pointer to the heap-allocated text drawer, deleting it or resetting it, depending on whether it is reusable or not. The CCoeControl::TextDrawer() method also implements fail-safety measures, which means that a valid text drawer will always be returned.
Through the text drawer, the text’s color and style, currently either plain, outlined, shadowed, or outlined and shadowed, can be controlled. The font to be used, including size and weight, is not a part of the text drawer’s parameters.
Now, when a control draws text, it calls the new protected TextDrawer() method, unless it is drawing text on a screen area that it is not drawing the background of itself. This method will in turn call the private virtual GetTextDrawer() method of all the control’s parents, from the one furthest back to, and including, the calling control itself. Each of the parents has the option of overriding the GetTextDrawer() method and either modifying the aTextDrawer object or completely replacing it.
XCoeTextDrawer textDrawer = TextDrawer();
textDrawer->SetAlignment(iAlignment);
textDrawer->SetMargins(iMargin);
textDrawer->SetLineGapInPixels(iGapBetweenLines);
textDrawer.SetClipRect(aRect);
textDrawer.DrawText(gc, *iTextToDraw, Rect(), *Font());
This way, the control that draws the background that the text is drawn on, the button in the example above, can implement the virtual GetTextDrawer() method and set or change the text drawer that is used for the text.
Note that all the properties of the text drawer, except the clip rect, can be accessed though the use of the -> operator on the XCoeTextDrawer object; see the example above. Be sure to not call the CCoeTextDrawerBase::SetReusable() unless you have allocated the text drawer object on the heap yourself. Otherwise, there will be a memory leak or invalid pointer access.
The CEikButtonBase::GetTextDrawer() might look something like this:
EXPORT_C void CEikButtonBase::GetTextDrawer(CCoeTextDrawerBase*& aTextDrawer, const CCoeControl* /*aDrawingControl*/, TInt /*aKey*/) const
{
const CSkinPatch& skin = SkinManager::SkinPatch(KButtonSkinUid, KButtonSkinIndex, this);
skin.GetTextDrawer(aTextDrawer, ESkinTextNotDimmed);
}
If the control draws 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);
Access to non-skinned text drawers can be found through the QikTextUtils class.
To improve support for zooming, and to allow controls to have text in different font sizes, depending on, for example, the current screen mode, improved support for zooming has been added to CONE. It is now possible to attach a TZoomFactor to every CCoeControl using this new CCoeControl method:
class CCoeControl
public:
IMPORT_C void SetZoomFactorL(TInt aZoomFactor, TZoomType aZoomType = ERelativeZoom);
The zoom factor assigned to a control applies to that control and to all the children inside. The zoom factor can be absolute, or relative to that of the control's parents. To retrieve a control’s accumulated zoom factor, call the new CCoeControl method:
class CCoeControl
protected:
IMPORT_C TZoomFactor AccumulatedZoom() const;
When the zoom factor of a control is changed, a KEikMessageZoomChange message is sent to the HandleResourceChange() method of all of the affected controls, and there is a subsequent call to the new CCoeControl::RequestRelayout() method of the control whose zoom has changed.
The zoom factor used for an entire application can also be changed by setting the CCoeEnv's zoom factor, which has been moved to CCoeEnv from CEikonEnv.
For more information about how this new API is used to control the size of control's fonts, see below.
To allow the size of a control's text to change when the zoom factor is changed, the control's direct use of CFont objects must be removed. That is, the control must not keep pointers or references to CFont objects as member data because these will not be updated to reflect the current zoom factor. More specifically, this means that the use of the CCoeEnv's NormalFont() and the CEikonEnv's LegendFont(), TitleFont(), AnnotationFont() and DenseFont() must be removed from all code.
Instead, a new TCoeFont class has been added that represents a font's size, logical or absolute in pixels, and style, plain, bold, italic, subscript, or superscript. By creating a TCoeFont object that describes the properties of the desired font, a CFont object reference, which is needed to actually draw the text, can be fetched from the new CCoeFontProvider. A font provider can be attached to any CCoeControl and keeps information about the typeface used by the control and all the controls below. A default font provider is attached to the CCoeEnv.
To get a hold of the CFont object, a Draw() method can be implemented like this:
CSomeControl::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);
}
But, for your convenience, there is a new 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;
So instead of using a CFont member object, or using the legacy methods such as NormalFont() and LegendFont(), a control calls the new ScreenFont() method, in its Draw() method, with the TCoeFont object as an argument and uses the returned CFont to draw the text. The CFont reference must not be kept as control member data because it may change in the font provider at any time.
CSomeControl::Draw(const TRect& aRect)
{
XCoeTextDrawer textDrawer = TextDrawer();
textDrawer->SetAlignment(EHCenterVCenter);
textDrawer.DrawText(gc, iText, rect, ScreenFont(TCoeFont::LegendFont());
}
As always, do not call the same method repeatedly if that can be avoided. Calling the ScreenFont() method is fast, but it does have a cost. If you find yourself using the CFont repeatedly within the same method, create and use a local const Font& on the stack rather than calling ScreenFont() repeatedly.
The properties of the font provider that are attached to a control change are typeface and logical-to-pixel size mapping. When these change, a KEikMessageFontChange message is sent to the HandleResourceChange() method of all the affected controls. There is subsequently a call to the new CCoeControl::RequestRelayout() method of the control whose font changed.
The default typeface used by an application can be changed by changing the CCoeEnv's new default font provider.
To support easy translation of legacy code, there are TCoeFont methods that return objects configured in the same way as the old methods, such as NormalFont() and LegentFont(). The old methods have been changed to return italic font for easy identification of code not yet migrated.
Note that these changes mean that all previously available SetFont() methods on UIQ stock controls have been removed. Instead, if you want to change the font size or font typeface used by a control, use the new mechanisms described above.
To make control layout easier, generic layout management support has been added to CONE through the MCoeLayoutManager interface.
Two CCoeControl access methods for setting and retrieving layout manager objects have been added:
class CCoeControl
public:
IMPORT_C virtual void SetLayoutManagerL(MCoeLayoutManager* aLayoutManager);
IMPORT_C MCoeLayoutManager* LayoutManager() const;
Controls that can change size, for example, grow when more text is entered, must be able to request more size from their container. To do this, a new virtual method has been added:
class CCoeControl
public:
IMPORT_C virtual TBool RequestRelayout(const CCoeControl* aChildCtrl);
To integrate the new layout support, the implementation of a few pre-existing CCoeControl methods have had to be adjusted. Most notable, the default implementation of CCoeControl::SizeChanged() has been changed to call PerformLayout() on the control's MCoeLayoutManager object, if one is set.
For more information about how to use layout managers, see section 4.2.
To allow resource driven view construction, each control within a view must have a unique identifier. The resource format allows for complicated nested, and changeable, control structures. It must always be possible to find a control through the identifier, whatever level in the control hierarchy it is on.
To do this, a new, optional, control identifier has been added to CCoeControl, with new access methods:
class CCoeControl
public:
IMPORT_C void SetUniqueHandle(TInt aId);
IMPORT_C TInt UniqueHandle() const;
Note that this identifier is not the same as the ID used when adding controls to the CCoeControlArray, and to make this clear, this identifier is referred to as the control’s Unique Handle (rather than ID). The CCoeControlArray ID only needs to be unique within one array (and thus the control owning the array), while this handle must be unique within a whole view.
This Unique Handle will also be useful when performing automated system tests, as, for example, the coordinates of a control can be fetched at run-time, rather than hard coded in the test script.
A method has been added to the CQikContainerBase, from which e.g. the CQikViewBase derives, that can be used to locate a control by its unique handle:
class CQikContainerBase
public:
template<typename T> T* LocateControlByUniqueHandle(TInt aUniqueHandle);
The CCoeControl::MinimumSize() method is essential for control layout. However, sometimes the control's minimum height depends on the maximum width allowed. That is, the lesser the width the control is allowed to have, the greater the height must be. However, there has previously not been any generic way to tell the control how wide it is allowed to be before calling its MinimumSize().
As this is essential for control layout on small, narrow screens, two new CCoeControl methods have been added:
class CCoeControl
public:
IMPORT_C TInt SetMaximumWidth(TInt aMaxWidth);
IMPORT_C TInt MaximumWidth() const;
The TInt return value from SetMaximumWidth() is an error code that can generally be ignored.
To support both western European languages as well as right-to-left languages, like Arabic and Hebrew, controls that draw text to the screen store the text in a TBidiText object.
When drawing text in scripts that run from right-to-left, much more text preparation is required than when drawing left-to-right scripts like English. These preparations include reordering the characters in the text from logical left-to-right to visual right-to-left order, as well as finding the right forms for the characters. For example, in Arabic, the character form depends on the character that follows.
To support bi-directional text presentation, controls that draw text on the screen themselves store the text in a TBidiText object and the new XCoeTextDrawer is used to print the text on the screen.
Note that this means that the CGraphicsContext, CWindowGc, and CBitmapGc classes DrawText() methods do not support drawing right-to-left scripts. Use of these methods is strongly discouraged. Always use the new XCoeTextDrawer to draw text kept in TBidiText objects.
When an application is running in a language with right-to-left script, the layout of all the controls must be mirrored, that is, horizontally reversed. The layout managers provided by the UI framework handle this for you, but if you choose not to use them, you will need to take the application language’s directionality into account when forming the control layout.
To help you, the new CEikAppUi::ApplicationLanguage() method, which returns the language that the application is currently running, is available. Together with the TBidiText::ScriptDirectionality() method, the directionality of the application language can be deduced:
#include <TBidiText.h>
#include <EikAppUi.h>
if( TBidiText::ScriptDirectionality(iEikonEnv->EikAppUi()->ApplicationLanguage()) == TBidiText::ERightToLeft)
{
// Mirror output
}
else
{
// No mirroring
}
On systems not supporting RTTI, like the Symbian OS, querying an object for support of an interface must be done through custom mechanisms. Such a mechanism typically consists of a virtual method taking a numerical identifier, a UID, and returning either a pointer, typically this if the object itself implements the interface, or NULL depending on whether the interface associated with the identifier is supported or not.
As an extension to this simple scheme that asks the object "do you implement this interface?" one could also extend the mechanism to ask "do you or any of your parents in this hierarchy of objects support this interface?" Such an extended mechanism has been added as a non-standard extension to the Symbian OS CONE component since the 8.1 release. It is explained in this section.
The implementation of the RTTI-like mechanism consists of the MObjectProvider interface and the TTypeUid class, both defined in the CoeMop.h file. Note that both classes are missing the Coe-prefix that CONE classes normally have.
The MObjectProvider mix-in has been added to the CCoeControl definition, allowing any CCoeControl to be asked for support of an interface. To allow objects supporting the interface to be located higher up in the hierarchy of CCoeControls, a new member variable called iMopParent has been added to CCoeControl, and a new method for setting it, CCoeControl::SetMopParent(). This method is implicitly called through the SetContainerWindowL() method, taking a CCoeControl as an argument. But if that method, for whatever reason, is not called, for example if one of the other SetContainerWindowL() methods is called instead, then the SetMopParent() method must be called explicitly.
The parts of the MObjectProvider interface are the virtual MopNext() and the MopSupplyObject() methods:
IMPORT_C virtual MObjectProvider* MObjectProvider::MopNext();
virtual TTypeUid::Ptr MObjectProvider::MopSupplyObject(TTypeUid aId) = 0;
The CCoeControl now implements the MopNext() method, returning the iMopParent member. The MopSupplyObject() is the method that anyone deriving a class from the CCoeControl overrides to allow an interface supported by the control to be found.
To allow an interface to be identified, it must have a UID associated with it. This is done by adding a line to the interface's class definition, where KYourInterfaceUid is a UID allocated from the Symbian UID database:
DECLARE_TYPE_ID(KYourInterfaceUid)
To add support for an interface to a CCoeControl, the control's MopSupplyObject() must be overridden to return a pointer to the object providing the interface, often this. See section 2.3 below for a code example.
To find the object closest to a control that provides an interface, you call MopGetObject() like this:
MSomeInterface* interface = NULL;
control->MopGetObject(interface)
If you want to find out whether the control itself supports the interface, call MopGetObjectNonChaining(), like this:
MSomeInterface* interface = NULL;
control->MopGetObjectNonChaining(interface)
If the interface is supported, the interface pointer will point to the object implementing it. Otherwise it will be NULL.
Note that if a class is implementing an interface, and you ask that class whether it supports the interface, you must be careful not to assume that the object asked is of any other type than the interface class. Here’s an example:
class MSomeInterface;
class CSomeControl : public CCoeControl, public MSomeInterface;
class CSomeOtherControl : public CCoeControl, public MSomeInterface;
CCoeControl* control = GetControl();
CSomeControl* interface = NULL; // Err, should be MSomeInterface!
control->MopGetObjectNonChaining(interface); // returns non-NULL
In the example above, the last line will compile without any warnings and return a non-NULL pointer. But, you might have incorrectly turned a CSomeOtherControl object to a CSomeControl object, which you should never do. This will create defects that are hard to find.
Here is an example showing how a new MEikAlignedControl is implemented in CEikAlignedControl.
class MEikAlignedControl
{
public:
DECLARE_TYPE_ID(0x10A3D51B)
};
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);
}
Note that it is very important that you make a base call in your MopSupplyObject() if you do not support the interface! If you do not do this, you will hide any support for the interface that your base class might have.
Here is the code you need to find out whether a control supports the MEikAlignedControl interface:
MEikAlignedControl* alignedControl = NULL;
someAnonymousControl->MopGetObjectNonChaining(alignedControl)
If the someAnonymousControl object supports the MEikAlignedInterface interface, the alignedControl pointer will be set to point to the interface. Otherwise, it will be NULL after the call to MopGetObjectNonChaining().
There are two main containers to use to put your controls in, CQikContainer and CQikScrollableContainer. The main difference is that CQikContainer does not have scroll bars, whereas CQikScrollableContainer has scroll bars.
For more information about how to create the containers and how to fill them with other controls by using data from resource files, see section 4.4.
The Control Stand-in consists of two parts. The first part is the Stand-in which is used to show a textual representation of the control that it holds. It also acts as a proxy for the actual control, for example, it passes on control events. The second part is the Container Pop-out, which is the control that holds the actual control and pops out when the user acts on the Control Stand-in.
Graphic example of a Control Stand-in, marked in red
Graphic example of a Container Pop-out
The reason for using a Control Stand-in is to prevent controls that require four-way navigation from stealing the hardware key events and thereby preventing navigation within the view , that is, moving focus between controls and changing tabs.
Control Stand-ins are created automatically, for the controls that need it, when views and dialogs are created by using data from resource files. This is done transparently to the developer. In all other cases, the developer needs to create the Control Stand-in for controls that need it.
When a Container Pop-out is closed, it generates either an EEventRequestExit or an EEventRequestCancel event, depending on whether the changes to the control in the Container Pop-out were confirmed or cancelled. These events are passed via the MCoeControlObserver interface.
If the actual control in the Container Pop-out generates an EEventStateChanged event, this will be passed on to the correct observer just as if the control was put directly in the view or dialog. Also, if a control wants the pop-out to close when a certain action has been performed, the control should report an EEventRequestExit event. This closes the pop-out and causes to changes to be persisted.
Here are two examples of how the Control Stand-in should be created manually from code, either by using ConstructL() or by using ConstructFromResourceL().
Using ConstructL
CMyControl* myControl = new(ELeave) CMyControl();
// Ownership of myControl is passed at instantiation time
CQikControlStandIn* standIn = new(ELeave) CQikControlStandIn(myControl);
// The standin’s container window is set at second phase construction.
standIn->ConstructL();
myControl->ConstructL(); // Second phase construction of myControl
// The size of the control should not be set
// manually since this will be handled by the
// Stand-in control
Using ConstructFromResourceL
CMyControl* myControl = new(ELeave) CMyControl();
CQikControlStandIn* standIn = new(ELeave) CQikControlStandIn(myControl);
// Next statement will set container window on myControl and pass on
// the ConstructFromResourceL call to myControl
standIn->ConstructFromResourceL(R_MYCONTROL_RESOURCE);
How to get a custom control to work with the Control Stand-in
The following two interfaces must be implemented by controls that are intended to be used with the Control Stand-in:
MQikTextRepresentationInterface
MQikControlStreamInterface
The MQikTextRepresentationInterface is used by the Control Stand-in to fetch a textual representation of the control’s value. The MQikControlStreamInterface is used by the Container Pop-out to handle cancel behavior of controls.
The custom control should call the MQikControlValueObserver::HandleControlValueChangedL() method every time the control’s value has been changed, for example, by a SetTextL() or a SetValue() method call. This must be done for the Control Stand-in to be able to update the text representation of the control if the value is changed by an API call while the Container Pop-out is not open. HandleControlValueChangedL() is not called when the control value has been changed by user interaction with the control.
The title text of the Container Pop-out is set by implementing the MCoeCaptionRetrieverForFep interface in the container control that holds the Control Stand-in. The developer usually does not need to think about this because both the dialog framework’s Captioned Control and the view’s building block implement this interface.
See the UIQ API Reference in the UIQ Developer Library for more information about these classes.
To create a view that supports the new framework, you need to inherit from the CQikViewBase class. If you override CQikViewBase::ConstructL(),
you should make sure that you call CQikViewBase::BaseConstructL() first in your ConstructL() method and implement the CQikViewBase::ViewId() method to return the view ID (TVwsViewId) that uniquely identifies your view. A view ID includes the application UID and a second UID for the particular view. The second UID only needs to be unique within the application.
The view contents, containers and UI controls, are preferably not created in ConstructL() but rather in ViewConstructL(), which is called the first time a view is activated. The reason for this is to improve application start-up time and to not waste memory for views that are not used. If, for some reason, you need to create a control and add it to the view as early on as in ConstructL(), call PreemptViewConstructionL() first.
Your AppUi class inherits from CQikAppUi. You should use CQikAppUi::AddViewL() to add the view to the application. AddViewL() registers the view and puts it on the control stack. Ownership of the view is transferred to CQikAppUi so you do not need to unregister and delete it yourself. This will be done by the destructor of CQikAppUi.
class CMyView : public CQikViewBase
class CMyAppUi : public CQikAppUi
CMyView::CMyView(CQikAppUi& aAppUi)
: CQikViewBase(aAppUi, KNullViewId)
{
}
CMyView* CMyView::NewLC(CQikAppUi& aAppUi)
{
CMyView* self = new(ELeave) CMyView(aAppUi);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
void CMyView::ConstructL()
{
BaseConstructL();
}
TVwsViewId CMyView::ViewId()const
{
return TVwsViewId(KUidMyApp, KUidMyView1);
}
void CMyAppUi::ConstructL()
{
CQikAppUi::ConstructL();
CMyView* myView = CMyView::NewLC(*this);
AddViewL(*myView);
CleanupStack::Pop(myView);
}
It is not a requirement, but it is recommended that you add your controls constructed in C++ code to your view with CCoeControlArray; see section 2.1 for an explanation. Since CQikViewBase inherits from CQikContainer, the access method to get the CCoeControlArray is called Controls():
CMyChildControl* component = new (ELeave) CMyChildControl;
Controls().AppendLC(component); //Do not push on Cleanup Stack before
component->ConstructL();
component->SetThisAndThat();
CleanupStack::Pop(component);
If you inherit from CQikMultiPageViewBase, you must use the Page() method to get the container of the page you want to add your controls to.
Answers to some FAQ about migrating from UIQ 2 to UIQ 3
Your view inherits preferably from CQikViewBase or CQikMultiPageViewBase. See the diagram in section 1.2 Prerequisites.
If, for some reason, you need to get the MCoeView instance, there are operators defined in CQikViewBase that will retrieve the MCoeView instance for you.
If you want hardware key navigation, you should not override CQikViewBase::OfferKeyEventL() or else make sure it gets called in your own OfferKeyEventL() implementation.
You do not need to call SetRect() on the view any more. The rect is automatically set to CQikViewBase::ViewRect() right after ViewConstructL() has executed. SetRect(ViewRect()) is also called when a view is activated and, in the currently active application, when the UI configuration is changed. In normal mode, ViewRect() will return ClientRect(); see CQikViewBase::SetViewModeL(TViewMode)).
If you override CCoeControl::SizeChanged(), make sure you call CQikViewBase’s SizeChanged(), or CQikMultiPageViewBase’s, depending on from which class you inherit. This will cause the layout managers to layout your controls.
CQikViewBase inherits from MCoeControlObserver. If you override MCoeControlObserver::HandleControlEventL(), make sure to call CQikViewBase::HandleControlEventL() from your implementation if you want automatic focus transitions when tapping on controls.
For hardware-key navigation to work correctly, you need to make sure you set a control’s parent. The parent is the compound control who owns the control. This is done automatically when you call SetContainerWindowL(CCoeControl) for a control. Otherwise, use the CCoeControlArray to add component controls or let the view framework construct your controls from resource data. A control's parent can also be set with CCoeControl::SetParent().
You should use layout managers for navigation to function as expected. If you do not use layout managers, navigation could be inconsistent. Without layout managers, navigation is performed by calculating the distance to controls in the desired direction. The distance is currently calculated by comparing the controls' center positions.
Also, make sure that the controls that are not supposed to have focus, are setup as non-focusing and those that are supposed to have focus, are setup as focusing. Use CCoeControl::SetFocusing() to set this. The default setting in CCoeControl is non-focusing, but many stock controls, which are able to receive focus, override this. If a child control wants to have focus, but its compound parent does not want to have focus, only the child control needs to be a focusing control.
To manually set focus on a control, use the CQikViewBase::RequestFocusL() method. This will take care of calling PrepareFocusLossL() and PrepareFocusGainL() through out the whole focus chain and it will also set/reset focus on the appropriate controls in focus chains.
If you use CQikViewBase::RequestFocusL() you should not call PrepareFocusLossL() and PrepareFocusGainL() for the controls in the control chain. If you want to set focus manually with CCoeControl::SetFocus(), you need to make sure that PrepareFocusLossL() and PrepareFocusGainL() are called for all the controls in the focus chain that change focus. For most cases, RequestFocusL() is sufficient, so avoid using SetFocus() in UIQ 3.
If you want automatic focus transitions when tapping on controls, you need to set your view as the control observer of the control with CCoeControl::SetObserver(). CQikViewBase::HandleControlEventL() will then take care of requesting focus for the control. If you define your view in a resource file, the view is automatically set as your control's observer.
If a view has tabs, the left and right keys are used to switch tabs if the key event is not consumed by any control or focus transition.
The base view’s OfferKeyEventL() will forward hardware key events downward in the focus chain. This means that parent controls get a chance to consume key events before they reach the child controls. Make sure to call the view base’s OfferKeyEventL() if you override this method.
One entry should be highlighted when a view is launched. The method SetInitialFocusL() is used to set the focus unless there is already a focused control. The default implementation sets focus on the first focusing control, which should be correct for most views. However, you can override SetInitialFocusL() to set the focus on any control.
If you want focus to be reset, for example when you back away from a view, you can call RequestFocusL(NULL) or SetInitialFocusL().
Navigation between views
Back links
Going back takes the user to the previous view. This could be the view in another application that used a DNL to the current view or the view’s parent view. All views, except the base/default view, normally have a parent view. The first view added to the AppUi becomes the default view, but this can be overridden by calling CCoeAppUi::SetDefaultViewL(). When going back from the application’s default view, the user ends up in the system default view.
A Cancel command is added by CQikViewBase but it is up to each application that handles persistent user data to implement an OK, save & exit, command. To go back to the view’s previous view, you call the ActivatePreviousViewL() method.
Example:
void CMyView::HandleCommandL(CQikCommand& aCommand)
{
switch(aCommand.Id())
{
case EMyCmdOk: // SaveL will be called asynchronously
RequestFocusL(NULL); //see 2.5.3.3
ActivatePreviousViewL(ESave);
break;
case EQikCmdGoBack: // Cancel – SaveL will NOT be called
RequestFocusL(NULL); //see 2.5.3.3
ActivatePreviousViewL(ECancel);
break;
default:
CQikViewBase::HandleCommandL(aCommand);
break;
}
}
Note: Back links are only remembered between views, not in dialogs.
CQikViewBase implements a virtual SaveL() method which is overridden by views that want to save data to disk. SaveL() is called:
At view deactivation, caused by switching away with the Application Launcher button,
At view deactivation, caused by a call to CQikViewBase::ActivatePreviousViewL(),
At view deactivation, caused by a call to CCoeAppUi::ActivateViewL(TVwsViewId&),
At activation of the same view that is already active. This is typically due to a notifier using CCoeAppUi::ActivateViewL(TVwsViewId&),
When SaveThenDnlToL(TVwsViewId&) is called.
SaveL() is not called when executing CQikViewBase::ActivatePreviousViewL(ECancel). ActivatePreviousViewL(ECancel) can be called directly, but it is also executed when CQikViewBase::HandleCommandL(CQikCommand&) handles the EQikCmdGoBack command.
It is up to each application to display the CQikSaveChangesDialog to warn users about unsaved data.
When a user returns to an application that was switched away from by using the back key, the view tab and highlight position are shown in their default positions.
Resetting the highlight, the scrollable container and the selected tab on back navigation events is the responsibility of each application. This behavior can be achieved, for example, by intercepting EQikCmdGoBack events in HandleCommandL(CQikCommand&) and there remove focus by calling RequestFocusL(NULL) before passing the event to the base implementation of HandleCommandL.
Using multiple applications to complete a task requires that the state remains unchanged in applications left by switching away, instead of being left by using the back option. The next time the application is activated, the application state, that is, the selected view, the selected tab and the scroll position, is just as it was when the application was deactivated except that menus and active pop-outs are closed. For this navigation approach to work, your application cannot delete the view contents when the view is deactivated.
If an application is left in a specific state as described above, the state will be lost if another application uses a DNL to switch to the application.
To support back behavior:
The OK exit route uses ActivatePreviousViewL().
The Cancel exit route forwards EQikCmdGoBack to CQikViewBase::HandleCommandL().
Call RequestFocusL(NULL) to force a call to SetInitialFocusL() at the next activation.
The Cancel exit route raises a warning dialog if the view contains unsaved data.
The base view class implements support for full screen in Softkey style.
The view mode is represented by a class called TQikViewMode. To retrieve the current view mode, use CQikViewBase::ViewMode(). This fills TQikViewMode with Boolean values concerning the use of the various pieces of screen furniture that comprise the current view mode. Then, use the exported functions in TQikViewMode to inquire whether a specific piece of screen furniture is being used or not. For example, UsesAppTitleBar() returns ETrue if the Application Title Bar is being used in the current view mode and EFalse if it is not being used.
Updating the view mode is also a two stage process. First, use the exported functions in TQikViewMode to set the Boolean values in the TQikViewMode object. These determine which pieces of screen furniture will be used in the view mode once it is updated. For example, calling SetButtonOrSoftkeyBar(ETrue) adds the button bar or softkey bar to the list of screen furniture in the TQikViewMode object; calling SetButtonOrSoftkeyBar(EFalse) removes the button bar or softkey bar from the list. Once all of the values in the TQikViewMode object have been updated to reflect the new view mode, pass the list, the TQikViewMode object, as an argument to CQikViewBase::SetViewModeL() to finally set the view mode.
NONSHARABLE_CLASS(TQikViewMode)
{
public:
IMPORT_C TQikViewMode();
IMPORT_C void SetAppTitleBar(TBool aUsed);
IMPORT_C TBool UsesAppTitleBar() const;
IMPORT_C void SetButtonOrSoftkeyBar(TBool aUsed);
IMPORT_C TBool UsesButtonOrSoftkeyBar() const;
IMPORT_C void SetStatusBar(TBool aUsed);
IMPORT_C TBool UsesStatusBar() const;
IMPORT_C void SetToolbar(TBool aUsed);
IMPORT_C TBool UsesToolbar() const;
IMPORT_C void SetNormal();
IMPORT_C TBool IsNormal() const;
IMPORT_C void SetFullscreen();
IMPORT_C TBool IsFullscreen() const;
IMPORT_C TBool operator==(const TQikViewMode& aOther) const;
IMPORT_C TBool operator!=(const TQikViewMode& aOther) const;
…
}
class CQikViewBase
{
public: // View mode
IMPORT_C TQikViewMode ViewMode() const;
protected:
IMPORT_C void SetViewModeL(const TQikViewMode& aMode);
}
A view uses the QIK_VIEW_CONFIGURATIONS resource structure to define which UI configurations it supports.
If an application view, for example, supports portrait mode only and the phone tries to launch such a view when currently in landscape mode, the phone will switch to portrait mode. An application can only be started in a UI configuration it supports. However, as an aid during development, the emulator can force an application into an unsupported screen mode with the window server’s debug keys. Please refer to section 5, UI configurations, for more information.
The example below sets up a view to support two different UIQ configurations.
|
RESOURCE QIK_VIEW_CONFIGURATIONS r_refui_configurations
{
configurations =
{
QIK_VIEW_CONFIGURATION
{
ui_config_mode = KQikSoftkeyStylePortrait;
}
};
}
The QIK_VIEW_CONFIGURATIONS resource structure is loaded by calling CQikViewBase::ViewConstructFromResourceL() from ViewConstructL().
Apart from defining the supported UI configurations, you can also use QIK_VIEW_CONFIGURATIONS to set up the view to switch layout and command list automatically when changes of UI configuration occur. This is done with the view and command_list members of the QIK_VIEW_CONFIGURATIONS.
Refer to section 4.4, Resource driven layout, for a code example.
The Application Title Bar, as default, shows the application name. When CQikViewBase::SetCategoryModelAsCommandsL() has been called, the Application Title Bar displays the name of the selected category instead of the application name when any other category than "All" is selected.
Application title bar in Softkey style
The Application Title Bar also functions as a command operator when the UI configuration is EQikUiConfigMenu, rather than EQikUiConfigSoftkey. The command types the different drop-down menus accept and some of the other parameters are configurable by phone manufacturers in QikCtl.rss.
When the Category title contains just one item, pressing the title is the same as opening the menu pane, selecting the item and closing the menu pane, but no menu pane will be opened graphically. If the item supplies an icon, that icon will replace the icon for the Category title. A phone manufacturer can turn on/off this behavior system wide from the resource file.
The Application Title Bar resides above the application area. It is not directly reachable for an application developer. The idea is that the Command Processing Framework, together with the current UI configuration, decides which commands are added to the Application Title Bar and thereby are displayed to the user. Any other interaction with the Application Title Bar is performed through the Category Model or the View Base Class. A developer should not, under any circumstance, try to access the Application Title Bar directly.
On a phone using Softkey Style, the Application Title Bar does not accept input from the user; it simply acts as an information bearer. On a phone with a touch screen, the Application Title Bar replaces the former menu bar. In the standard configuration, the Application Title Bar has two titles, one text title that opens up the applications menu and one icon title that typically opens up the Category menu.
The text on the title is usually the name of the application. If the Category Model is used, the chosen Category’s name will be shown instead of the name of the application after a category has been selected. Selecting the "All" category, determined by handle, not by name, will once again show the name of the application.
If you want to change the currently selected category programmatically, call void CQikViewBase::SelectCategoryL(TInt aHandle). This method is protected and will update the current category and change the title in the Application Title Bar. This is useful when entering a detail view with an item from a certain category. The category name should never be set by calling SetAppTitleNameL.
If the name of the application needs to be changed, call SetAppTitleNameL. If the icon needs to be changed, call SetAppTitleIcon. Calling them with KNullDesC() and NULL respectively results in resetting them to the original name/icon.
The View Context Bar is owned by the view, and not shared between views as is the Application Title Bar. It resides inside the Application Title Bar, to the right of the application icon and below the title. The View Context Bar has no public APIs. Interaction is handled by retrieving a CQikViewContext from the view.
Graphic example of a Container Pop-out
View context bar displaying tabs
The View Context Bar has two purposes, first to display and handle tabs, and second to add decorations.
The tabs are handled through CQikViewBase or CQikMultiPageViewBase.
CQikMultiPageViewBase implements the system with tab pages. As long as your applications use tab pages, you just need your view to inherit from CQikMultiPageViewBase. If you do not use tab pages or want to use the tabs in a customized way, inherit from CQikViewBase. Special implementations can inherit directly from CQikViewBase and make use of the tabs in customized ways.
To add texts and icons to the View Context Bar, call ViewContext() in the view. This gives you an interface, CQikViewContext, to the View Context Bar. Here you can add texts, add icons, change the text, remove them, etc.
It is worth noting that the component IDs you use when adding/deleting range from 1 to 999 and then from 1001 and upwards. The first series makes them left aligned, while the second will make them right aligned. The use of 0 and 1000 are strongly discouraged. Do not expect your code to work if you do try to use them. A component with a higher numbered ID is granted placement to the right of a component with a lower numbered ID.
Commands of type EQikCommandTypeYes, EQikCommandTypeDone, EQikCommandTypeNo and EQikCommandTypeCancel are automatically consumed by the button bar as long as there is enough room.
If a command is used in the button bar, it is also consumed, that is, it is not passed on to other command operators.
The following commands will automatically be added as buttons to the button bar if they fit:
EQikCommandTypeDone
EQikCommandTypeCancel
EQikCommandTypeYes
EQikCommandTypeNo
Any other command type combined with the cpf-flag: EQikCpfFlagPreferToBePlacedInButtonbar
The button bar may also contain a default button, which differs in appearance from the other buttons, and is mapped to the hardware confirm key. To specify a button as the default button, use the flag EQikCpfFlagIsDefault.
Note however, that if the view or the focused control contains an EQikCommandTypeItem command which is not flagged with EQikCpfFlagPreferToBePlacedInButtonbar, the command will be consumed by the CQikKeyListener and mapped to the hardware confirm key. The result is that the default button is not invoked on the hardware confirm key.
Number of buttons
If all of the buttons have an image, it is only available space that limits the number of buttons in the button bar. An icon button has a minimum size of 1/9 of the button bar width, minus margins.
Buttons with text grow as the text grows, but as a minimum they are 1/3 of the button bar width, minus margins.
If too many commands are received by the button bar, that is, more than three text buttons or more buttons than fit within the available space, some buttons will be discarded and the corresponding commands passed on to other command operators. The commands will be discarded in the following order:
EQikCommandTypeDone and all other commands specifically flagged as being used in the button bar
EQikCommandTypeNo
EQikCommandTypeYes
EQikCommandTypeCancel
Button order
The buttons will be arranged according to type in the following order from left to right:
EQikCommandTypeYes
EQikCommandTypeDone
EQikCommandTypeNo
EQikCommandTypeCancel
All other command types
The buttons are left aligned within the button bar.
This section describes how to create view contents and command lists on view activation and destroy them on view deactivation so that UI objects do not need to be held in memory all of the time.
Although this can be useful under some circumstances, we do not recommend this as the default way of developing applications because:
Deleting UI controls when deactivating a view also deletes the view state. Loosing the view state means the multi-task navigation model will not work without tweaking code in the application,
Dynamic deletion/creation of UI controls has a negative impact on performance,
Dynamic deletion/creation of commands also has a negative impact on performance,
Manually switching command lists when UI configurations change and transferring the application state to commands at view activation complicate application code,
Command lists often use small amounts of memory. Destroying command lists and recreating them when needed, as a general rule, is not as beneficial as saving memory in the few areas that consume large amounts of memory.
This code example demonstrates how view contents and commands are dynamically destroyed at view deactivation and recreated when the view is activated.
/** Set the command_list member of your QIK_VIEW_CONFIGURATION structs to zero to prevent commandlists from being auto-loaded by ViewConstructFromResourceL()
*/
RESOURCE QIK_VIEW_CONFIGURATIONS r_ui_configurations
{
configurations =
{
QIK_VIEW_CONFIGURATION
{
ui_config_mode = KQikSoftkeyStylePortrait;
command_list = 0;
view = r_layout;
}
};
}
/**
Overrides InitializeViewFromResourceL to prevent it from being executed twice at program start up when you also call it from your ViewActivatedL. (It is also called from inside ViewConstructFromResourceL)
*/
void CMyView::InitializeViewFromResourceL(const TQikViewConfigData& aViewConfigData)
{
if( iAppUi.IsViewConstructed(ViewId()) )
CQikViewBase::InitializeViewFromResourceL(aResourceId);
}
/**
Called the first time a view is activated
*/
void CMyView::ViewConstructL()
{
ViewConstructFromResourceL(R_UI_CONFIGURATIONS, R_CONTROLS);
}
/**
Called every time a view is activated
*/
void CMyView::ViewActivatedL(const TVwsViewId& /*aPrevViewId*/,TUid /*aCustomMessageId*/,const TDesC8& /*aCustomMessage*/)
{
// Load commandlist
CQikCommandManager& cmdManager = CQikCommandManager::Static();
cmdManager.InsertIntoCommandListL(*this, *this, R_COMMANDS);
// Create and layout UI controls
TQikViewConfigData currentViewConfigData;
if(GetCurrentViewConfigData(currentViewConfigData) == KErrNone)
{
InitializeViewFromResourceL(currentViewConfigData);
}
}
/**
Called every time a view is deactivated
*/
void CMyView::ViewDeactivated()
{
// Delete commandlist
CQikCommandManager& cmdManager = CQikCommandManager::Static();
cmdManager.DeleteFromCommandList(*this, R_COMMANDS);
RequestFocusL(NULL);
// Detach controls from the view. Param store which they are
ReleaseControls(ControlProvider().ReleasedControls());
// Delete detached controls
ControlProvider()->ReleasedControls().ResetAndDestroySilently();
}
Quick guide for creating a view
Set logical back view in CQikViewBase’s constructor.
Implement CQikViewBase::ViewId().
Call CQikViewBase::BaseContructL() first in ConstructL().
Add the view to a CQikAppUi with CQikAppUi::AddViewL().
Load UI configurations, controls, layouts and commands in your view’s ViewConstructL().
Use CCoeControlArray for compound controls and views.
Note:
CEikDialog is deprecated and has been replaced by the CQikSimpleDialog/CQikViewDialog.
In the CQikSimpleDialog you can't have tabs. In general you could say the CQikSimpleDialog can do all the things the CEikDailog
can, apart from having tabs(multiple pages). It is prefeered from an UI perspective to use the CQikViewDialog for more advanced dialogs, i.e.
dialogs that require scrolling and several pages.
CQikViewDialog is a new framework class that extends CQikMultiPageViewBase and allows for CEikDialog-like invocation. This means that the same code can be used for both views and complex dialogs. A view-dialog looks like a view, but behaves like a dialog.
Dialogs that are complex, that require scrolling, or use multiple tabs, are prime candidates for porting to the view-dialog framework.
Views that need to be decoupled from the application launching them are other candidates.
A CEikDialog uses the same resource data, and therefore has the same layout, regardless of screen configuration. A CQikViewDialog, on the other hand, can have different layouts for different screen configurations, just like a view.
A CEikDialog can be non-waiting. But a CQikViewDialog, when invoked as a dialog, is always waiting.
A CEikDialog can be used as-is, whereas a CQikViewDialog must always be derived. The constructor is protected and ViewId should be set if the view-dialog is to be used as a view as well.
The event order for non-sleeping, waiting CEikDialogs is:
> CEikDialog* diag = new(ELeave) CEikDialog;
> diag->ExecuteLD(R_EIK_DIAG_1);
PrepareLC(aResourceId);
CleanupStack::PushL(this);
CreateWindowL();
ConstructFromResourceL(aResourceId);
return(RunLD());
DrawableWindow()->FadeBehind(ETrue);
PreLayoutDynInitL();
Layout();
PostLayoutDynInitL();
CleanupStack::Pop(this);
...
TryExitL(TInt aCommandId);
OkToExitL(aCommandId);
delete this;
The methods in bold-italics are virtual and can be overridden in derived classes.
The corresponding event order for a non-sleeping CQikViewDialog is:
> CQikViewDialog* viewdiag = new(ELeave) CQikViewDialogTest;
> viewdiag->ExecuteLD();
PrepareLC();
CleanupStack::PushL(this);
BaseConstructL();
CreateWindowL();
ViewConstructL();
ViewConstructFromResourceL(R_..,R_...);
return(RunLD());
ViewActivatedL(KNullViewId,KNullUid,KNullDesC8);
...
CQikViewDialog::HandleCommandL(aCommand); CloseDialog(retCode);
...
SaveL();
ViewDeactivated();
CleanupStack::PopAndDestory(this);
CQikViewDialog constructs a view in the same way that a view is constructed, except that it does so synchronously. The view-dialog signals itself when it is ready to be closed using the CQikViewDialog::CloseDialog method. A closing view-dialog receives SaveL() and ViewDeactivated() callbacks, which can be overridden by derived classes.
CQikViewDialogs as Views
CQikViewDialog can be used as a view in the same way that views can. This means that the same code and resource structures can be used.
However, this does not allow the same instance of a CQikViewDialog to be used as a view and as a dialog.
A CQikViewDialog can query whether it has been launched as a dialog using the protected Launched() method.
Sleeping CQikViewDialogs
Like CEikDialog, CQikViewDialogs can be sleeping. However, the purpose behind this functionality is quite different.
Sleeping CEikDialogs are typically system dialogs which are pre-allocated so that they can be invoked to display error messages in the event of error conditions such as low memory.
Sleeping CQikViewDialogs are typically intended for use when the view-dialog is particularly complex and therefore costly to construct, and will be used more than once. However, a view-dialog cannot be invoked if it is already invoked; a panic would occur.
Example usage:
iImagePicker = new(ELeave) CImagePickerViewDialog();
iImagePicker->PrepareL();
…
Tint ret = iImagePicker->RunL();
…
delete iImagePicker;
PrepareL and RunL are equivalent to PrepareLC and RunLD except that they do not place the view-dialog on the cleanup stack and dispose of it after it has returned. Therefore, they can be used to repeatedly to invoke a view-dialog, as long as that dialog is owned by some code, such as a member variable.
CQikViewDialogs use standard CQikCommands to execute. The default HandleCommandL method closes the dialog if a command is of type Yes, No, Done or Cancel. This method is often overridden by derived classes. It is not necessary for an overridden HandleCommandL to call CQikViewDialog::HandleCommandL unless it particularly wishes to continue this default behavior.
To close the dialog, call the protected CloseDialog() function. This should be done manually.
Dialogs might also be closed by the system in some circumstances, for example, when another application is activated, or when the screen mode changes.
A command is the visual representation of an action in an application. Usually, it does not contain the action itself, although that can be achieved by subclassing CQikCommand. Application users initiate an action by selecting the corresponding command in the user interface.
In UIQ 3, a command can be located in a number of places, for example, on a softkey, in a button bar or in a menu, depending on the interaction style of the phone. Because of this, developers cannot hard code commands to be represented by a menu item in a menu pane as was previously done.
To deal successfully with this, a more abstract command concept has been introduced. Instead of creating a menu item in a specific menu pane to represent an action, you now create a command to represent the action.
In addition to the text that represents a command in the user interface, a developer can set various attributes, such as type and priority, to control where in the user interface the command will be located.
The Command Processing Framework distributes commands to Invokers using an active object with CActive::EPriorityStandard, and re-sorts command lists using CActive::EPriorityStandard+1. Each time a command is added or a command already present in the UI is changed in a way that might affect where it is displayed, all invokers are reset and the commands are redistributed. The use of active objects will ensure that only one distribution is performed even when multiple changes are made at the same time.
If your application uses an active object with normal (or lower) priority to manipulate the commands, the manipulation will take place after the commands have been distributed. Your manipulation will therefore cause all invokers to be reset and all commands to be redistributed. The execution time for the distribution is linear to the number of commands and worse that linear to the number of named groups (cascading menu panes).
The use of active objects in applications is common to improve application startup time because you want the UI to display something as quickly as possible and therefore read the application's ini-file or initialize the database asynchronously. In most of these cases, it is recommended to raise the priority of the application-defined active object to at least CActive::EPriorityStandard+2 (or EPriorityUserInput) to eliminate redundant command distributions that would slow down your application and perhaps cause flicker in the UI. You should, however, stay below EPriorityHigh which is the priority used for view activation.
The key difference in the way that CPF handles input and the way a Virtual Machine (VM) handles input is that a VM is designed to conceal hardware differences, while CPF is designed to enable use of them.
A difference between UIQ commands and MIDP commands is that the attributes of UIQ commands, type, priority, group, namedgroup, and namedgrouplink, are aimed at giving application developers full control over command location in the user interface for known UI configurations.
In MIDP, the command type tells the VM the intent of a command. It is then up to the VM to interpret the intent and present the command in an appropriate way.
MIDP specifies types that map to the standard actions of a software program, for example, BACK, CANCEL, STOP, EXIT, ITEM, OK, SCREEN and HELP. It is not easy to map a certain kind of action to a specific location in the user interface in a way that can be consistent between different applications.
UIQ command types map to different locations in the user interface rather than to a certain kind of action.
Here are the participants in the command pattern, together with the corresponding UIQ interfaces:
Client – An entity that creates commands and adds them to the framework. Clients are responsible for setting the receiver, also known as the handler, of each command. A client implements the MQikCommandModelOwner interface.
Receiver – The receiver executes the actions associated with a command. A receiver implements the MQikCommandHandler interface.
Invoker – Asks the command to carry out the request. For example, softkeys and the Applications Title Bar are invokers. An invoker implements MQikCommandOperator.
A Client in the UIQ 3 framework is required to be a CCoeControl subclass which implements the MQikCommandModelOwner interface. CQikViewBase and CEikDialog already implement the Client interface. MQikCommandModelOwner defines the MapCommandHandler() callback where the receiver is set on each command. CQikViewBase has a default implementation that sets the CQikViewBase instance as receiver for all commands.
A command that, for some reason, ends up with NULL set as receiver uses the application’s AppUi instance as receiver.
In UIQ 3 the Client, as default, is also set as the receiver. This means that each CQikViewBase instance is the default receiver of commands added from it. This behavior is very similar to previous versions of Symbian OS where the AppUi was the receiver of all of the commands in the entire application.
In UIQ 3, a receiver is implemented with MQikCommandHandler::HandleCommandL.
In UIQ 3 it is easy to implement the command pattern and set a different object as receiver for each command.
Client - Receiver example
The default behavior is often not the preferred behavior. Preferably, receivers for commands dealing with domain logic are set to an engine class, as these actions should not be implemented in the view. Keeping domain logic separate from the view is a key factor for code reuse and for the program to be easily maintained.
In this code example, two commands added from the same client are set up with different receivers:
MQikCommandHandler* CMyView::MapCommandHandler(CQikCommand& aCommand)
{
switch(aCommand.Id())
{
case EMyCmdInitPhoneCall:
return iPhoneEngine;
break;
case EMyCmdConnectHeadset:
return iBluetoothEquipmentHandler;
break;
default:
return this:
break;
}
}
The picture below shows the key configuration in Softkey style.
Softkey: A key that can take commands for different purposes. What the key does is displayed on the screen just above the key. The softkey label in the screen can be tapped on devices with a touch screen.
Hardware key: A key which is specialized for a certain type of command. There is no further information in the UI revealing what action the key will perform.
Key configuration in Softkey style
The type attribute of a command is the single most important attribute to set to control where in the user interface the control will occur and how it will be presented to the user.
Here is a simplified example, just to give you an idea of how it works:
The delete hardware key only accepts a command with type attribute Delete. The center softkey looks first for a command with type attribute Item; if there is no command with this type attribute, the center softkey looks for a command with type attribute Done; if there is no command with this type attribute either, the center softkey will be left empty.
The left softkey looks first for a command with a type attribute Done; if there is no command with this type attribute, the left softkey looks for a command with type attribute Item; if there is no command with this type attribute either, the left softkey will be left empty. Note, however, that there is an exception to this general rule, the Yes-No rule, but we will disregard that rule at the moment. Refer to the table below for details about the location of specific commands in the user interface.
Commands appearing in the same menu pane are sorted first by type, then by priority and finally by creation order. A developer must explicitly set the type of a command but can omit the priority. In that case, commands are sorted in the order by which they were added, because the default priority is 0. Priority can be set to a positive or negative integer value. A negative value is placed before a positive value, like this: -10, -3, 0, 5, 10.
There is seldom need to manually reorder the command list. When type or priority has been changed, an asynchronous re-sort will be pending and executed later, transparently to the application developer. Type and priority can be changed at any time.
Location of commands according to type
|
Note: Item commands are mostly used by controls since their commands have the highest priority and end up on the center softkey/action key. Controls add their commands when receiving focus and remove them when losing focus.
Presentation
A command can have a text and an optional short text. When only the text attribute is set, the text will be used both in menus and on softkeys. When there also is a short text, the short text will be the preferred choice on softkeys and buttons, while the text will be preferred in menus.
A command can also have an icon. In menus, icons are shown beside the text. Icons are preferred instead of text in buttons.
A command can be dimmed in the user interface. It is not possible to initiate the action of a dimmed command. A command can also be made invisible. An invisible command is not available in the user interface.
Controlling command state, for example, dimming
In UIQ 2 the MEikMenuObserver::DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane) was used to dynamically initialize a menu pane immediately before the menu pane was activated. But when working with softkeys, commands are placed directly on the softkeys and not in a pane. This means that the state of commands in the user interface must always be up-to-date because they must be available all of the time.
There is no event like "opening a menu pane" which is always performed before commands become available to the user. Because of this, the DynInitMenuPaneL approach cannot be used. Instead, your application must update the state of commands as soon as state changes occur in the application. To dim or hide commands you, uses CQikCommandManager’s SetDimmed(aClient, aCommandId) or SetAllDimmed(aCommandId).
In Softkey style user interface commands that are unavailable are made invisible. To make it easier for applications to adapt to this convention, the SetAvailable method was developed. Calling SetAvailable(EFalse) either dims the command or makes it invisible, depending on the current UI configuration, and changes when the UI configuration of the phone changes.
If a cascading menu pane, also called named group, only contains dimmed or invisible commands, the framework makes even the cascaded command itself dimmed or invisible. Applications should always use SetAvailable() because it encapsulates the difference between the different UI configurations.
Putting a cascaded command in an invisible or dimmed state automatically puts all of the commands in that named group in an invisible or dimmed state. Putting the cascaded command back in the normal state automatically puts all of the commands in that named group back in the normal state, that is, visible or not dimmed.
An alternative to using the command manager's SetInvisible(), SetDimmed(), SetChecked() or SetAvailable() methods, which can be useful when a number of commands change state because of a single event, is to unload a complete command list and load another list with the same commands but with different default states. A command’s default state can b