
Dear ImGui 官方 FAQ 深度解读输入分发、ID Stack、纹理标识与字体 DPI 的权威实践【免费下载链接】imguiDear ImGui: Bloat-free Graphical User interface for C with minimal dependencies项目地址: https://gitcode.com/GitHub_Trending/im/imgui本文以 Dear ImGui 仓库中的官方文档 docs/FAQ.md 为骨架系统梳理该库从集成、输入分发、ID 唯一性、纹理显示到字体 DPI 处理的核心问题并结合 imgui.h、imgui.cpp、backends/imgui_impl_dx11.cpp 等源码给出可验证的实现依据。读完本文你能够独立完成判断鼠标/键盘输入该交给 ImGui 还是宿主应用、解决标签冲突导致的 ID 撞车、理解 1.92 之后ImTextureID/ImTextureRef双标识体系、以及按 DPI 正确缩放字体与样式。一、基本认知Dear ImGui 与立即模式 UI 的本质这个库到底叫什么官方明确库的全称是Dear ImGui既不是 ImGui也不是 IMGUI。IMGUIimmediate-mode graphical user interface立即模式图形用户界面这个术语在库诞生之前就已存在是 Unity 等其他项目也在使用的范式名称。为避免歧义、又不影响既有代码基础作者于 2015 年 12 月将其正式命名为 Dear ImGui。与 Qt/GTK/WPF 等传统工具包的对比FAQ 给出了一张简化对比表这里完整保留其核心差异点维度Dear ImGui传统工具包Qt/GTK/WPF 等UI 生成时机每帧全量提交只创建一次之后被修改布局特性完全动态可随时变化通常以编程方式发出适合反映动态数据布局基本静态若需反映动态数据需要额外且易错的代码性能优化方向针对最坏情况优化频繁变化时依然高效针对什么都不变的情况优化一旦有变化性能下降状态存储库只存极少数据不知道也不记住其他控件库保存完整的控件树和状态便于排版但数据冗余数据同步数据天然保持同步依赖回调/信号槽同步容易出错API 复杂度简单、易学基础操作非常容易偏底层、抽象少更复杂、更高层抽象、更依赖专业程序员运行平台几乎所有平台含 Web、主机、移动端、老旧系统主要是主流桌面平台FAQ 用两段惯用代码直观展示了两种范式的差别// 惯用的 Dear ImGui 代码 if (ImGui::Button(Save)) MySaveFunction(); ImGui::SliderFloat(Slider, m_MyValue, 0.0f, 1.0f);// 传统工具包的惯用代码 UiButton* button new UiButton(Save); button-OnClick MySaveFunction; parent-Add(button); UiSlider* slider new UiSlider(Slider); slider-SetRange(0.0f, 1.0f); slider-BindDatafloat(m_MyValue); parent-Add(slider);值得注意的是 FAQ 对 IMGUI 一词的界定IMGUI 指的是 API——应用与 UI 系统之间的接口本身其特征是应用代码持有数据、成为单一事实来源双方都尽量少保存对方相关的数据从而让同步自然且少出错。IMGUI 不指实现——UI 库内部发生什么都不重要。因此市面上IMGUI 一定怎样怎样的说法很多是把特定库如 Dear ImGui源自游戏行业需求的特性当成了范式特性。该用哪个版本FAQ 建议Release 标签只是偶尔打一下同步 master 分支一般是安全且推荐的库相当稳定回归问题报告后修复很快。另外存在一个docking分支额外包含 Docking停靠与 Multi-viewport多视口功能很多项目在使用且该分支会定期与 master 保持同步。当前仓库版本号为 imgui.h 中声明的IMGUI_VERSION 1.93.0 WIP文中涉及 1.92 之后 的新行为如ImTextureRef、动态字号均以该版本线为适用前提。去哪里找更多资料FAQ 开篇坦白这个库目前文档偏少假定使用者熟悉 C/C。可依赖的资料包括examples/目录下 20 多个独立示例应用如 examples/README.txtimgui_demo.cpp 中的ImGui::ShowDemoWindow()它覆盖了大部分特性docs/BACKENDS.md、docs/EXAMPLES.md、docs/FONTS.md 三份随仓库发布的文档imgui.cpp 顶部的文档注释与 imgui.h 的 API 注释以及ImGui::ShowMetricsWindow()它虽然定位是调试工具但暴露大量内部信息对理解概念非常有帮助。二、集成要点输入分发是第一道坎如何判断输入该给 ImGui 还是给应用读ImGuiIO结构中的io.WantCaptureMouse、io.WantCaptureKeyboard和io.WantTextInput三个标志io.WantCaptureMouse置位时丢弃/隐藏传给你的主应用的鼠标输入io.WantCaptureKeyboard置位时丢弃/隐藏键盘输入io.WantTextInput置位时可在移动端/主机上通知系统弹出屏幕键盘。关键原则无论上述标志取值如何鼠标/键盘输入都必须始终传给 Dear ImGui——因为例如需要在点击空白处取消窗口聚焦这类场景中检测事件。FAQ 给出的标准写法是void MyLowLevelMouseButtonHandler(int button, bool down) { // (1) 永远先把鼠标数据转发给 ImGui默认后端是自动的自定义后端则 ImGuiIO io ImGui::GetIO(); io.AddMouseButtonEvent(button, down); // (2) 只有在 ImGui 不捕获时才转发给你的应用/游戏。 if (!io.WantCaptureMouse) my_game-HandleMouseData(...); }从源码看这三个标志的语义与 FAQ 完全一致见 imgui.hbool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them // to your main game/application (either way, always pass on mouse inputs to imgui). bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, ... bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard.FAQ 特别警告io.WantCaptureMouse比任何手工的检测鼠标是否悬停在窗口上都更正确——它能正确处理从你的应用或 ImGui 窗口发起的拖拽、弹窗与模态窗口对输入的屏蔽等情形不要用悬停判断替代它。imgui.h 中IsWindowHovered()的注释也明确写道如果你是想决定把鼠标分发给谁不要用这个函数请用io.WantCaptureMouse。另一个容易踩坑的细节文本输入控件在 Return 键的KeyDown事件就释放焦点因此你应用随后收到的对应 KeyUp 事件通常已经是WantCaptureKeyboard false。如果应用逻辑介意这个 KeyUp可以自己记录哪些 keydown 被 ImGui 消费比如一个 bool 数组再过滤掉对应的 keyup。启用键盘/手柄导航键盘io.ConfigFlags | ImGuiConfigFlags_NavEnableKeyboard;手柄io.ConfigFlags | ImGuiConfigFlags_NavEnableGamepad;需后端支持FAQ 说明手柄导航最初是重点尤其适合 PS4、Switch、XB1 等无鼠标场景键盘导航现在也越来越可用。更多细节见 imgui.cpp 中 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS 一节的文档注释。无鼠标、无键盘、无屏幕的机器怎么办FAQ 给出的方案梯队共享主机鼠标使用 Synergy 类方案把 PC 鼠标无缝共享给主机/平板/手机其中 micro-synergy-client 项目提供了简洁可移植的uSynergy.c/.h可嵌入客户端基于 Synergy 1.x 协议——本仓库也附带了同款文件 examples/libs/usynergy/uSynergy.c 与 examples/libs/usynergy/uSynergy.h可直接作为嵌入参考。主机用户考虑用 DualShock4 触摸板或闲置摇杆模拟鼠标光标作为兜底。第三方远程渲染方案把渲染顶点通过局域网发送出去netImgui、Remote ImGui、imgui-ws 等思路让无屏机器也能使用 Dear ImGui。触摸输入FAQ 建议增大控件命中区域来适应触摸精度不足但推荐使用鼠标或手柄以便优化屏幕空间利用率。如何自己写一个后端FAQ 指向两处权威资料docs/BACKENDS.md 与 imgui.cpp 顶部的文档。backends/目录下有 20 余套现成实现OpenGL3、Vulkan、DirectX9/10/11/12、Metal、SDL2/3、Win32、Android 等可作参照。三、集成排错小方块与裁剪问题文本显示成小方块说明渲染器后端没有正确使用字体纹理或纹理还没上传到 GPU。FAQ 按情形给出排查路径使用标准后端且版本较老是否在ImGui_ImplXXX_NewFrame()之后修改了字体图集纹理图集过大也可能导致上传失败并参见 docs/FONTS.md。使用自定义后端确认字体纹理已上传 GPUshader 与渲染状态尤其是纹理绑定设置正确对照backends/现有实现并使用 RenderDoc 之类的图形调试器检查渲染状态。移动窗口时元素被裁剪/消失或画到窗口边界外多半是渲染函数中裁剪矩形处理错误。每条绘制命令都必须使用ImDrawCmd-ClipRect提供的裁剪矩形且 Dear ImGui 的矩形定义为(x1left, y1top, x2right, y2bottom)不是(x1, y1, width, height)。FAQ 引用了 DirectX11 后端的做法本仓库 backends/imgui_impl_dx11.cpp 的当前实现与之对应// 将 scissor/裁剪矩形投影到帧缓冲空间相对 DisplayPos 偏移 ImVec2 clip_off draw_data-DisplayPos; ImVec2 clip_min(pcmd-ClipRect.x - clip_off.x, pcmd-ClipRect.y - clip_off.y); ImVec2 clip_max(pcmd-ClipRect.z - clip_off.x, pcmd-ClipRect.w - clip_off.y); if (clip_max.x clip_min.x || clip_max.y clip_min.y) continue; // 裁剪矩形无效跳过 const D3D11_RECT r { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; device-RSSetScissorRects(1, r);核心要点把ClipRect的(x, y, z, w)视为(left, top, right, bottom)减去显示区域偏移后再应用 scissor。四、ID Stack 系统最常见的用户错误区FAQ 用加粗强调了两句话同一作用域使用相同 labelID 是最常见的用户错误空 label 等价于使用与父控件相同的 label。核心规则TL;DR控件 label 同时用于计算控件唯一标识符唯一标识是 label 父作用域父窗口、父 Tree Node 等 label的哈希PushID()追加不可见的标识前缀##something把后缀并入 ID 但不显示###something让 ID 计算忽略显示部分。多个同 label 控件同一作用域、数量有限时用##suffixButton(Play); // Label Play, ID hash of (MyWindow, Play) Button(Play##foo1); // Label Play, ID hash of (MyWindow, Play##foo1) Button(Play##foo2); // Label Play, ID hash of (MyWindow, Play##foo2)循环等更一般的情况用PushID()/PopID()压入前缀// 用指针/字符串 for (int i 0; i 100; i) { MyObject* obj Objects[i]; PushID(obj-Name); Button(Click); // ID hash of (Window, obj-Name, Click) PopID(); } // 用索引 for (int i 0; i 100; i) { PushID(i); Button(Click); // ID hash of (Window, i, Click) PopID(); }空 labelCheckbox(##On, b); // Label , ID hash of (..., ##On) // 无可见标签只有一个复选框动态 label### 用法Dear ImGui 每帧都可以提交不同控件但为保持控件状态哪个 Tree Node 展开、哪个按钮被聚焦内部依赖唯一 ID。想在改变 label 的同时保持 ID 不变用###Button(Hello###ID); // ID hash of (..., ID) Button(World###ID); // 同一 ID不同 label// 窗口标题带动态 FPS窗口 ID 始终是 MyGame 的哈希 char buf[128]; sprintf(buf, My game (%.1f FPS)###MyGame, io.Framerate); ImGui::Begin(buf); // label 在 Enable/Disable 间切换ID 始终为 (MyGame, MyButton) if (ImGui::Button(enabled ? Disable###MyButton : Enable###MyButton, { -FLT_MIN, 0.0f })) enabled !enabled; ImGui::End();label 与 ID Stack 的完整机制从源码结构看这套机制的实现非常紧凑。不可点击元素如Text()不需要 ID交互控件如Button()需要唯一 ID唯一 ID 用于内部跟踪活动控件、偶尔关联状态并且隐式地由标识路径的多个元素的哈希构成。imgui.cpp 中ImGuiWindow::GetID()的三个重载展示了 ID 的生成方式以IDStack.back()作为种子对字符串ImHashStr、指针ImHashData或整数做哈希。而 imgui.cpp 中PushID()就是把当前层种子哈希出的 ID 压入window-IDStack。这正对应 FAQ 的解释栈的每一层保存该层项目的种子最终 ID 是压入 ID 栈的一切内容的级联哈希。Begin(MyWindow); if (TreeNode(MyTreeNode)) // Tree Node 会隐式 PushID { Button(OK); // ID hash of (MyWindow, MyTreeNode, OK) TreePop(); } End();不同窗口、不同树位置的两个 OK 按钮不会冲突但在同一位置出现两个相同 ID 就是冲突——第二个 OK 与第一个撞车交互任一都会触发第一个而Button()会与Begin(MyWindow)撞车空 label 等价于父作用域 label。Tree 场景中 ID 的选取需要设计考量跟踪一个会变化的指针时用静态字符串作 ID 可保持展开状态稳定展示对象列表时用索引或指针作 ID 则有不同行为按需求选择。调试利器Demo Tools ID Stack Tool或直接调用ImGui::ShowIDStackToolWindow()见 imgui.h鼠标悬停控件即可看到生成唯一 ID 的中间值非常适合理解与排查 ID 问题。五、显示图片ImTextureID 与 ImTextureRef 双标识体系FAQ 对图片问题的回答是本仓库最新、信息量最大的部分之一。简短结论用ImGui::Image()、ImGui::ImageButton()或更底层的ImDrawList::AddImage()发出使用自定义纹理的绘制调用纹理标识方式完全由用户/引擎决定以不透明的ImTextureID值存储并传递默认ImTextureID可存 64 位需要时可在 imconfig.h 中#define ImTextureID MyType替换为自定义类型/结构体从磁盘加载图片文件并变成纹理不属于 Dear ImGui 的职责这是有意设计。1.92 引入的 ImTextureRefimgui.h 中可以看到基础类型定义与 FAQ 描述一致#ifndef ImTextureID typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or integer). #endif #ifndef ImTextureID_Invalid #define ImTextureID_Invalid ((ImTextureID)0) #endif而 imgui.h 中的ImTextureRefstruct ImTextureRef { ImTextureRef() { _TexData NULL; _TexID ImTextureID_Invalid; } ImTextureRef(ImTextureID tex_id) { _TexData NULL; _TexID tex_id; } inline ImTextureID GetTexID() const; // (_TexData ? _TexData-TexID : _TexID) // Members (either are set, never both!) ImTextureData* _TexData; // 一般由 ImFontAtlas 持有的纹理渲染时上传后转换为 ImTextureID ImTextureID _TexID; // _OR_ 低层后端纹理标识已由用户创建/上传 };要点与 FAQ 逐条对应1.92 起所有原来接受ImTextureID的绘制函数改接受ImTextureRefImTextureRef可由ImTextureID平凡地隐式构造所以你自己加载/创建的纹理绝大多数情况下只需存ImTextureID传参时自动变成ImTextureRef只有处理后端自管纹理目前主要是字体图集时才需要真正操纵ImTextureRef由ImTextureData*创建用ImTextureData::GetTexRef()——官方刻意不提供从ImTextureData*的构造器因为预期用户很少需要且会被大量旧代码误用官方刻意不提供ImTextureRef - ImTextureID的隐式转换因为渲染前做这个转换在技术上有损需要绑定当前图集时可用io.Fonts-TexRef面向 C 等无构造器语言的绑定生成器建议提供ImTextureRefFromID(ImTextureID)之类的辅助函数。长解释为什么这样设计Dear ImGui 的职责是生成网格——由渲染器无关格式的绘制命令与顶点组成帧末由你的渲染函数显示。纹理这个概念完全绑定在底层引擎/图形 API 上ImTextureID只是携带识别信息的类型默认 8 字节ImU64足够放一个指针或整数Dear ImGui 不解释你存的是什么只是原样透传给渲染函数。各示例后端的取法FAQ 原列表OpenGLImTextureID存GLuint见 backends/imgui_impl_opengl3.cpp 的ImGui_ImplOpenGL3_RenderDrawData()DirectX9存LPDIRECT3DTEXTURE9指针见 backends/imgui_impl_dx9.cppDirectX11存ID3D11ShaderResourceView*指针见 backends/imgui_impl_dx11.cppDirectX12存D3D12_GPU_DESCRIPTOR_HANDLE固定 64 位见 backends/imgui_impl_dx12.cpp。自定义引擎可以更进一步用高层纹理/材质类型做ImTextureID只要你的引擎有这类类型就值得用若刚起步、引擎层较薄跟随示例后端的默认表示即可。透传模型的典型用法// 用户代码把自定义纹理类型强转为 ImTextureID MyTexture* texture g_CoffeeTableTexture; ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(texture-Width, texture-Height)); // 渲染函数ImGui::Render() 之后调用原样取回 MyTexture* texture (MyTexture*)(intptr_t)pcmd-GetTexID(); MyEngineBindTexture2D(texture);C/C 小技巧u64 是 8 字节可以安全地用(ImTextureID)(intptr_t)value存取任意指针或整数并还原。理解了这套设计就明白加载 PNG 变成纹理为什么不在 Dear ImGui 范围内——这让你对自己的数据类型和显示方式拥有完全控制权。调试时可调用ImGui::ShowMetricsWindow()观察ImDrawList是如何生成的。六、数学类型与标准 C 类型ImVec2 的数学运算符默认不在 imgui.h 中导出数学运算符避免与你的数学类型冲突。需要时在包含头文件前或写入 imconfig.h定义IMGUI_DEFINE_MATH_OPERATORS即可获得基础运算符imgui.h 中的#ifdef IMGUI_DEFINE_MATH_OPERATORS即其实现入口。用自己的数学类型在 imconfig.h 中配置IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA宏添加与ImVec2/ImVec4的隐式转换之后就可以在任何接受ImVec2的 API 处直接传glm::vec2或自定义MyVector2。与 std::string / std::vector 的交互Dear ImGui 为保持高可移植性多语言绑定、多框架、老旧平台/编译器与实时游戏引擎所需的兼容性和性能不使用任何 std C 类型用裸类型char*而非std::string用std::string等可伸缩字符串接InputText()见现成封装 misc/cpp/imgui_stdlib.h对std::vector做下拉框/列表框优先用BeginCombo()/EndCombo()和ListBoxHeader()/ListBoxFooter()这类自己迭代提交条目的 API而不是老式且别扭的Combo()/ListBox()对大多数高层类型可以直接访问底层数据自己写一行小封装提示可以在自己的文件里往ImGui::命名空间加函数但不要修改 imgui 源码文件性能提示大量字符串的 UI 遍历中std::string可能带来不满意的性能。现代std::string的小字符串优化SSO不可配置且各实现不一致。若发现 UI 遍历开销大检查是否产生过多堆分配优先字面量、固定大小缓冲和自研辅助函数——典型场景是每帧动态构造大量有界长度的字符串想用std::string_viewFAQ 指出上游存在一个持续维护的features/string_view分支等待合适时机合入主线。七、自定义图形低层 ImDrawList APIFAQ 给出的标准示例可直接复制ImGui::Begin(My shapes); ImDrawList* draw_list ImGui::GetWindowDrawList(); ImVec2 p ImGui::GetCursorScreenPos(); // 当前 ImGui 光标位置 // 红色实心圆 draw_list-AddCircleFilled(ImVec2(p.x 50, p.y 50), 30.0f, IM_COL32(255, 0, 0, 255)); // 3 像素粗的黄色线 draw_list-AddLine(ImVec2(p.x, p.y), ImVec2(p.x 100.0f, p.y 100.0f), IM_COL32(255, 255, 0, 255), 3.0f); // 推进光标以在窗口中占据空间否则窗口会显得很小 ImGui::Dummy(ImVec2(200, 200)); ImGui::End();FAQ 补充的实践要点演示窗口中Demo Examples Custom Rendering有更多示例对应源码是 imgui_demo.cpp 中的ShowExampleAppCustomRendering()颜色生成IM_COL32(r,g,b,a)在编译期生成ImGui::GetColorU32(...)则会与当前style.Alpha相乘数学类型若在 imconfig.h 配置了IM_VEC2_CLASS_EXTRA直接用自己的数学类型ImVec2默认不导出运算符全局绘制层ImGui::GetBackgroundDrawList()/ImGui::GetForegroundDrawList()提供位于所有窗口之后/之前的绘制列表每个视口各一对适合快速绘制不归属于任何窗口的内容创建透明画布窗口Begin()时传NoBackground | NoDecoration | NoSavedSettings | NoInputs其中NoDecoration本身是NoTitleBar | NoResize | NoScrollbar | NoCollapse的快捷方式再用GetWindowDrawList()任意绘制甚至可以创建自己的ImDrawList实例用ImGui::GetDrawListSharedData()初始化再把自定义的ImDrawList/ImDrawData交给渲染函数。八、多线程FAQ 的四条原则同一个 Dear ImGui 上下文不能被多个线程并行使用若想在并行任务中偶尔用于调试同一上下文加锁即可若要在主/更新线程提交内容、在专用渲染线程渲染需要暂存stageImDrawData与纹理请求参考作者 imgui_club 仓库中的ImDrawDataSnapshot与ImTextureQueue辅助若使用多个上下文且想跨线程使用需#define GImGui使其成为 TLS线程局部存储变量见 imgui.cpp 中GImGui定义附近的说明。九、字体与文本DPI 处理1.92 起字体可动态任意尺寸缩放字体——从源码看ImGuiStyle中有对应的两个字段imgui.h// ImGuiStyle 相关字段 float FontSizeBase; // 应用外部全局缩放因子之前的基础字号 float FontScaleDpi; // 来自视口/显示器内容缩放的额外全局因子FAQ 的操作方式style.FontSizeBase 20.0f; // 选择默认字号 style.FontScaleDpi 2.0f; // 缩放所有字体 ImGui::PushFont(nullptr, 42.0f); // 只改字号会再乘以 style.FontScaleDpi ImGui::PushFont(new_font, 42.0f); // 同时改字体和字号在 docking 分支/多视口下io.ConfigDpiScaleFonts true; // (仅 docking 分支) 显示器 DPI 变化时在 Begin() 自动覆写 style.FontScaleDpi只缩放字体暂不缩放尺寸/内边距 io.ConfigDpiScaleViewports true; // (仅 docking 分支) 显示器 DPI 变化时缩放 Dear ImGui 与平台窗口缩放样式内边距、间距、粗细FAQ 明确标注这仍是大规模进行中的工作。样式值目前不能方便地动态缩放单视口应用可一次性调用style.ScaleAllSizes(factor);该 API 在 imgui.h 中有定义注释强调不要缩放字体、初始缩放因子小于 1 时慎用。需要改缩放因子时目前最实际的做法是重置 style 后重新调用。FAQ 给出的代码风格建议UI 代码应避免硬编码尺寸/定位常量优先用参考值的倍数表达例如用30 * ImGui::GetFontSize()代替硬编码高度 500。examples/中的应用部分 DPI 感知但无法从文件系统加载自定义字体所以观感欠佳DPI 没有自动魔法的根本原因是多 DPI问题docking 分支下多个视口横跨不同 DPI 的显示器对ImGuiStyle结构尚无满意方案——但字体如今是完全可缩放的。Windows 平台必须声明 DPI 感知否则 Windows 会缩放窗口、文字发虚。可选方案SDL2SDL_CreateWindow()传SDL_WINDOW_ALLOW_HIGHDPI 调用::SetProcessDPIAware()SDL3SDL_CreateWindow()传SDL_WINDOW_HIGH_PIXEL_DENSITYGLFW自动完成其他后端/封装项目Win32 后端提供ImGui_ImplWin32_EnableDpiAwareness()辅助方法或使用应用清单文件设置dpiAware属性。加载非默认字体用字体图集加载 TTF/OTFAddFontFromFileTTF声明见 imgui.hImGuiIO io ImGui::GetIO(); io.Fonts-AddFontFromFileTTF(myfontfile.ttf, size_in_pixels); // 之后把纹理数据交给后端上传 io.Fonts-GetTexDataAsRGBA32(); // 或 GetTexDataAsAlpha8()默认字体是等宽的 ProggyClean.ttf以 13 像素嵌入源码。等宽字体便于在字符串层面做水平对齐。更多细节见 docs/FONTS.md。FAQ 还特别提醒新手C/C 字符串字面量中反斜杠要写双份io.Fonts-AddFontFromFileTTF(MyFolder\MyFont.ttf, size); // 错误转义了 M io.Fonts-AddFontFromFileTTF(MyFolder\\MyFont.ttf, size); // 正确Windows io.Fonts-AddFontFromFileTTF(MyFolder/MyFont.ttf, size); // 同样正确图标、多字体、非拉丁字符图标最实用方式是把 FontAwesome 等图标字体合并进主字体之后在字符串中直接引用图标码点细节见 docs/FONTS.md。多字体用字体图集把它们打包进一张纹理仓库自带的字体文件可在 misc/fonts/ 查看Cousine、DroidSans、Karla、ProggyClean、Roboto 等。中日韩/西里尔等非拉丁字符1.922025 年 6 月起配合更新后的后端不再需要指定字形范围。1.92 之前需手动传 Unicode 范围如// [1.92 之前] 加载日文字形范围 io.Fonts-AddFontFromFileTTF(myfontfile.ttf, size_in_pixels, nullptr, io.Fonts-GetGlyphRangesJapanese()); // 或自定义范围游戏里可以喂入全部剧本文本只构建用到的字符 ImVectorImWchar ranges; ImFontGlyphRangesBuilder builder; builder.AddText(Hello world); // 添加字符串 builder.AddChar(0x7262); // 添加单个字符 builder.AddRanges(io.Fonts-GetGlyphRangesJapanese()); // 添加默认范围之一 builder.BuildRanges(ranges); io.Fonts-AddFontFromFileTTF(myfontfile.ttf, 16.0f, nullptr, ranges.Data);所有字符串必须使用UTF-8编码需告知编译器使用 UTF-8或在 C11 用u8hello语法用本地代码页日文 CP-923、西里尔 CP-1251 等写源码字面量不行。详见 docs/FONTS.md 的 About UTF-8 Encoding 一节。文本输入由你的应用调用io.AddInputCharacter()传入正确码点examples/中的应用都做了这件事。Windows 上可用WM_CHAR/WM_UNICHAR/WM_IME_CHAR消息取决于 Unicode/MultiByte 构建模式或用MultiByteToWideChar()/ToUnicode()取码点。依赖 IME 的语言可把 HWND 写入ImGui::GetMainViewport()-PlatformHandleRaw让默认Platform_SetImeDataFn()正确放置微软 IME。十、常见疑虑能否用它做严肃的工具FAQ 的回答是肯定的已有游戏编辑器、数据浏览器、调试器、性能分析器等非平凡工具。作者的体会是 API 的简单性非常有赋能感——你的 UI 贴近实时数据运行工具始终在线会让团队每个人都愿意造新工具该库面向全天候运行的 AAA 级应用做了效率与可扩展性设计IMGUI 范式提供的优化机会与传统 RMGUI 范式不同。能换肤吗有限度。可以改颜色、尺寸、内边距、圆角、字体但 Dear ImGui 设计目标是调试工具换肤空间有限官方明确它不是为做游戏界面而设计的当然巧妙使用低层 API 可以做到。为什么用 C 而不是 CDear ImGui 只用到一个很小的 C11 特性子集不依赖任何 C 头文件主要利用函数重载与默认参数让 API 更简洁此外用到命名空间、构造器以及模板ImVector。放弃这些特性会让 API 更啰嗦。面向 C 的自动生成的 C 接口 cimgui第三方项目可用于构建其他语言绑定FAQ 建议尽量在目标语言里复刻重载与默认参数否则 API 会更难用。如何参与/支持FAQ 列出的途径企业可通过商业支持/赞助资助开发个人可通过捐赠支持维护熟悉 Dear ImGui 和 C 者可关注 Issues、Discussions、Wiki 与 docs/TODO.txt 寻找切入点公开分享使用案例博客、截图能帮库积累可信度即使不求支持分享遇到的问题或不完整的 PR 也有价值。十一、参考文件索引主题仓库内参考FAQ 原文docs/FAQ.md输入捕获标志定义imgui.hImTextureID / ImTextureRefimgui.hID 哈希与 PushID 实现imgui.cpp裁剪矩形应用DX11 参考实现backends/imgui_impl_dx11.cpp样式缩放因子imgui.hstd::string 适配misc/cpp/imgui_stdlib.h字体加载细节docs/FONTS.md后端实现参考docs/BACKENDS.md、backends/示例工程examples/README.txt配置宏入口imconfig.h适用前提说明本文以当前仓库IMGUI_VERSION 1.93.0 WIPimgui.h为准ImTextureRef、动态字号、免字形范围等非拉丁加载等行为均自 1.92 起生效升级到 1.92 之前版本时需按 FAQ 中标注的1.92 之前分支处理ConfigDpiScaleFonts/ConfigDpiScaleViewports等自动 DPI 行为仅在 docking 分支可用。【免费下载链接】imguiDear ImGui: Bloat-free Graphical User interface for C with minimal dependencies项目地址: https://gitcode.com/GitHub_Trending/im/imgui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考