Click here to Skip to main content
15,867,686 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 755.5K   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

 
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 
AnswerRe: 太不像话了!! Pin
Leslie Zhai24-May-12 3:28
Leslie Zhai24-May-12 3:28 
GeneralRe: 太不像话了!! Pin
User 817688624-May-12 19:53
User 817688624-May-12 19:53 
GeneralRe: 太不像话了!! Pin
Leslie Zhai24-May-12 21:50
Leslie Zhai24-May-12 21:50 
TODO List中列出了我已经做到的, 和未来要做的.

现在做到的:
* 将DirectX添加回DirectUICore, 因为DuiLib移除了原有的"动画特效", 就是因为这个分歧, 我才创建了这个DirectUICore的分支版本;
* 向CEditUI添加AutoComplete功能;

BUG List中列出了DirectUICore的缺陷
* 如果使用黑色主色调的皮肤, CEditUI在SetFocus后会显示为默认的白色, 虽然可以通过设置nativebkcolor来近似接近皮肤主色调, 但是就显得颜色过渡的很生硬了.
General与duilib如此之类似? Pin
wacr200825-Apr-12 17:52
wacr200825-Apr-12 17:52 
GeneralRe: 与duilib如此之类似? Pin
Leslie Zhai25-Apr-12 18:42
Leslie Zhai25-Apr-12 18:42 
GeneralRe: 与duilib如此之类似? Pin
wangchyz25-Apr-12 23:02
wangchyz25-Apr-12 23:02 
GeneralRe: 与duilib如此之类似? Pin
Leslie Zhai26-Apr-12 14:35
Leslie Zhai26-Apr-12 14:35 
QuestionNot an article Pin
Vivek Krishnamurthy24-Apr-12 18:21
Vivek Krishnamurthy24-Apr-12 18:21 
AnswerRe: Not an article Pin
Leslie Zhai24-Apr-12 21:30
Leslie Zhai24-Apr-12 21:30 
GeneralRe: Not an article Pin
Pete O'Hanlon24-Apr-12 21:58
subeditorPete O'Hanlon24-Apr-12 21:58 
GeneralRe: Not an article Pin
Leslie Zhai24-Apr-12 22:54
Leslie Zhai24-Apr-12 22:54 
GeneralRe: Not an article Pin
Shahin Khorshidnia25-Apr-12 2:09
professionalShahin Khorshidnia25-Apr-12 2:09 
GeneralRe: Not an article Pin
Leslie Zhai25-Apr-12 4:26
Leslie Zhai25-Apr-12 4:26 

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.