1. 程式人生 > >DirectUI介面技術和lua指令碼

DirectUI介面技術和lua指令碼

Duilib庫地址

類圖結構(刪除了介面):


類圖2:

(1)window類可以用來host那些UI控制元件, CWindowWnd使用DialogBuilder放置所有的UI控制元件在它上面。

(2)也可以被UI控制元件作為部件組合,比如CEditWnd,就是被CEditUi組合。

為什麼Edit控制元件是這樣設計呢,以及acitvex控制元件:

“Native Win32 controls are used only for the EDIT control. This control contains so much functionalitythat it would take ages to do a decent replacement. Single-line edit controls are created on the fly (when you click on theframe) and multi-line edits are always visible. So the framework does have the ability to embed native Win32 controlsand even ActiveX controls, but at the expense of screen flickering and severe restrictions in the visual effects I'm planning.”


Lua官方地址, for windows.

// LuaUiFrame.cpp : Defines the exported functions for the DLL application.
//
#include <windows.h>
#include <objbase.h>
#include <string>

/*dui include*/
#include <UIlib.h>
using namespace DuiLib;

/*lua include*/
#ifdef __cplusplus
extern "C" {
#endif
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#ifdef __cplusplus
};
#endif


#define MSGBOX(title,content) MessageBoxA(GetActiveWindow(), content, title, MB_YESNOCANCEL | MB_ICONQUESTION);


// 宣告宿主的5個函式給指令碼使用
#ifdef __cplusplus
extern "C"
{
#endif
        
	int FindControl(lua_State * l);

	int SetAttr(lua_State * l);

	int GetAttr(lua_State * l);

	int SetCallback(lua_State * l);

	int MsgBox(lua_State * l);

#ifdef __cplusplus
};
#endif

// 用這5個函式定義一個匯出陣列
const static struct luaL_reg LuaUiFrameExports [] = {
	{"FindControl", &FindControl},
	{"SetAttr", &SetAttr},
	{"GetAttr", &GetAttr},
	{"SetCallback", &SetCallback},
	{"MsgBox", &MsgBox},
	{NULL,NULL} //required!
};

class CFrameWindowWnd : public CWindowWnd, public INotifyUI
{
public:
	CFrameWindowWnd(const char * xmlPath
		, const char * luaPath) 
		: m_xmlPath(xmlPath)
		, m_luaPath(luaPath)
		, m_L(NULL){ 
		if (!m_L) m_L = lua_open();
		if (m_L)  luaL_openlibs(m_L);
 	}
	~CFrameWindowWnd(){
		if (m_L) lua_close(m_L);
	}

	LPCTSTR GetWindowClassName() const { 
		return _T("LuaUIFrame"); 
	};

	UINT GetClassStyle() const { 
		return CS_DBLCLKS; 
	};

	void OnFinalMessage(HWND /*hWnd*/) { 
		delete this; 
	};

	void Init() 
	{
		/**
		* 把這5個函式註冊成lua的一個庫,叫做frame
		**/
		luaL_register(m_L, "frame", LuaUiFrameExports);
		char szPath[MAX_PATH] = "";
		GetModuleFileNameA(NULL, szPath, MAX_PATH-1);
		strrchr(szPath, '\\')[1] = '\0';
		strcat_s(szPath, MAX_PATH, m_luaPath.c_str());
		luaL_dofile(m_L, szPath);
	}

	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 Notify(TNotifyUI& msg)
	{
		if( msg.sType == _T("windowinit") ) {
			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);
		}
		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 ) {
			// 設定視窗無邊框樣式
			LONG styleValue = ::GetWindowLong(*this, GWL_STYLE);
			styleValue &= ~WS_CAPTION ; //& ~WS_BORDER & ~WS_THICKFRAME;
			::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);

			m_pm.Init(m_hWnd);
			CDialogBuilder builder;
			//CDialogBuilderCallbackEx cb;
			wchar_t szXmlPath [MAX_PATH] = L"";
			MultiByteToWideChar(CP_ACP, 0, m_xmlPath.c_str(), -1, szXmlPath, _countof(szXmlPath));
			CControlUI* pRoot = builder.Create(szXmlPath, (UINT)0, NULL, &m_pm);
			ASSERT(pRoot && "Failed to parse XML");
			m_pm.AttachDialog(pRoot);
			m_pm.AddNotifier(this);

			// 圓角
			SIZE szRoundCorner = m_pm.GetRoundCorner();
			if( !::IsIconic(*this) && (szRoundCorner.cx != 0 || szRoundCorner.cy != 0) ) {
				CRect rcWnd;
				::GetWindowRect(*this, &rcWnd);
				rcWnd.Offset(-rcWnd.left, -rcWnd.top);
				rcWnd.right++; rcWnd.bottom++;
				HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right,rcWnd.bottom
					, szRoundCorner.cx, szRoundCorner.cy);
				::SetWindowRgn(*this, hRgn, TRUE);
				::DeleteObject(hRgn);
			}

			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;
	// 佈局檔案
	std::string m_xmlPath;
	// 控制指令碼
	std::string m_luaPath;
protected:
	// lua指令碼狀態機
	lua_State * m_L;
};

int APIENTRY WinMain(
					 __in  HINSTANCE hInstance,
					 __in  HINSTANCE hPrevInstance,
					 __in  LPSTR lpCmdLine,
					 __in  int nCmdShow
					 )
{	
	CPaintManagerUI::SetInstance((HINSTANCE)hInstance);
	CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());

	HRESULT Hr = ::CoInitialize(NULL);
	if( FAILED(Hr) ) return 0;

	CFrameWindowWnd* pFrame = new CFrameWindowWnd("LuaUiFrame.xml"
		, "LuaUiFrame.lua");

	if( pFrame == NULL ) 
		return 0;

	pFrame->Create(NULL
				, _T("LuaUiFrameApp")
				, WS_VISIBLE | WS_POPUPWINDOW
				//, WS_VISIBLE | WS_POPUPWINDOW| WS_OVERLAPPEDWINDOW
				, 0L, 0, 0, 1, 1);

	pFrame->CenterWindow();
	pFrame->ShowWindow(true);
	CPaintManagerUI::MessageLoop();

	::CoUninitialize();
	return 0;
}


int FindControl(lua_State * l)
{
	return 0;
}


int SetAttr(lua_State * L)
{
	return 0;
}


int GetAttr(lua_State * l)
{
	return 0;
}

int SetCallback(lua_State * l)
{
	const char * pCallbackName = lua_tostring(l,1);
	//MSGBOX(pCallbackName, pCallbackName);
	lua_getglobal(l, pCallbackName);
	//lua_pushnumber(l, 0);   /* push 1st argument */
	//lua_pushnumber(l, 0);   /* push 2nd argument */
	lua_pcall(l, 0, 0, 0);
	return 0;
}

int MsgBox(lua_State * l)
{
	int argc = lua_gettop(l);
	MSGBOX(lua_tostring(l, 1), lua_tostring(l, 2));
	lua_pushnumber(l, 0x00000000);	// return value
	return 1;	// count of ret val
}

xml佈局檔案

<?xml version="1.0" encoding="UTF-8"?>
<Window mininfo="10,10" size="200,200">
	<Font name="幼圓" size="16" default="true" />
	<Font name="微軟雅黑" size="18" />
  <!-- Slider的外觀定義 -->
  <Default name="Slider" value="thumbsize="10,10" bkimage="file='bg.bmp' corner='6,0,6,0' mask='#FFFF00FF'" fgimage="file='fg.bmp' corner='6,0,6,0' mask='#FFFF00FF'" thumbimage="file='thumb.bmp' source='30,0,40,10' mask='#FFDAEBF9'" thumbhotimage="file='thumb.bmp' source='10,0,20,10' mask='#FFDAEBF9'" " />
  <!-- Button的外觀定義 -->
  <Default name="Button" value="normalimage="file='button_nor.bmp' corner='4,2,4,2' fade='200' hsl='true'" hotimage="file='button_over.bmp' corner='4,2,4,2' fade='200' hsl='true'" pushedimage="file='button_down.bmp' corner='4,2,4,2' fade='200' hsl='true' " " />
  
  <!-- 使用垂直佈局放置以下4個ui元件 -->
  <VerticalLayout inset="10,6,10,6" bkcolor="#FFEA679F" colorhsl="true" borderround="18,18">
		<!-- 如果richedit在視窗內是第一個獲得焦點的,會出現一個游標重新整理bug,暫無解決辦法 -->
    
    <!-- 定義3個slider -->
		<Slider name="alpha_controlor" min="20" max="255" value="255"/>
		<Slider name="h_controlor" max="360" value="180"/>
		<Slider name="s_controlor" max="200" value="100"/>
		<Slider name="l_controlor" max="200" value="100"/>
    
    <!-- 定義1個按鈕 -->
		<Button name="changeskinbtn" height="20" text="換膚" maxwidth="120" />
	</VerticalLayout>
</Window>

lua指令碼檔案
function onButtonClick() // frame 這個庫已經註冊到lua指令碼虛擬機器裡面了,可以直接使用
	frame.MsgBox("Hello", "ButtonClicked");
end

frame.MsgBox("title", "text");

frame.SetCallback("onButtonClick");


一些看法:

1) 作者當嘗試dui和lua的結合是僅僅是一次探索,該技術的跨平臺是一個大問題。dui是關於windows的技術。

2) 以前也關注過xul,屬於mozila的跨平臺介面技術實現,這個比較有意思,指令碼是js,moziila在html標籤之外重新定義了一套標籤專門用於app的開發,確實煞費苦心,為什麼不直接增強html標籤呢?當然也是可以xmlns引入xhtml標籤,但是一個app裡面使用2中ns的標籤有些奇怪,另外mozilla也不建議在xulrunner裡面使用xhtml標籤。

3)html5和js未來應該是一個大的趨勢,瀏覽器程式設計和本地程式設計應該可以融合。