Stingray® Foundation : Chapter 12 Developing Applications : User Interface Updating
User Interface Updating
The Stingray Foundation Library classes include facilities for keeping an application’s user interface consistent with the state of the application. For example, an application may have a toolbar and provide a menu option for showing and hiding the menu. While the toolbar is showing, the menu should be checked. While the toolbar is hidden, the menu should be unchecked. This section describes how the SFL User Interface Updating mechanism works, and how to use the mechanism to create a consistent user interface.
User Interface Updating Essentials
SFL’s User Interface Updating mechanism has several components, including CUIUpdateGenerator, the IEventRouter interface, CUIUpdateAdapter, and an interface named IIdleHandler.
The UIUpdating mechanism works like this. Any class wishing to implement User Interface updating handles idle-time processing by plugging in its own idle-time processing interface while processing the WM_CREATE message. You’ll see the specific calls for doing that shortly. The class performing User Interface updating also inherits from a class named CUIUpdateGenerator, which has a member function named GenerateUIUpdates(). The class performing User Interface updating handles idle-time processing by calling GenerateUIUpdates(). GenerateUIUpdates() goes through the menu, toolbars, and status bars that have been registered in to the User Interface Updating mechanism, creating a wrapper class for each User Interface item (menu, tool bar button, and status pane). The User Interface mechanism generates a WM_UIUPDATE message and routes the event, where the class performing User Interface updating may modify the element that handles it.
Classes wanting to receive UIUpdating events inherit from CUIUpdateAdapter, which redirects the UI updating logic to a handler function. The handler function takes each incoming User Interface element and massages it in a way appropriate for the current state of the application. For example, if the toolbar is already showing, you may want to put a check mark on the Toolbar toggling menu option. When the toolbar is hidden, you may wish to remove that check mark. Figure 15 shows the User Interface Updating mechanism in action.
Figure 15 – The SFL User Interface Updating mechanism in action
Figure 16 illustrates the architecture of SFL’s User Interface Updating mechanism.
Figure 16 – SFL’s User Interface Updating architecture
The best way to examine SFL’s User Interface Updating mechanism is to examine a class that uses the User Interface Mechanism. Example 122 shows an entire class with User Interface Updating applied.
Example 122 – A C++ class with User Interface Updating applied
template <class _Base>
class CMainFrameBase : public IEventRouterImpl,
public _Base,
public foundation::IIdleHandler,
public CEventRouterMap< CMainFrame >,
public CCommandAdapter,
public CUIUpdateAdapter,
public CUIUpdateGenerator
{
// Attributes
protected:
bool m_bMenuCheckA;
bool m_bMenuCheckB;
bool m_bEnable;
 
// Embedded types
public:
typedef CMainFrameBase<_Base> _ThisClass;
typedef _Base _BaseClass;
 
// Constructors/destructor
public:
CMainFrameBase() {
m_bMenuCheckA = true;
m_bMenuCheckB = false;
m_bEnable = true;
AddListener(static_cast<ICommandListener*>(this));
}
 
// Operations
public:
virtual void InitLayout(ILayoutNode* pRootNode) {
_BaseClass::InitLayout(pRootNode);
}
 
// GUID map implements QueryGuid()
public:
BEGIN_GUID_MAP(_ThisClass)
GUID_CHAIN_ENTRY(IEventRouterImpl)
GUID_CHAIN_ENTRY(CCommandAdapter)
GUID_CHAIN_ENTRY(CUIUpdateAdapter)
END_GUID_MAP
 
// Implement AddRef() and Release() to resolve ambiguity
public:
virtual ULONG STDMETHODCALLTYPE AddRef()
{ return 1L;}
 
virtual ULONG STDMETHODCALLTYPE Release()
{ return 1L; }
 
// Message map
public:
BEGIN_MSG_MAP(_ThisClass)
MESSAGE_HANDLER_DELEGATE(WM_CREATE, OnCreate)
CHAIN_MSG_MAP(CEventRouterMap<CMainFrame>)
CHAIN_MSG_MAP(_BaseClass)
END_MSG_MAP()
 
BEGIN_COMMAND_MAP(_ThisClass)
COMMAND_ENTRY(ID_UIUPDATETEST_MENUCHECKA, OnMenuCheckA)
COMMAND_ENTRY(ID_UIUPDATETEST_MENUCHECKB, OnMenuCheckB)
COMMAND_ENTRY(ID_UIUPDATETEST_DISABLE, OnDisable)
COMMAND_ENTRY(ID_APP_EXIT, OnAppExit)
END_COMMAND_MAP
 
// Event Handlers
public:
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled) {
// Register objects for participation in command UI updating.
SetUIUpdateRouter(guid_cast<IEventRouter*>(this));
_Module.GetMessageLoop()->AddIdleHandler(this);
 
bHandled = FALSE;
return 0L;
}
 
virtual void OnFinalMessage(HWND hWnd) {
_BaseClass::OnFinalMessage(hWnd);
_Module.GetMessageLoop()->RemoveIdleHandler(this);
}
 
virtual bool OnIdle() {
GenerateUIUpdates();
HandleMenu(GetMenu());
// do this if there’s a toolbar…
// HandleToolBar(&m_wndToolBar);
return true;
}
 
virtual bool OnMenuCheckA(UINT nID, int nNotifyCode) {
if(m_bMenuCheckA) {
m_bMenuCheckA = false;
} else {
m_bMenuCheckA = true;
}
return true;
}
 
virtual bool OnDisable(UINT nID, int nNotifyCode) {
if(m_bEnable) {
m_bEnable = false;
} else {
m_bEnable = true;
}
return true;
}
 
virtual bool OnMenuCheckB(UINT nID, int nNotifyCode) {
if(m_bMenuCheckB) {
m_bMenuCheckB = false;
} else {
m_bMenuCheckB = true;
}
return true;
}
 
virtual bool OnAppExit(UINT nID, int nNotifyCode) {
DestroyWindow();
return true;
}
 
virtual bool OnUIUpdate(IUIUpdateElement* pUIUpdateElement,
UINT nCommandID) {
switch(nCommandID) {
case ID_UIUPDATETEST_DISABLE:
pUIUpdateElement->Enable(nCommandID, m_bEnable);
break;
 
case ID_UIUPDATETEST_MENUCHECKA:
pUIUpdateElement->SetCheck(nCommandID, m_bMenuCheckA);
break;
 
case ID_UIUPDATETEST_MENUCHECKB:
pUIUpdateElement->SetCheck(nCommandID, m_bMenuCheckB);
break;
 
default:
return 0;
};
 
return true;
}
};
This listing illustrates a main window frame with User Interface Updating applied. Notice how the class derives from IEventRouterImpl, IIdleHandler, -CEventRouterMap, CCommandAdapter, CUIUpdateAdapter, and CUIUpdateGenerator.
Deriving from IIdleHandler adds idle processing capability to the class. The class registers itself as an idle-time processor during window creation by getting the module’s message loop and calling AddIdleHandler(). AddIdleHandler() comes in through inheritance from the message loop. Notice the class also disconnects the idle handler during OnFinalMessage().
Deriving from CUIUpdateAdapter brings in the virtual function OnUIUpdate(), which the class overrides by checking the user element ID and handling the menu item appropriately (either by setting the text, enabling the control, setting or a check box.
The idle time processing generates WM_UIUPDATE messages for every toolbar item, every status bar pane, and every menu item. These are handled in the -OnUIUpdate() handler.