Skip to content

Latest commit

 

History

History
388 lines (298 loc) · 12.9 KB

Getting-Started.md

File metadata and controls

388 lines (298 loc) · 12.9 KB

快速上手

此示例将引导你快速部署一个基于 nim_duilib 的基本应用,此示例与 samples 中的 MyDuilibApp 项目一致,如果你更喜欢查看代码可以参考示例代码而无需多花费时间。

获取项目代码并编译

  1. 获取项目代码
git clone https://github.com/rhett-lee/nim_duilib
  1. 获取skia代码的编译方法和修改的代码(nim_duilib默认使用skia作为绘制引擎,所以先要编译skia)
git clone https://github.com/rhett-lee/skia_compile
  1. 编译skia源码:按照skia_compile目录中的Windows下编译skia.md文档中的方法,编译出skia相关的lib文件
    注意事项:skia源码应该与nim_duilib源码位于相同的目录下。
    注意事项:skia源码编译的时候,应使用LLVM编译,程序运行比较流畅;如果使用VS编译,运行速度很满,界面比较卡。
    检查方法:编译成功以后,在skia/out的子目录下,有生成skia.lib等lib文件

  2. 编译nim_duilib:进入 nim_duilib/examples 目录,使用 Visual Studio 2022版本的 IDE 打开 examples.sln,选择编译选项为Debug|x64或者Release|x64,按下 F7 即可编译所有示例程序(编译完成的示例程序位于bin目录中)。

创建基础工程

使用 Visual Studio 打开项目目录中 samples\\samples.sln 解决方案, 解决方案中包含了一些简单示例作为参考,你可以参考示例或新建一个 Windows 桌面应用,来一步一步完成第一个 duilib 窗口。

  1. samples\\samples.sln 解决方案中新建一个 Windows 桌面程序(VS2022,程序类型为:Windows Desktop Application),假定程序名为:MyDuilibApp
  2. 将生成的代码清理一下,只保留关键的 wWinMain 函数:
#include "MyDuilibApp.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;
}

配置项目属性

  • 项目属性->常规,修改输出目录为克隆后项目的 bin 目录下
  1. Output Directory 改为:..\..\bin\
  2. Intermediate Directory 改为:$(ProjectDir)..\..\tmp\$(PlatformName)\$(ProjectName)\$(Configuration)\
  • 项目属性->常规,修改平台工具集和C++/C语言选项与你编译的 duilib 保持一致
  1. Platform Toolset 改为:Visual Studio 2022 (v143)
  2. C++ Language Standard 改为:ISO C++20 Standard (/std:c++20)
  3. C Language Standard 改为:ISO C17 (2018) Standard (/std:c17)
  • 项目属性->常规,修改目标名称(最终的exe名称)
  1. Target Name: Debug|x64的改为$(ProjectName)64_d,Release|x64的改为$(ProjectName)64
  2. Target Name: Debug|Win32的改为$(ProjectName)_d,Release|x32的改为$(ProjectName)(可选,如果不编译32位程序,可以不设置)

  • 项目属性->C/C++->常规->包含目录中,添加 nim_duilib 根目录到包含目录中:
  1. Additional Include Directories 改为:../../
  2. 如果需要使用 CEF 模块,Additional Include Directories 改为:..\..\;..\..\third_party\cef_wrapper\;..\..\ui_components\third_party\cef_wrapper

  • 项目属性->C/C++->代码生成,将 Debug 模式的运行库修改为 /MTd,将 Release 模式的运行库修改为 /MT

  • 项目右键->添加->引用,将 base、duilib、cximage、libpng、libwebp、zlib 作为引用项目(如果使用CEF,需要添加ui_components作为引用项目),这样就不需要手动引入静态库文件了。

添加成功后,可以看到引用成功的项目:

设置应用程序清单,以使代码兼容Win7/8/10/11等(Win32和x64需要分开设置):
x64设置内容:../../manifest/duilib.x64.manifest Win32设置内容:../../manifest/duilib.x86.manifest 源文件的编码格式,设置为UTF-8格式:

引入线程库

在创建的项目中增加自定义的线程类(主线程和一个工作线程)
创建两个文件(MainThread.hMainThread.cpp),并添加到VS工程中,两个文件的内容分别如下:

#ifndef EXAMPLES_MAIN_THREAD_H_
#define EXAMPLES_MAIN_THREAD_H_

// duilib
#include "duilib/duilib.h"

/** 工作线程
*/
class WorkerThread : public ui::FrameworkThread
{
public:
    WorkerThread();
    virtual ~WorkerThread() override;

private:
    /** 运行前初始化,在进入消息循环前调用
    */
    virtual void OnInit() override;

    /** 退出时清理,在退出消息循环后调用
    */
    virtual void OnCleanup() override;
};

/** 主线程
*/
class MainThread : public ui::FrameworkThread
{
public:
    MainThread();
    virtual ~MainThread() override;

private:
    /** 运行前初始化,在进入消息循环前调用
    */
    virtual void OnInit() override;

    /** 退出时清理,在退出消息循环后调用
    */
    virtual void OnCleanup() override;

private:
    /** 工作线程(如果不需要多线程处理业务,可以移除工作线程的代码)
    */
    std::unique_ptr<WorkerThread> m_workerThread;
};

#endif // EXAMPLES_MAIN_THREAD_H_
#include "MainThread.h"
#include "MainForm.h"

WorkerThread::WorkerThread()
    : FrameworkThread(_T("WorkerThread"), ui::kThreadWorker)
{
}

WorkerThread::~WorkerThread()
{
}

void WorkerThread::OnInit()
{
    ::OleInitialize(nullptr);
}

void WorkerThread::OnCleanup()
{
    ::OleUninitialize();
}

MainThread::MainThread() :
    FrameworkThread(_T("MainThread"), ui::kThreadUI)
{
}

MainThread::~MainThread()
{
}

void MainThread::OnInit()
{
    ::OleInitialize(nullptr);

    //启动工作线程
    m_workerThread.reset(new WorkerThread);
    m_workerThread->Start();

    //初始化全局资源, 使用本地文件夹作为资源
    ui::FilePath resourcePath = ui::FilePathUtil::GetCurrentModuleDirectory();
    resourcePath += _T("resources\\");
    ui::GlobalManager::Instance().Startup(ui::LocalFilesResParam(resourcePath));

    //在下面加入启动窗口代码

}

void MainThread::OnCleanup()
{
    ui::GlobalManager::Instance().Shutdown();
    if (m_workerThread != nullptr) {
        m_workerThread->Stop();
        m_workerThread.reset(nullptr);
    }
    ::OleUninitialize();
}

在 wWinMain 实例化主线程对象,并调用执行主线程循环,添加后 wWinMain 函数修改如下:

// MyDuilibApp.cpp : Defines the entry point for the application.
//

#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.RunOnCurrentThreadWithLoop();

    //正常退出程序
    return 0;
}

创建一个简单窗口

创建一个窗口类,继承 ui::WindowImplBase 类,并覆写 GetSkinFolder GetSkinFile GetWindowClassName 三个方法。

//MainForm.h
#ifndef EXAMPLES_MAIN_FORM_H_
#define EXAMPLES_MAIN_FORM_H_

// duilib
#include "duilib/duilib.h"

/** 应用程序的主窗口实现
*/
class MainForm : public ui::WindowImplBase
{
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()
{
    __super::OnInitWindow();
    //窗口初始化完成,可以进行本Form的初始化

}

创建窗口描述 XML 文件

在我们创建的窗口类中,指定了窗口描述文件目录是 my_duilib_app,指定窗口的描述文件为 MyDuilibForm.xml。 接下来在 resources\\themes\\default 目录下创建 my_duilib_app 文件夹并新建一个 MyDuilibForm.xml 文件,写下如下内容。
注意事项:XML文件的编码格式是UTF-8。

<?xml version="1.0" encoding="UTF-8"?>
<Window size="60%,80%" mininfo="80,50" use_system_caption="false" snap_layout_menu="true" sys_menu="true" sys_menu_rect="0,0,36,36" caption="0,0,0,36" shadow_attached="true" layered_window="true" sizebox="4,4,4,4">
    <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()
{
    ::OleInitialize(nullptr);

    //启动工作线程
    m_workerThread.reset(new WorkerThread);
    m_workerThread->Start();

    //初始化全局资源, 使用本地文件夹作为资源
    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")));
    window->PostQuitMsgWhenClosed(true);
    window->CenterWindow();
    window->ShowWindow(ui::kSW_SHOW_NORMAL);
}

这样一个简单的带有最小化、最大化、还原和关闭按钮、全屏按钮,具有阴影效果和一行文字提示的窗口就创建出来了,你可以编译运行以下代码看一看窗口效果。

如何设置项目中使用的源代码文件编码为UTF-8格式

  1. 在项目根目录创建一个格式配置文件,文件名为:.editorconfig
  2. 文件内容如下:
# 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                    # 制表符的宽度
  1. 该方法适用于Visual Studio 2022。