# RadUI **Repository Path**: falion/RadUI ## Basic Information - **Project Name**: RadUI - **Description**: C++ ui库,模仿qt - **Primary Language**: C - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-11 - **Last Updated**: 2026-09-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Rad-UI 独立 MSVC 解决方案 这是一个自包含的 Visual Studio 2022 解决方案,把上游两个库整理成标准的、可移动的 MSVC 工程: - **rad** —— 底层基础库(异步、事件循环、字符串、容器、IO 等),编译为**静态库**。 - **rad-ui** —— UI 库(对外的发布库),同时支持**静态库**和**动态库**两种形态。 - **example-ui** —— 使用 rad-ui 的演示程序。 不依赖 vcpkg 或任何外部包管理工具,克隆/复制后可直接用 Visual Studio 或 MSBuild 编译。 ## 目录结构 ``` UI/ ├── RadUI.sln 解决方案(4 个配置) ├── include/ 对外发布的头文件(rad + rad-ui 全部公开接口) │ ├── rad/ rad 库公开头 │ └── rad/ui/ rad-ui 库公开头 │ └── dll_api.h RAD_UI_API 导出/导入宏(动态库用) ├── src/ │ ├── rad/ rad 库源文件(Windows 平台清单) │ └── rad-ui/ rad-ui 库源文件(Direct2D + Windows 后端) ├── examples/ 演示程序源文件 ├── projects/ MSBuild 工程文件与公共属性 │ ├── Common.props 各工程共用的输出目录/编译选项 │ ├── rad.vcxproj │ ├── rad-ui.vcxproj │ └── example-ui.vcxproj ├── tools/ │ └── make_def.ps1 动态库符号导出脚本(见下) ├── build/ 中间文件(自动生成,可删除) └── out/ 编译产物(自动生成,可删除) ``` 头文件集中在 `include/`,与源文件分离,可直接把这个目录连同 `out/` 的库文件一起打包发布。 ## 编译配置 解决方案提供 4 个配置(平台固定 x64): | 配置 | rad | rad-ui | example-ui | 说明 | |------|-----|--------|------------|------| | `Debug` | 静态 | 静态 | 链接静态库 | 调试版,静态链接 | | `Release` | 静态 | 静态 | 链接静态库 | 发布版,静态链接(推荐) | | `DebugDll` | 静态 | **动态** | 链接导入库 | 调试版,rad-ui 为 DLL | | `ReleaseDll` | 静态 | **动态** | 链接导入库 | 发布版,rad-ui 为 DLL | 无论哪种配置,**rad 始终是静态库**,其符号会并入 rad-ui(静态库时并入 .lib,动态库时并入 rad-ui.dll)。 ### 用 MSBuild 命令行编译 ```bat :: 在 UI 目录下 msbuild RadUI.sln -p:Configuration=Release -p:Platform=x64 -m msbuild RadUI.sln -p:Configuration=ReleaseDll -p:Platform=x64 -m ``` ### 用 Visual Studio 直接打开 `RadUI.sln`,在工具栏切换配置后生成即可。 ## 产物位置 ``` out/<配置>/ ├── rad.lib rad 静态库 ├── rad-ui.lib 静态配置:rad-ui 静态库 │ 动态配置:rad-ui 的导入库 ├── rad-ui.dll 仅动态配置 └── example-ui.exe 演示程序 ``` ## 使用 rad-ui(静态链接) 在自有工程中: 1. 头文件路径添加 `UI/include`。 2. 链接 `out/Release/rad-ui.lib` 和 `out/Release/rad.lib`。 3. 无需定义额外的预处理宏。 ## 使用 rad-ui(动态链接) 1. 头文件路径添加 `UI/include`。 2. 预处理宏定义 **`RAD_UI_USE_DLL`**(让编译器按 dllimport 处理接口)。 3. 链接 `out/ReleaseDll/rad-ui.lib`(导入库)。 4. 运行时把 `rad-ui.dll` 放到 exe 同目录或 PATH 中。 > `RAD_UI_USE_DLL` 必须定义,否则你代码中直接构造的 rad-ui 对象(如把 `Text`、`Window` > 作为成员)会尝试在本地生成 vtable,导致链接或运行时错误。 ## 动态库的符号导出机制 上游 rad-ui 没有为 DLL 设计导出注解,本工程用两种手段组合完成导出: 1. **rad-ui 自身的类**:头文件里用 `RAD_UI_API` 宏标注(构建 DLL 时定义为 `__declspec(dllexport)`,使用者定义 `RAD_UI_USE_DLL` 时为 `__declspec(dllimport)`)。 只有真正有库内实现的类才标注;纯头文件实现的类保持内联,由使用方自行实例化。 2. **rad 静态库的符号**:`tools/make_def.ps1` 在链接前扫描目标文件与 `rad.lib`,生成 `.def` 文件导出 C++ 修饰符号(相当于 CMake 的 `WINDOWS_EXPORT_ALL_SYMBOLS`)。因为 rad 也没有导出注解,只能这样把它并入 DLL。 由此 `example-ui.exe` 对外的唯一非系统依赖就是 `rad-ui.dll`。 ## 运行示例 示例额外需要字体和图标资源(不在源码仓库中)。把它们放到 exe 同目录: - `Roboto-Regular.ttf` - `Roboto-Bold.ttf` - `Segoe_Fluent_Icons.ttf` - 若干 Material 图标 `*.png` 缺少资源时程序会打印 `application has failed !` 并退出。 ## SVG 渲染 `include/rad/ui/svg.h` 提供了 `rad::ui::Svg`,它用 `src/rad-ui/nanosvg.h` (上游自带、之前未接入编译)解析 SVG,并把它画到当前 `Painter` 上,等价于参考实现里的 `RenderSVG` / `GenBitmap` 两种用法: ```cpp // 1) 作为普通 Item 参与界面渲染(自动按 viewBox 缩放到自身区域) auto svg = parent.add_child(std::make_unique()); svg->width = 96; svg->height = 96; svg->load_from_file("icon.svg"); // 或 load_from_string(markup) svg->preserve_aspect_ratio = true; // false 时拉伸铺满 // 2) 离屏光栅化成图片(相当于 wx 的 GenBitmap) auto pixmap = svg->to_pixmap(SizeU{256, 256}, 96.f); some_image_item->source = Pixmap::from_data(pixmap); // 也可以直接 draw_pixmap(target, *pixmap) ``` 要点: - `nanosvg.h` 自带实现且函数均为 `static`,因此只在 `svg.cpp` 里包含一次。 - 支持纯色、线性渐变(保留方向)与文本(``/``);径向渐变退化为中间色。 - 描边支持宽度、颜色、cap/join;填充支持 nonzero/evenodd。 - `load_from_file` / `load_from_string` 取代了上游的 `nsvgParseFromFile` / `nsvgParse`, `NSVGimage` 在函数内解析完即被 `nsvgDelete` 释放,句柄不会外泄。 - 解析结果放在共享的 `detail::SvgDocument` 里,多个 `Svg`(或图标集)共享同一份解析结果, 可用 `Svg::set_document()` / `Svg::document()` 读写。 ### 图标集:按 id 批量加载 `SvgIconSet` 面向 `resources/iconfont.ini` 这类“每行一个 ``”的文件:加载时 一次性把所有图标解析好常驻内存,运行期用 id(如 `AE029`)即可取用;光栅化结果按 (id, 像素尺寸, dpi) 缓存,重复取用几乎零开销。 ```cpp SvgIconSet icons; icons.load_from_file("resources/iconfont.ini"); // 205 个图标,约 12 ms // 转成图片:缓存,重复调用直接命中 Pixmap pm = icons.pixmap("AE029", SizeU{96, 96}); // 作为 Image/按钮图标 auto img = icons.to_pixmap("AE029", SizeU{96, 96}); // 原始 pixmap // 作为 Item 使用(共享已解析好的文档,不再重复解析) auto svg = icons.make_svg("AE029"); if (svg) { svg->width = 48; svg->height = 48; parent.add_child(std::move(svg)); } // 直接画到任意矩形 icons.render("AE029", painter, RectF{0, 0, 48, 48}); icons.contains("AE029"); // 是否存在 icons.icon_size("AE029"); // 固有尺寸(如 1024x1024) icons.ids(); // 全部 id icons.set_raster_cache_limit(256); // 光栅缓存上限,0 为不限 icons.clear_raster_cache(); ``` ## 相对上游的修改 为让代码能在 MSVC 下作为标准工程编译、并让 rad-ui 可作为 DLL 使用,做了以下修正 (均保持原有行为): - `include/rad/ui/property.h`:修正 `data_.val` → `data_.val_`;`was_destroyed_` 成员改为 无条件存在,保证不同 NDEBUG 设置的模块布局一致。 - `include/rad/stack_list.h`、`include/rad/stack_forward_list.h`、 `include/rad/ui/const_stack_list.h`:节点/迭代器的 `container` 成员改为无条件存在, 避免 DLL 与用户代码的类布局差异(跨模块 ABI 必须一致)。 - `include/rad/ui/try_cast.h`:类型标识由取静态变量地址改为编译期签名哈希,使跨模块 `is_instance_of`/`try_cast` 判断有效。 - `include/rad/ui/item.h`、`ink.h`、`model_view/tree_model.h`:`inline static` 单例改为在 .cpp 中定义一次,避免每个模块各持一份状态。 - `include/rad/ui/app.h` / `src/rad-ui/app.cpp`:`Application::inst()` 由头文件内联改为 库内定义,保证单例唯一。 - `src/direct2d/d2d_text_layout.cpp`:`reading_direction_at` 增加空布局保护,修复启动崩溃。 - `include/rad/ui/material3/combobox.h`:修正错误的相对包含路径 `../mv/listview.h` → `../model_view/listview.h`。