Click here to Skip to main content
16,020,314 members
Articles / Desktop Programming / Win32

DirectUI

Rate me:
Please Sign up or sign in to vote.
4.95/5 (20 votes)
27 Apr 2012LGPL33 min read 764.6K   13.2K   104   39
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)


Written By
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.

Comments and Discussions

 
GeneralTry WiseXUI, a real support PNG & DirectUI Delphi interface library Pin
Member 130454247-Mar-17 21:56
Member 130454247-Mar-17 21:56 
GeneralRe: Try WiseXUI, a real support PNG & DirectUI Delphi interface library Pin
codevisio18-May-23 7:05
codevisio18-May-23 7:05 
Question在外国网站国人还是低调点嘛 Pin
Member 114046139-Feb-15 21:09
Member 114046139-Feb-15 21:09 
QuestionLicense Pin
bling28-Jan-15 7:40
bling28-Jan-15 7:40 
Questionthe svn link can not be accessed Pin
jackyxinli9-Jul-14 23:49
jackyxinli9-Jul-14 23:49 
AnswerRe: the svn link can not be accessed Pin
Leslie Zhai31-Jul-14 18:20
Leslie Zhai31-Jul-14 18:20 
GeneralRe: the svn link can not be accessed Pin
jackyxinli2-Aug-14 14:33
jackyxinli2-Aug-14 14:33 
GeneralRe: the svn link can not be accessed Pin
Leslie Zhai19-Aug-14 17:05
Leslie Zhai19-Aug-14 17:05 
General支持国人 Pin
Koh Chia19-Feb-14 21:09
professionalKoh Chia19-Feb-14 21:09 
Question附带的 bin debug 目录下的exe运行不了啊! 能否共享下源代码?附件一下? Pin
embeded_zcd3-Sep-13 22:46
embeded_zcd3-Sep-13 22:46 
Question有点Bug Pin
Guo_guo24-Jun-13 20:42
Guo_guo24-Jun-13 20:42 
General太好了 我也来参与 支持 顶 别听那些说丧家话的 做了就做好 要不心里超不爽 Pin
xunonxyz18-Feb-13 20:01
xunonxyz18-Feb-13 20:01 
太好了 我也来参与 支持 顶 别听那些说丧家话的 做了就做好 要不心里超不爽
Question您这个好多warning啊,怎么让人放心用?就是在您这上开发也放心不下啊 Pin
guolisen17-Feb-13 19:18
guolisen17-Feb-13 19:18 
AnswerRe: 您这个好多warning啊,怎么让人放心用?就是在您这上开发也放心不下啊 Pin
xunonxyz18-Feb-13 20:02
xunonxyz18-Feb-13 20:02 
GeneralRe: 您这个好多warning啊,怎么让人放心用?就是在您这上开发也放心不下啊 Pin
guolisen20-Feb-13 19:28
guolisen20-Feb-13 19:28 
GeneralRe: 您这个好多warning啊,怎么让人放心用?就是在您这上开发也放心不下啊 Pin
xunonxyz24-Apr-13 19:37
xunonxyz24-Apr-13 19:37 
AnswerRe: 您这个好多warning啊,怎么让人放心用?就是在您这上开发也放心不下啊 Pin
yoyocn8-May-13 17:28
yoyocn8-May-13 17:28 
AnswerRe: 您这个好多warning啊,怎么让人放心用?就是在您这上开发也放心不下啊 Pin
harvic8-May-13 21:35
harvic8-May-13 21:35 
QuestionHow can run project on VS 2008? Pin
Sun-Mi Kang29-Jan-13 15:17
Sun-Mi Kang29-Jan-13 15:17 
Generalvery happy Pin
gh071625-Jul-12 19:38
gh071625-Jul-12 19:38 
GeneralRe: very happy Pin
Leslie Zhai26-Jul-12 14:58
Leslie Zhai26-Jul-12 14:58 
General希望和 DUILIB 有所区别 Pin
NeverForever6-Jul-12 22:52
NeverForever6-Jul-12 22:52 
Question附带的Debug文件无法运行 Pin
User 817688624-May-12 19:59
User 817688624-May-12 19:59 
AnswerRe: 附带的Debug文件无法运行 Pin
Leslie Zhai24-May-12 21:43
Leslie Zhai24-May-12 21:43 
Question太不像话了!! Pin
User 817688623-May-12 17:49
User 817688623-May-12 17:49 

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

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