Click here to Skip to main content
Click here to Skip to main content

DirectUI

, 27 Apr 2012
Rate this:
Please Sign up or sign in to vote.
Open source windowless presentation manager library with DirectX 3D anmiation

Introduction

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.  

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: 

  

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.

    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
// 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.

XXXWindowWnd architecture generated by doxygen shown as below:


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:

 

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. 

 

  • 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:  
  • AddAnimJob in the OnPrepare adding 3D animation based on DirectX cool effect;
  • changeskinbtn is able to change the skin runtime; 
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 Smile | :)

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)

About the Author

Leslie Zhai
Engineer
China China
An individual human existence should be like a river - small at first, narrowly contained within its banks, and rushing passionately past boulders and over waterfalls. Gradually the river grows wider, the banks recede, the waters flow more quietly, and in the end, without any visible break, they become merged in the sea, and painlessly lose their individual being.
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
General与duilib如此之类似? Pinmemberwacr200825-Apr-12 17:52 
GeneralRe: 与duilib如此之类似? PinmemberXiang Zhai25-Apr-12 18:42 
GeneralRe: 与duilib如此之类似? Pinmemberwangchyz25-Apr-12 23:02 
GeneralRe: 与duilib如此之类似? PinmemberXiang Zhai26-Apr-12 14:35 
QuestionNot an article PinmemberVivek Krishnamurthy24-Apr-12 18:21 
AnswerRe: Not an article PinmemberXiang Zhai24-Apr-12 21:30 
GeneralRe: Not an article PinprotectorPete O'Hanlon24-Apr-12 21:58 
GeneralRe: Not an article PinmemberXiang Zhai24-Apr-12 22:54 
Thank Pete, I will add more detailed comment about the test cases HelloWorld and TestApp1.
 
And I could give some explanation about the cool 3D animation based on DirectX ASAP.
GeneralRe: Not an article PinmemberShahin Khorshidnia25-Apr-12 2:09 
GeneralRe: Not an article PinmemberXiang Zhai25-Apr-12 4:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 27 Apr 2012
Article Copyright 2012 by Leslie Zhai
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid