上篇文章介绍了脚本响应duilib控件事件,以及脚本中调用duilib的函数。现在说一下具体的实现方式。
为了避免对duilib库太大的改动,使用插件方式载入。首先在CPaintManagerUI头部定义一个接口。
class UILIB_API IScriptEngine
{
public:
virtual bool AddScriptCode(LPCTSTR pScriptCode) = 0; //添加脚本段
virtual bool AddScriptFile(LPCTSTR pstrFileName) = 0; //添加脚本文件
virtual bool ExecuteScript(LPCTSTR funName, CControlUI *pControl) = 0; //执行脚本
virtual bool ExecuteScript(LPCTSTR funName, CControlUI *pControl, TEventUI *ev) = 0; //执行脚本
};
typedef IScriptEngine* (__stdcall *CREATE_SCRIPT_ENGINE_INSTANCE)(); //插件接口,创建新的脚本引擎
typedef void (__stdcall *DELETE_SCRIPT_ENGINE_INSTANCE)(IScriptEngine *pEngine); //插件接口,删除脚本引擎
给CPaintManagerUI添加成员变量和函数,依然提供shared与否的方式。
//
// 脚本
//
public:
static bool LoadScriptPlugin(LPCTSTR pstrModuleName);
IScriptEngine *GetScriptEngine(bool bShared = false);
void AddScriptCode(LPCTSTR pScriptCode, LPCTSTR pLanguageType, bool bShared = false);
void AddScriptFile(LPCTSTR pstrFileName, LPCTSTR pLanguageType, bool bShared = false);
bool ExecuteScript(LPCTSTR funName, CControlUI *pControl);
bool ExecuteScript(LPCTSTR funName, CControlUI *pControl, TEventUI *ev);
private:
IScriptEngine *m_pScriptEngine;
static IScriptEngine *m_pSharedScriptEngine;
static CREATE_SCRIPT_ENGINE_INSTANCE m_funCreateScriptEngine;
static DELETE_SCRIPT_ENGINE_INSTANCE m_funDeleteScriptEngine;
UIManager.cpp的修改内容,别忘了在构造函数中初始化指针。
CPaintManagerUI::~CPaintManagerUI()
{
//卸载脚本引擎
if(m_funDeleteScriptEngine)
{
(*m_funDeleteScriptEngine)(m_pScriptEngine);
}
..................
}
void CPaintManagerUI::Term()
{
//卸载脚本引擎
if(m_funDeleteScriptEngine){
(*m_funDeleteScriptEngine)(m_pSharedScriptEngine);
}
.....................
}
CREATE_SCRIPT_ENGINE_INSTANCE CPaintManagerUI::m_funCreateScriptEngine = NULL; //add by liqs99
DELETE_SCRIPT_ENGINE_INSTANCE CPaintManagerUI::m_funDeleteScriptEngine = NULL; //add by liqs99
IScriptEngine* CPaintManagerUI::m_pSharedScriptEngine = NULL;
bool CPaintManagerUI::LoadScriptPlugin(LPCTSTR pstrModuleName)
{
ASSERT( !::IsBadStringPtr(pstrModuleName,-1) || pstrModuleName == NULL );
if( pstrModuleName == NULL ) return false;
HMODULE hModule = ::LoadLibrary(pstrModuleName);
if( hModule != NULL )
{
m_funCreateScriptEngine = (CREATE_SCRIPT_ENGINE_INSTANCE)::GetProcAddress(hModule, "CreateScriptEngine");
m_funDeleteScriptEngine = (DELETE_SCRIPT_ENGINE_INSTANCE)::GetProcAddress(hModule, "DeleteScriptEngine");
if(m_funCreateScriptEngine != NULL || m_funDeleteScriptEngine != NULL)
{
return true;
}
}
return false;
}
IScriptEngine *CPaintManagerUI::GetScriptEngine(bool bShared)
{
if(m_funCreateScriptEngine == NULL) return NULL;
if(bShared)
{
if(m_pSharedScriptEngine == NULL)
m_pSharedScriptEngine = (*m_funCreateScriptEngine)();
return m_pSharedScriptEngine;
}
if(m_pScriptEngine == NULL)
m_pScriptEngine = (*m_funCreateScriptEngine)();
return m_pScriptEngine;
}
void CPaintManagerUI::AddScriptCode(LPCTSTR pScriptCode, LPCTSTR pLanguageType, bool bShared)
{
IScriptEngine *pScriptEngine = GetScriptEngine(bShared);
if(pScriptEngine == NULL) return;
pScriptEngine->AddScriptCode(pScriptCode);
}
void CPaintManagerUI::AddScriptFile(LPCTSTR pstrFileName, LPCTSTR pLanguageType, bool bShared)
{
IScriptEngine *pScriptEngine = GetScriptEngine(bShared);
if(pScriptEngine == NULL) return;
pScriptEngine->AddScriptFile(pstrFileName);
}
bool CPaintManagerUI::ExecuteScript(LPCTSTR funName, CControlUI *pControl)
{
IScriptEngine *pScriptEngine = GetScriptEngine(false);
if(pScriptEngine)
{
if(!pScriptEngine->ExecuteScript(funName, pControl))
{
pScriptEngine = GetScriptEngine(true);
if(!pScriptEngine) return false;
if(!pScriptEngine->ExecuteScript(funName, pControl)) return false;
}
}
else return false;
return true;
}
bool CPaintManagerUI::ExecuteScript(LPCTSTR funName, CControlUI *pControl, TEventUI *ev)
{
IScriptEngine *pScriptEngine = GetScriptEngine(false);
if(pScriptEngine)
{
if(!pScriptEngine->ExecuteScript(funName, pControl, ev))
{
pScriptEngine = GetScriptEngine(true);
if(!pScriptEngine) return false;
if(!pScriptEngine->ExecuteScript(funName, pControl, ev)) return false;
}
}
else return false;
return true;
}
接着,修改CControlUI::Event(TEventUI& event), 截获事件传递给脚本。
//在事件中执行脚本内容
void CControlUI::Event(TEventUI& event)
{
if( OnEvent(&event) ) DoEvent(event);
if( event.Type == UIEVENT_SETFOCUS )
{
if(!m_asOnSetFocus.IsEmpty()) GetManager()->ExecuteScript(m_asOnSetFocus, this);
}
if( event.Type == UIEVENT_KILLFOCUS )
{
if(!m_asOnKillFocus.IsEmpty()) GetManager()->ExecuteScript(m_asOnKillFocus, this);
}
}
//增加属性定义
void CControlUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
{
............
else if( _tcscmp(pstrName, _T("OnSetFocus")) == 0 ) m_asOnSetFocus = pstrValue;
else if( _tcscmp(pstrName, _T("OnKillFocus")) == 0 ) m_asOnKillFocus = pstrValue;
...............
}
m_asOnSetFocus和m_asOnKillFocus是两个CDuiString,保存脚本内容。
接下来,需要让duilib载入脚本内容,如上一篇在xml中定义或as文件定义的函数。language属性无用,只做预留。
else if( _tcsicmp(pstrClass, _T("Script")) == 0 )
{
nAttributes = node.GetAttributeCount();
LPCTSTR pstrIncludeFile = NULL;
LPCTSTR pstrLanguage = NULL;
LPCTSTR pScriptCode = node.GetValue();
bool shared = false;
for( int i = 0; i < nAttributes; i++ )
{
pstrName = node.GetAttributeName(i);
pstrValue = node.GetAttributeValue(i);
if( _tcsicmp(pstrName, _T("include")) == 0 )
{
pstrIncludeFile = pstrValue;
}
else if( _tcsicmp(pstrName, _T("language")) == 0 )
{
pstrLanguage = pstrValue;
}
else if( _tcsicmp(pstrName, _T("shared")) == 0 )
{
shared = (_tcsicmp(pstrValue, _T("true")) == 0);
}
}
if(pstrIncludeFile)
{
pManager->AddScriptFile(pstrIncludeFile, pstrLanguage, shared);
}
if(pScriptCode && pScriptCode[0] != '\0')
{
pManager->AddScriptCode(pScriptCode, pstrLanguage, shared);
}
}
现在万事俱备,只欠插件了。
代码共享地址:
https://gitee.com/Liqs99/DuiLib_DuiEditor
https://github.com/xfcanyue/DuiLib_DuiEditor