ODLIB (Windows CE 5.0)
The Networked Media Device Sample Application uses a user interface library called the Owner-Draw Library (ODLIB). This library is provided as an example only, and may contain untested functionality. If you choose to use portions of this library, you should test it fully to make sure that it functions as expected in your environment.
ODLIB Classes
CSetupApp:
This is the base class for the library main class. It does the following:- Initializes the EBU Engine
- Checks for previous setup instance
- Checks for application already running
- Manages window registration
- Maintains the main message loop
- Starts the engine thread
- Maintains application global application variables such as busy state.
- Is responsible for creation of the following global objects:
- Border window (CBorderWnd)
- Page manager (CPageManager)
- Font manager (CFontManager)
- The Heartbeat thread
CBorderWnd:
This class creates a window that acts as parent window for all other application Windows.All general application message processing is done in CBorderWnd::WndProc. Messages that are not handled there are passed to the CPageManager for processing. If the CPageManager does not process the message it is then passed to the DefWindowProc.
This window may have a background fill color or fill bitmap. If neither of these is used then the window will automatically size itself to the current front page and remain hidden behind that page.
CPageManager:
This class maintains an array of available pages (based on CAnimPage) along with their creation callbacks.It creates pages on demand as the "back" page.
It creates the message / alert page on startup for ready use.
It controls the transition of a page to be shown from back page to front page. For more information on transitions see the Page Transitions section later in this topic.
It handles page transition / display messages (these start with WMU_AM_). Any messages it receives that are not processed are then passed to the current front page (if any).
CFontManager:
The font manager is responsible for maintaining the internal cache of font objects (SFont).On startup the font manager loads the default fonts from the application string table (STR_FONT_BUTTON, STR_FONT_DEFAULT, STR_FONT_LARGE, STR_FONT_SMALL, STR_FONT_BOLD).
When fonts are requested at runtime, the font cache is checked to see if it is already loaded. If not the font is created and stored in the cache.
The font manager allows per string font specifications. A font specification is in the format: [font name, font height, font weight]. For example
, [Segoe Light, 30, 700]
.Any string can have a font specifier at the beginning of the string as the following:
"[Segoe Light, 30, 700]A large bold text string"
"[Segoe Light, 10, 500]A small text string"
ODL defines a set of default fonts in the %_WINCEROOT%\public\fp_nmd\oak\ownerdrawnUIlib\odlib.rc resource file, as shown below:
ALS_FONT_BUTTON "[Segoe Light,30,700]" // default for buttons, Alias [BN]
ALS_FONT_DEFAULT "[Segoe Light,15,700]" // default text font, Alias [DF]
ALS_FONT_BOLD "[Segoe Light,19,700]" // Alias [BF]
ALS_FONT_LARGE "[Segoe Light,30,700]" // Alias [LF]
ALS_FONT_SMALL "[Segoe Light,12,400]" // Alias [SF]
Buttons always use the ALS_FONT_BUTTON specification if no other font is specified. All other text uses the ALS_FONT_DEFAULT specification if no other font is specified.
The following default fonts can be used by alias in strings:
"[BN]String using button default font"
"[LF]String using large default font"
There is also one additional alias used to access the system font "[SY]":
"[SY]A string using the system default font"
Font allocation and tracking is handled within the font manager automatically.
If a string does not fit where you want it, for example, if a button message is too large for the button; you can modify the font by adding a custom font specifier to the beginning of the string. For example:
"[Segoe Light, 25, 700]Button String"
This forces the font to be rendered at 25 pixels instead of the default 30; this will allow more text to fit into the button. This is useful during the localization of a product, if a localized string does not fit it can have a custom font added to it and no code changes are required.
Heartbeat Thread
The heartbeat thread uses WaitForSingleObject to act as a low system impact delay. This delay can be defined by the application.When WaitForSingleObject times out, a WMU_PULSE event is sent to the main message loop. This WMU_PULSE event is then passed down to all animlib objects to allow them to process animations and page changes in a coordinated manner.
The m_hHeartBeatEvent event is sent by CSetupApp to notify the heartbeat that it is time to shutdown.
Interface Classes
CSetupApp
Base class for the setup applications main class.Developer derives from this class for the main setup UI functionality.
CPageBase
This class is derived from for all UI pages. These are roughly analogous with dialog templates. This class is based around a child window of the border window.
Message Flow
Messages flow through the application in the following manner depending on the source of the message.
CSetupApp::Main
The main message loop.CBorderWnd::ProcessMessage
The main message handler.CPageManager::ProcessMessage
CAnimPage::ProcessMessage
Derived page ::ProcessMessage
Window specific messages start in the same pipeline as general messages then filter from there down.Control specific messages (Button, text, etc) will either start at the CAnimPage (or derived page) level or the control itself.
Pulse Flow
The WMU_PULSE messages generated by the heartbeat thread filter through the application as follows.
- CBorderWnd::ProcessMessage
- CPageManager->Pulse
- The front page if any
- If the page if visible ScreenItemList::Pulse
- For each control in the screen item list CScreenItem::Pulse
- The message page if any
- If the page if visible ScreenItemList::Pulse
- For each control in the screen item list CScreenItem::Pulse
Configuring a Graphical Busy Message
You can declare a callback that takes a CAppMessage object as a parameter, and returns a BOOL in the following way:
BOOL CMyApp::MessageInitCallback(CAppMessage *pAppMessage)
{
// static method used as a callback for initalizing the
// application alert
// Set the background image (required)
pAppMessage->SetBackground(IDB_Sprite_MessageBox);
SHADOW_TEXT_CREATION
TextParams;
// page heading (required)
TextParams.m_ScreenRect = CRect(10, 10, 160, 160);
TextParams.m_pszText = "";
pAppMessage->SetTitleText(pAppMessage->AddControl(TextParams));
// Message (required)
TextParams.m_ScreenRect = CRect(360, 10, 550, 160);
TextParams.m_pszText = "";
pAppMessage->SetBodyText(pAppMessage->AddControl(TextParams));
// add a sprite of the icon
PHASED_SPRITE_CREATION
SpriteCreation;
SpriteCreation.m_uiResID = IDB_Sprite_MainAnim;
SpriteCreation.m_Position = CPoint(170, 10);
SpriteCreation.m_uiNumFrames = 8;
// add the sprite
pAppMessage->AddControl(SpriteCreation);
return TRUE;
}
In this callback, you should call CAppMessage::SetMessageInitCallback with the callback address.
At a minimum, you must set the background for the message in this callback, and create a CScreenText for the title and body of the message. These are then assigned to the appropriate internal structures using the SetTitleText and SetTextBody method of the CAppMessage pointer.
This callback will be called during processing of the SetMessageInitCallback method.
You may create any other read-only screen items you wish in this callback.
This busy message will be used when you make a call to SetBusy. The title text will be set to the setup application title and the body will be set to the passed in message.
Configuring a Graphical MesageBox (alert)
You can declare a callback returning a BOOL and accepting a CAppAlert pointer as follows:
BOOL CMyApp::AlertInitCallback(CAppAlert *pAppAlert)
{
// static method used as a callback for initalizing the
// application alert
// Set the background image (required)
pAppAlert->SetBackground(IDB_Sprite_AlertBox);
SHADOW_TEXT_CREATION
TextParams;
// page heading (required)
TextParams.m_ScreenRect = CRect(10, 10, 160, 160);
TextParams.m_pszText = "[BF]";
pAppAlert->SetTitleText(pAppAlert->AddControl(TextParams));
// Message (required)
TextParams.m_ScreenRect = CRect(360, 10, 550, 160);
TextParams.m_pszText = "";
pAppAlert->SetBodyText(pAppAlert->AddControl(TextParams));
// add the three potential buttons (these are required)
BUTTON_CREATION
ButtonParams;
ButtonParams.m_uiButtonSndID = IDW_Button;
ButtonParams.ButtonTextInfo.m_uiFormat
= DT_VCENTER | DT_CENTER | DT_SINGLELINE;
ButtonParams.m_dwStyle = WS_GROUP; // first button must be group
ButtonParams.m_ptBtnPos.y = 193;
ButtonParams.m_uiBmpID = IDB_Button_1;
ButtonParams.ButtonTextInfo.m_pszText = "";
ButtonParams.m_hwndMessageDest = pAppAlert->GetPageWnd();
ButtonParams.m_bManualShow = TRUE;
// left button
ButtonParams.m_pszTestingTitle = "Left";
ButtonParams.m_ptBtnPos.x = 11;
ButtonParams.m_wMessage = WMU_PAGE_BUTTON_LEFT;
pAppAlert->SetLeftButton(pAppAlert->AddControl(ButtonParams));
ButtonParams.m_dwStyle = 0; // shut of the WS_GROUP bit
// Center button
ButtonParams.m_pszTestingTitle = "Center";
ButtonParams.m_ptBtnPos.x = 194;
ButtonParams.m_wMessage = WMU_PAGE_BUTTON_CENTER;
pAppAlert->SetCenterButton(pAppAlert->AddControl(ButtonParams));
// right button
ButtonParams.m_pszTestingTitle = "Right";
ButtonParams.m_ptBtnPos.x = 376;
ButtonParams.m_wMessage = WMU_PAGE_BUTTON_RIGHT;
pAppAlert->SetRightButton(pAppAlert->AddControl(ButtonParams));
// add a sprite of the icon
PHASED_SPRITE_CREATION
SpriteCreation;
SpriteCreation.m_uiResID = IDB_Sprite_MainAnim;
SpriteCreation.m_Position = CPoint(170, 10);
SpriteCreation.m_uiNumFrames = 8;
// add the sprite
pAppAlert->AddControl(SpriteCreation);
return TRUE;
}
This callback will be called when the alert is being constructed for use. Because it is a modal dialog it will be created and destroyed during each use.
Required Controls
The following table shows the required controls.
Control | Assignment |
---|---|
CScreenText: Title for alert. | Use SetTitleText to assign it. |
CScreenText: Body for alert. | Use SetTextBody to assign it. |
CButton: Potential left button. | Use SetLeftButton to assign it. |
CButton: Potential center button. | Use SetCenterButton to assign it. |
CButton: Potential right button. | Use SetRightButton to assign it. |
You may create any other read only screen items you wish in this callback.
To use the message box call:
int Alert( HWND hwndParent, UINT uType, UINT uMessage, ... );
6-State Button Behavior
Be sure that your artists are aware of the rollover state. When your mouse is on top of a button, this third state is drawn. When you move your mouse off of the button, the button immediately returns to its previous state.
States:
- Inactive,
- Active (up),
- ROLLOVER (mouse is over it),
- Active (depressed),
- Default(without keyboard focus),
- Default(with keyboard focus)
Note: State 5 is the default without keyboard focus. This means that if a carriage-return is hit, the button displaying state 5 will be the button to process the <CR>.
This can cause problems if some other control has the keyboard focus. An example of this is when you have an edit box on the screen. You can be typing in the text edit box while one of the buttons is displaying state 5. At this point in time, if you were to bump your mouse and rollover any button, that button would display state 3, the rolled-over state. But your edit box would still have the keyboard focus; all keystrokes, with the exception of the <CR> would go to the text edit box. Repositioning the mouse off of the button, without clicking, would cause the button to redraw as state 5.
7 State (Animated) Button Behavior
Each of the seven states of this button is represented by a bitmap. The first six states correspond to the same states as the 6-state button mentioned above. The seventh state is only necessary for those buttons that should do a post-button-up animation. This means that if you want to have a button animate after it is clicked (button-down and button-up), you will need a seventh state, and hence a seventh bitmap. You might also note that all states do not necessarily need to have the same number of frames. This way, it is possible to have only one state animate!
States:
- Inactive
- Active
- Rolled-over
- Depressed
- Default
- Default w/focus
- Post-click
Checkbox/Radio Button Bhavior
As mentioned above, in the section on 6-state Button behavior, moving the mouse over a button, will cause the button to draw differently. For checkboxes and radio buttons, the two rolled-over states should be added to the bottom of their corresponding bitmaps.
States:
- Inactive (unchecked),
- Inactive (checked),
- Active (unchecked),
- Active (checked),
- (has keyboard) Focus (unchecked),
- (has keyboard) Focus (checked),
- depressed (unchecked),
- depressed (checked),
- (mouse) rolled-over (unchecked),
- (mouse) rolled-over (checked)
See Also
Networked Media Device Sample Application | Networked Media Device Architecture | How to Develop a Networked Media Device
Send Feedback on this topic to the authors