Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

DirectUI

4.95/5 (20 votes)
27 Apr 2012LGPL33 min read 765K   13.2K  
Open source windowless presentation manager library with DirectX 3D anmiation

Introduction

I argue that QML https://en.wikipedia.org/wiki/QML  is better cross-platform solution!  Please have a look at the awesome demos http://quitcoding.com/

Recently I chatted with my university classmate, he asked me choosing which library to develop GUI, I said using MFC for VC++, WinForm for C#, and Gtk for Linux. Then he passionatly introduced DirectUI to me, a windowless presentation manager using XML to describe GUI, skinning with bmp/jpg/png, animating based on DirectX. Actrualy it is smiliar with Linux GUI toolchain Glade (based on XML), Cairo (2D graphic library), OpenGL (3D graphic library) and Gtk.  

After these years I found that ncurses is better UI library :)

Background

DirectUI is a C++ user interface library created by Microsoft to be a WPF like API for native applications. It is not released to the public but is being used widely throughout Microsoft products including Windows, Microsoft Office, and Windows Live Messenger. 

So I google with DirectUI keyword, it often shown the commercial products, but I really want to find out some open source libraries. Fortunatly it is on the Bjarke Viksoe personal website and the extended version maintained by DuiLib group.

I checked out the extended version source code, it added alpha rendering support and some other cool features, but it removed 3D animation based on DirectX. So I simply added 3D animation based on DirectX developed by Bjarke Viksoe to the extended version.

DirectUI controls` architecture generated by doxygen for the extended version shown as below: 

Image 1  

Using the code

Because there is #include <d3d9.h> in DirectUICore/Internal.h, it need to download DirectX SDK.

Set DirectX SDK include and library path, for example, in VS 2005`s Tool -> Option... -> Project and solution -> VC++ directory.

You can check out the DirectUI source code from DirectUI Souce Code Version Control, then compilered and hack it.

HelloWorld

In the DirectUI source code there is HelloWorld test case to play with DirectUI.

Image 2

  • Use VS2005 to create VC++ project step by step:
  • Create Win32 console application;
  • Choose category as Window application;
  • Set Link -> Output file: ..\bin\Debug\HelloWorld.exe
C++
// WinMain is the main entry point
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
    // CPaintManagerUI is the Windowless presentation manager
    CPaintManagerUI::SetInstance(hInstance);
    CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());

    HRESULT Hr = ::CoInitialize(NULL);
    if (FAILED(Hr)) return 0;
    // CFrameWindowWnd is the major class object to show a dialog window
    CFrameWindowWnd* pFrame = new CFrameWindowWnd();
    if (NULL == pFrame) return 0;
    pFrame->Create(NULL, _T("HelloWorld"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
    pFrame->CenterWindow();
    pFrame->ShowWindow(true);
    CPaintManagerUI::MessageLoop();

    ::CoUninitialize();
    return 0;
}

WM_CREATE in the HandleMessage use res/hello.xml (it need to copy the files and directories under res/ directory to compiler folder such as bin/Debug) to describe GUI. In the hello.xml, set window min and normal size, defined the button with normal, hot and pushed images then notify the click event.

Image 3

XXXWindowWnd architecture generated by doxygen shown as below:

Image 4

class CFrameWindowWnd : public CWindowWnd, public INotifyUI
{
public:
    CFrameWindowWnd() { };
    LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); };
    UINT GetClassStyle() const { return UI_CLASSSTYLE_FRAME | CS_DBLCLKS; };
    void OnFinalMessage(HWND /*hWnd*/) { delete this; };

    void Notify(TNotifyUI& msg)
    {
	if (msg.sType == _T("click")) 
        {
            // When clicked the HelloWorld button, it shown a message box
            if (msg.pSender->GetName() == _T("hellobtn")) 
	    {
	        ::MessageBox(NULL, _T("HelloWorld"), _T("HelloWorld"), MB_OK);
            }
        }
    }

    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_CREATE) 
	{
            m_pm.Init(m_hWnd);
            CDialogBuilder builder;
            CControlUI* pRoot = builder.Create(_T("hello.xml"), (UINT)0, NULL, &m_pm);
            ASSERT(pRoot && "Failed to parse XML");
            m_pm.AttachDialog(pRoot);
            m_pm.AddNotifier(this);
            return 0;
        }
        else if (uMsg == WM_DESTROY) 
	{
            ::PostQuitMessage(0L);
        }
        else if (uMsg == WM_ERASEBKGND) 
	{
            return 1;
        }
        LRESULT lRes = 0;
        if (m_pm.MessageHandler(uMsg, wParam, lParam, lRes)) return lRes;
        return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
    }

public:
    CPaintManagerUI m_pm;
};

Control(Widget) Factory

There is no Menubar container, NumericUpDown, CheckBox/RadioBox, ToggleButton, SwitchButton... supported by DuiLib the extended version of DirectUI, so simple Control Factory only shown as below:

Image 5 

Control Factory skin resource use Mrdoob Widget Factory SVG, it looks like GNOME 3 widget factory.

TestCase

TestApp1 is a sandbox test case more complex than Hello World stuff. 

Image 6 

  • WM_CREATE in the HandleMessage use res/test1.xml (it need to copy the files and directories under res/ directory to compiler folder such as bin/Debug) to describe GUI;
  • Windowinit in the Notify call OnPrepare to add Delegate routine for some slider controls, CDelegateBase architecture shown as below:  Image 7
  • AddAnimJob in the OnPrepare adding 3D animation based on DirectX cool effect;
  • changeskinbtn is able to change the skin runtime; 
C++
class CFrameWindowWnd : public CWindowWnd, public INotifyUI
{
public:
    CFrameWindowWnd() { };
    LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); };
    UINT GetClassStyle() const { return UI_CLASSSTYLE_FRAME | CS_DBLCLKS; };
    void OnFinalMessage(HWND /*hWnd*/) { delete this; };

    void Init() { }

    bool OnHChanged(void* param) 
    {
        TNotifyUI* pMsg = (TNotifyUI*)param;
        if (pMsg->sType == _T("valuechanged")) 
	{
            short H, S, L;
            CPaintManagerUI::GetHSL(&H, &S, &L);
            CPaintManagerUI::SetHSL(true, (static_cast<CSliderUI*>(pMsg->pSender))->GetValue(), S, L);
        }
        return true;
    }

    bool OnSChanged(void* param) 
    {
        TNotifyUI* pMsg = (TNotifyUI*)param;
        if (pMsg->sType == _T("valuechanged")) 
	{
            short H, S, L;
            CPaintManagerUI::GetHSL(&H, &S, &L);
            CPaintManagerUI::SetHSL(true, H, (static_cast<CSliderUI*>(pMsg->pSender))->GetValue(), L);
        }
        return true;
    }

    bool OnLChanged(void* param) 
    {
        TNotifyUI* pMsg = (TNotifyUI*)param;
        if (pMsg->sType == _T("valuechanged")) 
	{
            short H, S, L;
            CPaintManagerUI::GetHSL(&H, &S, &L);
            CPaintManagerUI::SetHSL(true, H, S, (static_cast<CSliderUI*>(pMsg->pSender))->GetValue());
        }
        return true;
    }

    bool OnAlphaChanged(void* param) 
    {
        TNotifyUI* pMsg = (TNotifyUI*)param;
        if (pMsg->sType == _T("valuechanged")) 
	{
            m_pm.SetTransparent((static_cast<CSliderUI*>(pMsg->pSender))->GetValue());
        }
        return true;
    }

    void OnPrepare() 
    {
        CSliderUI* pSilder = static_cast<CSliderUI*>(m_pm.FindControl(_T("alpha_controlor")));
        if (pSilder) pSilder->OnNotify += MakeDelegate(this, &CFrameWindowWnd::OnAlphaChanged);
        
	pSilder = static_cast<CSliderUI*>(m_pm.FindControl(_T("h_controlor")));
        if (pSilder) pSilder->OnNotify += MakeDelegate(this, &CFrameWindowWnd::OnHChanged);
        
	pSilder = static_cast<CSliderUI*>(m_pm.FindControl(_T("s_controlor")));
        if (pSilder) pSilder->OnNotify += MakeDelegate(this, &CFrameWindowWnd::OnSChanged);
        
	pSilder = static_cast<CSliderUI*>(m_pm.FindControl(_T("l_controlor")));
        if (pSilder) pSilder->OnNotify += MakeDelegate(this, &CFrameWindowWnd::OnLChanged);

	COLORREF clrBack = RGB(0, 0, 0);
	RECT rcCtrl = m_pm.FindControl(_T("changeskinbtn"))->GetPos();
	m_pm.AddAnimJob(CAnimJobUI(UIANIMTYPE_FLAT, 0, 350, clrBack, clrBack, CRect(rcCtrl.left, rcCtrl.top, rcCtrl.left + 50, rcCtrl.top + 50), 40, 0, 4, 255, 0.3f));
    }

    void Notify(TNotifyUI& msg)
    {
	if (msg.sType == _T("windowinit")) 
	{
	    OnPrepare();
	}
        else if (msg.sType == _T("click")) 
	{
            if (msg.pSender->GetName() == _T("insertimagebtn")) 
	    {
                CRichEditUI* pRich = static_cast<CRichEditUI*>(m_pm.FindControl(_T("testrichedit")));
                if (pRich) 
		{
                    pRich->RemoveAll();
                }
            }
            else if (msg.pSender->GetName() == _T("changeskinbtn")) 
	    {
                if (CPaintManagerUI::GetResourcePath() == CPaintManagerUI::GetInstancePath())
                    CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin\\FlashRes"));
                else
                    CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());
                CPaintManagerUI::ReloadSkin();
            }
        }
    }

    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_CREATE) 
	{
            m_pm.Init(m_hWnd);
            CDialogBuilder builder;
            CControlUI* pRoot = builder.Create(_T("test1.xml"), (UINT)0, NULL, &m_pm);
            ASSERT(pRoot && "Failed to parse XML");
            m_pm.AttachDialog(pRoot);
            m_pm.AddNotifier(this);
            Init();
            return 0;
        }
        else if (uMsg == WM_DESTROY) 
	{
            ::PostQuitMessage(0L);
        }
        else if (uMsg == WM_ERASEBKGND) 
	{
            return 1;
        }
        LRESULT lRes = 0;
        if (m_pm.MessageHandler(uMsg, wParam, lParam, lRes)) return lRes;
        return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
    }

public:
    CPaintManagerUI m_pm;
}; 

Points of Interest

DirectUI developed by Bjarke Viksoe is an outstanding open source project, so it is comfortable to read the source code and learn a lot! What`s more, it is glad to see more and more Chinese developers such as DuiLib group had contributed to open source world :)

TODO list

  • Add DirectX animation support developed by Bjarke Viksoe (bjarke@viksoe.dk)
  • Add Menubar container 
  • Add NumericUpDown
  • Add CheckBox/RadioBox
  • Add ToggleButton
  • Add SwitchButton
  • Fix CEditUI focused bkcolor when Window`s bkcolor is black
  • Fix CComboUI fail to inherit Window`s defaultfontcolor

History

  • 2012-04-24: Added 3D animation based on DirectX developed by Bjarke Viksoe to the extended version.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)