此示例将引导你快速部署一个基于 nim_duilib 的基本应用,此示例与 examples 中的 basic 项目相似,如果你更喜欢查看代码,可以打开examples.sln工程,参考示例代码而无需多花费时间。
- 获取项目代码
git clone https://github.com/rhett-lee/nim_duilib- 获取skia代码的编译方法和修改的代码(nim_duilib使用skia作为绘制引擎,所以先要编译skia)
git clone https://github.com/rhett-lee/skia_compile- 编译skia源码:按照skia_compile项目文档Windows下编译skia.md中的方法,编译出skia相关的lib文件
注意事项:skia源码应该与nim_duilib源码位于相同的目录下。
注意事项:skia源码编译的时候,应使用LLVM编译,程序运行比较流畅;如果使用VS编译,运行速度很慢,界面比较卡。
检查方法:编译成功以后,在skia/out的子目录下,有生成skia.lib等lib文件。 - 在工作目录内,几个项目的源码目录的基本结构如下
- 编译nim_duilib:进入
nim_duilib目录,使用 Visual Studio 2022版本的 IDE 打开examples.sln,选择编译选项为Debug|x64或者Release|x64,按下 F7 即可编译所有示例程序(编译完成的示例程序位于bin目录中)。
使用 Visual Studio 打开项目目录中 examples.sln 解决方案,新建一个 Windows 桌面应用,来一步一步完成第一个基于 duilib 界面库的程序。
- 在
examples.sln解决方案中新建一个 Windows 桌面程序(VS2022,程序类型为:Windows Desktop Application)。 假定程序名为:MyDuilibApp,源码放在examples子目录中。
- 将生成的代码清理一下,只保留关键的 wWinMain 函数:
#include "MainThread.h"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
//正常退出程序
return 0;
}- 使用nim_duilib提供的通用配置(
msvc\PropertySheets\BinCommonSettings.props)
(1)用文本编辑器打开刚刚创建的工程文件(examples\MyDuilibApp\MyDuilibApp.vcxproj)
(2)找到<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />这一行的位置,在这行的后面插入一行,添加一下内容:
<Import Project="..\..\msvc\PropertySheets\BinCommonSettings.props" />
(3)保存该工程文件的修改,如果已经在VS中打开,需要重新加载。
- 项目右键->添加->引用,将 duilib、cximage、libpng、libwebp、zlib 作为引用项目,这样就不需要手动引入静态库文件了。
在创建的项目中增加自定义的线程类MainThread(主线程和一个工作线程)
创建两个文件(MainThread.h 和 MainThread.cpp),并添加到VS工程中,两个文件的内容分别如下:
MainThread.h
#ifndef EXAMPLES_MAIN_THREAD_H_
#define EXAMPLES_MAIN_THREAD_H_
// duilib
#include "duilib/duilib.h"
/** 主线程
*/
class MainThread : public ui::FrameworkThread
{
public:
MainThread();
virtual ~MainThread() override;
private:
/** 运行前初始化,在进入消息循环前调用
*/
virtual void OnInit() override;
/** 退出时清理,在退出消息循环后调用
*/
virtual void OnCleanup() override;
};
#endif // EXAMPLES_MAIN_THREAD_H_MainThread.cpp
#include "MainThread.h"
#include "MainForm.h"
MainThread::MainThread() :
FrameworkThread(_T("MainThread"), ui::kThreadUI)
{
}
MainThread::~MainThread()
{
}
void MainThread::OnInit()
{
//初始化全局资源, 使用本地文件夹作为资源
ui::FilePath resourcePath = ui::FilePathUtil::GetCurrentModuleDirectory();
resourcePath += _T("resources\\");
ui::GlobalManager::Instance().Startup(ui::LocalFilesResParam(resourcePath));
//在下面加入启动窗口代码
//
//创建一个默认带有阴影的居中窗口
MainForm* window = new MainForm();
window->CreateWnd(nullptr, ui::WindowCreateParam(_T("MyDuilibApp"), true));
window->PostQuitMsgWhenClosed(true);
window->ShowWindow(ui::kSW_SHOW_NORMAL);
}
void MainThread::OnCleanup()
{
ui::GlobalManager::Instance().Shutdown();
}在 wWinMain 实例化主线程对象,并调用执行主线程循环,添加后 wWinMain 函数修改如下:
#include "MainThread.h"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
//创建主线程
MainThread thread;
//执行主线程消息循环
thread.RunMessageLoop();
//正常退出程序
return 0;
}创建一个窗口类MainForm,继承 ui::WindowImplBase 类,并覆写 GetSkinFolder GetSkinFile 等方法。
//MainForm.h
#ifndef EXAMPLES_MAIN_FORM_H_
#define EXAMPLES_MAIN_FORM_H_
// duilib
#include "duilib/duilib.h"
/** 应用程序的主窗口实现
*/
class MainForm : public ui::WindowImplBase
{
typedef ui::WindowImplBase BaseClass;
public:
MainForm();
virtual ~MainForm() override;
/** 创建窗口时被调用,由子类实现用以获取窗口皮肤目录
* @return 子类需实现并返回窗口皮肤目录
*/
virtual DString GetSkinFolder() override;
/** 创建窗口时被调用,由子类实现用以获取窗口皮肤 XML 描述文件
* @return 子类需实现并返回窗口皮肤 XML 描述文件
* 返回的内容,可以是XML文件内容(以字符'<'为开始的字符串),
* 或者是文件路径(不是以'<'字符开始的字符串),文件要在GetSkinFolder()路径中能够找到
*/
virtual DString GetSkinFile() override;
/** 当窗口创建完成以后调用此函数,供子类中做一些初始化的工作
*/
virtual void OnInitWindow() override;
};
#endif //EXAMPLES_MAIN_FORM_H_//MainForm.cpp
#include "MainForm.h"
MainForm::MainForm()
{
}
MainForm::~MainForm()
{
}
DString MainForm::GetSkinFolder()
{
return _T("my_duilib_app");
}
DString MainForm::GetSkinFile()
{
return _T("MyDuilibForm.xml");
}
void MainForm::OnInitWindow()
{
BaseClass::OnInitWindow();
//窗口初始化完成,可以进行本Form的初始化
}在我们创建的窗口类中,指定了窗口描述文件目录是 my_duilib_app,指定窗口的描述文件为 MyDuilibForm.xml。
接下来在 bin\resources\themes\default 目录下创建 my_duilib_app 文件夹并新建一个 MyDuilibForm.xml 文件,写下如下内容。
注意事项:XML文件的编码格式是UTF-8。
<?xml version="1.0" encoding="UTF-8"?>
<Window size="800,600" mininfo="80,60"
caption="0,0,0,36" use_system_caption="false" snap_layout_menu="true" sys_menu="true" sys_menu_rect="0,0,36,36"
shadow_type="default" shadow_attached="true" layered_window="true"
alpha="255" sizebox="4,4,4,4" icon="../public/caption/logo.ico">
<VBox bkcolor="bk_wnd_darkcolor" visible="true">
<!-- 标题栏区域 -->
<HBox name="window_caption_bar" width="stretch" height="36" bkcolor="bk_wnd_lightcolor">
<Control />
<Button class="btn_wnd_fullscreen_11" height="32" width="40" name="fullscreenbtn" margin="0,2,0,2" tooltip_text="全屏,按ESC键可退出全屏"/>
<Button class="btn_wnd_min_11" height="32" width="40" name="minbtn" margin="0,2,0,2" tooltip_text="最小化"/>
<Box height="stretch" width="40" margin="0,2,0,2">
<Button class="btn_wnd_max_11" height="32" width="stretch" name="maxbtn" tooltip_text="最大化"/>
<Button class="btn_wnd_restore_11" height="32" width="stretch" name="restorebtn" visible="false" tooltip_text="还原"/>
</Box>
<Button class="btn_wnd_close_11" height="stretch" width="40" name="closebtn" margin="0,0,0,2" tooltip_text="关闭"/>
</HBox>
<!-- 工作区域,除了标题栏外的内容都放在这个大的Box区域 -->
<Box>
<VBox margin="0,0,0,0" valign="center" halign="center">
<Label name="tooltip" text="这是一个简单的nim_duilib窗口,带有标题栏和常规按钮。" height="100%" width="100%" text_align="hcenter,vcenter"/>
</VBox>
</Box>
</VBox>
</Window>在主线程的 MainThread::Init 方法中,创建窗口并居中显示,创建窗口前先引入窗口的头文件,修改后的代码如下:
(首先在文件中包含头文件:#include "MainForm.h")
void MainThread::OnInit()
{
//初始化全局资源, 使用本地文件夹作为资源
ui::FilePath resourcePath = ui::FilePathUtil::GetCurrentModuleDirectory();
resourcePath += _T("resources\\");
ui::GlobalManager::Instance().Startup(ui::LocalFilesResParam(resourcePath));
//在下面加入启动窗口代码
//
//创建一个默认带有阴影的居中窗口
MainForm* window = new MainForm();
window->CreateWnd(nullptr, ui::WindowCreateParam(_T("MyDuilibApp"), true));
window->PostQuitMsgWhenClosed(true);
window->ShowWindow(ui::kSW_SHOW_NORMAL);
}这样一个简单的带有最小化、最大化、还原和关闭按钮、全屏按钮,具有阴影效果和一行文字提示的窗口就创建出来了,你可以编译运行以下代码看一看窗口效果。
可以参考相关的文档CEF.md
项目中关于Visual Studio的工程配置是使用属性文件,保存在以下目录:nim_duilib\msvc\PropertySheets
- 在项目根目录创建一个格式配置文件,文件名为:.editorconfig
- 文件内容如下:
# Visual Studio generated .editorconfig file with C++ settings.
root = true
[*.{c,c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
# Visual C++ Formatting settings
end_of_line = crlf # 行尾格式,可选值为 lf(Unix 风格)、cr(Mac 风格)或 crlf(Windows 风格)
charset = utf-8 # 文件编码字符集为 UTF-8(可选值为 utf-8、utf-8-bom、latin1 等)
trim_trailing_whitespace = true # 删除文件末尾空格
insert_final_newline = true # 末尾插入新行
indent_style = space # 以空格代替 tab
indent_size = 4 # 代替 tab 的空格数量
tab_width = 4 # 制表符的宽度
- 该方法适用于Visual Studio 2022。




