Introduction
Type list is one of more amazing things ever made in C++, weird thing! That's why I prefer C++ to any. Are you familiar with type lists? If not then you have to click here [1] and read. Andrei will explain you better and you'll never regret of it. I've overhauled the previous edition using the type list and was excited of how it made easy a lot of things.
There is the CUpdateUI
class template in WTL that manages UI elements updating, and I like that. In most of cases it's what we need. The thing that made me thinking of another implementation is the way the
CUIUpdate
may be extended to have more functionality. You would have one option in deriving from
CUpdateUI
or CUpdateUIBase
. Seems that would be mess and awkward. I thought it would be nice to have a
CUpdateUI
generalization based on a policy design. The benefits of such a model are:
-
it's extendable; to add new feature, we never should rewrite it wholly;
-
it's adjustable; having put another policy in set of policies, we get more functionality;
-
it has high performance.
I attempted to do so.
Implementation
I thought it looks as that:
class CMainFrame :
public CFrameWindowImpl<CMainFrame>,
public CUIStates<StatePolicy1, StatePolicy2, ...>,
public CUIUpdate<CMainFrame, UIPolicy1, UIPolicy2, ...>
{
...
};
The CUIStates
is a class template that accumulate changes of UI which the
CUIUpdate
has to apply. Each StatePolicyNs
supports a specific kind of change. The
CUIUpdate
shows changes that have been made in the CUIStates
. It has a set of
UIPolicyNs
; each of them supports specific kind of user interface. Both the
CUIStates
and the CUIUpdate
can have an arbitrary set of policies. You may specify up to 32 policies. If you took a look at the definition of the
CUIStates
you would see something like that:
struct nil {}
template
<
typename S01 = nil, typename S02 = nil, typename S03 = nil,
typename S04 = nil, typename S05 = nil,
...
typename S31 = nil, typename S32 = nil
>
class CUIStates :
...
What is last ellipsis (...)? It's a stuff that derives CUIStates from parameters SXX
which are not nil. That's where the type list works and it's a kind of magic. It could be a subject for another article. Well, a compiler has lots of work to exclude from run-time an unused code. The CUIUpdate has been done in the same manner.
Here is the WTL wizard equal code that updates "Toolbar" and "Status Bar" menu's items:
class CMainFrame :
public CUIStates<CStyle>,
public CUIUpdate<CMainFrame, CUICommand<CMainFrame> >,
...
{
...
virtual BOOL OnIdle() {
UIUpdate();
return FALSE;
}
BEGIN_MSG_MAP(CMainFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
CHAIN_MSG_MAP(CUICommand<CMainFrame>)
COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
...
END_MSG_MAP()
LRESULT OnCreate(UINT , WPARAM , LPARAM ,
BOOL& ) {
...
UIAddStyle(ID_VIEW_TOOLBAR, CStyle::Enable|CStyle::Visible|CStyle::Checked);
UIAddStyle(ID_VIEW_STATUS_BAR, CStyle::Enable|CStyle::Visible|CStyle::Checked);
...
return 0;
}
LRESULT OnViewToolBar(WORD , WORD ,
HWND , BOOL& ) {
bool bVisible = !::IsWindowVisible(m_hWndToolBar);
::ShowWindow(m_hWndToolBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE);
UISetCheck(ID_VIEW_TOOLBAR, bVisible);
UpdateLayout();
return 0;
}
LRESULT OnViewStatusBar(WORD , WORD ,
HWND , BOOL& ) {
bool bVisible = !::IsWindowVisible(m_hWndStatusBar);
::ShowWindow(m_hWndStatusBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE);
UISetCheck(ID_VIEW_STATUS_BAR, bVisible);
UpdateLayout();
return 0;
}
...
};
The CStyle
policy supports flags of command IDs such as enable/disable, visible/hidden, checked/unchecked. The
CUICommand
supports updates of popup menus and handles accelerator keys. Thus, the
CUICommand
requires that the CUIState
has the CStyle
; but what would be if it had not? Nothing, the
CUICommand
just does nothing and I ensure you that the CUICommand
doesn't have such an ugly code as:
if(Does_CUIState_Have_CStyle()) {
}
else {
}
All of job done by a compiler at compile-time, there's no such "if". Do you think it's not right? Then you are in bad place, please, close your browser.
If we wanted to manage text labels of items we should add a CText
state policy and use its methods to be in control of UI changes:
class CMainFrame :
public CUIStates<CStyle, CText>,
public CUIUpdate<CMainFrame, CUICommand<CMainFrame> >,
...
To manage toolbar's buttons just rewrite:
class CMainFrame :
public CUIStates<CStyle, CText>,
public CUIUpdate<CMainFrame, CUICommand<CMainFrame>, CUIToolBar<CMainFrame> >,
...
To manage a status bar: CUIStatusBar
, a rebar: CUIReBar
.
If you had your own control you might implement CUIUpdate
policy and thus add the control to that model. Moreover we may specify our own state policy and enlarge functionality of existing
CUIToolBar
, CUIStatusBar
.
How to add new state and UI policies
The sample project shows how to implement state policy called CParts that manages number and widths of status bar parts and how to make CUIStatusBar extended to change the status bar according to changes in CParts.
State policy is supposed to have three function:
bool dirty(ui_type id) const
void clean(ui_type id)
void set_default_value()
dirty and clean support dirty flags; the same as in WTL's CUpdateUI
. set_default_value changes values of state to default ones if state has them; it's made for easy MDI interface support. That's enough to compile it with
CUIStates
. The others methods are at discretion, they are going to be used at changes applying code of UI elements.
The requirement for UI policy is that it should have a following templated function:
template<class State>
void update_state(HWND hwnd, const State& state) {}
Thus, if an UI policy doesn't support specified kind of a state policy then it does nothing; if we wanted it to support, say, CText then it should have function as following:
void update_state(HWND hwnd, CText& state) {
...
}
For details, you should see a source code.
A couple of words about efficiency. Both the CUIStates
and the CUIUpdate
made by aggressively using of generic programming techniques; they have no virtual function. So performance is very high. For comparison, release size of binary generated by WTL wizard is the same. There're two
implementations, specifying #include <map> before #include <wtlx/ui_update.hpp> we switch to STL library from
CSimpleMaps
and CStrings
.
I think using such techniques will make a future of C++ libraries. What is WTL going
to be? Will see, I just hope that isn't a MFC2. What is the reason of some things as
CString
? To make MFC programmers feel themselves comfortable? Well, well, well...
Acknowledgements
[1] Andrei Alexandrescu. Generic<Programming>: Typelists and
Applications.
[2] Andrei Alexandrescu. Generic<Programming>: Mappings between Types and
Values.