即时模式 GUI 编程

像 Dear ImGui 和 egui 这样的即时模式 GUI 框架,通过每帧重绘界面并避免显式事件处理器绑定,承诺更简单、更线性的 UI 代码。评论者将其与传统保留模式工具包(Qt、原生 OS 小部件、Web/CSS 布局)进行对比,认为即时模式在引擎内工具、调试 UI 和某些桌面应用中表现出色,但在复杂布局、平台集成、可访问性和功耗方面往往力不从心,除非额外重新构建了大量状态与布局系统。一些人指出,现代“声明式”系统如 React 介于两者之间;在实践中,正确选择与其说取决于范式的纯粹性,不如说取决于工具成熟度、性能需求,以及最终用户对 UI 复杂度和精致度的期望。

高层次:即时模式与保留模式 GUI

  • 即时模式:每一帧都用直线式代码描述 UI(例如 if Button(...) { ... })。用户代码中没有显式的小部件对象或已安装的事件处理器;状态往往隐藏在框架内部。
  • 保留模式:UI 是由其自身状态和回调组成的小部件/对象树;工具包“拥有”事件循环,并在需要时重绘。
  • 几位评论者强调,这种区分主要关乎公开 API,而不是底层是否保留状态。

事件循环、控制流与可调试性

  • 支持者喜欢单一主循环和线性、可调试的控制流,而不是分散的回调。
  • 批评者认为你并没有真正“摆脱”事件循环;操作系统仍然驱动事件,而且大多数 IMGUI 后端仍期望传统的事件循环模式。
  • 一些人认为控制流的简化能带来更快的开发速度,尤其适合不喜欢传统 UI 编程的开发者。

性能、电池与重绘

  • 怀疑者:持续的全屏重绘和逐帧布局可能浪费 CPU 和电量,尤其是在笔记本和移动设备上。有例子提到 IMGUI 工具在空闲时也会明显占用 CPU。
  • 其他人反驳说:
    • 你可以阻塞等待事件,只在交互时重绘。
    • IMGUI 框架可以跟踪“交互矩形”、脏区域,并缓存状态。
    • 一些库已经只在变化时重绘,不过围绕“省电模式”仍有未解决的问题和 PR。
  • 争论仍然存在:这些优化到底是 IMGUI 的内在特性,还是只是把额外复杂性转嫁给应用/框架作者。

复杂 UI、布局与状态

  • 支持 IMGUI 的经验来自游戏开发工具:复杂编辑器、检查器、性能分析器和调试 UI 都能成功构建,而且受益于与引擎逻辑的紧密集成。
  • 批评者表示,当从“临时调试器”扩展到完整工具时会很痛苦:人们开始模拟保留模式,手动持有大量状态和布局逻辑,导致代码变得混乱。
  • 布局是一个主要争议点:
    • 即时模式被认为非常适合简单布局和手动定位。
    • 响应式、多遍约束(例如文本换行、多个小部件居中、动态调整大小)更难;一些库(例如某些 Rust IMGUI)存在明显限制。
    • 其他人则描述了多遍布局算法在 IMGUI 中也能正常工作;挑战在于性能,以及每帧都需要重新运行布局。

使用场景与适用性

  • 非常适合:
    • 引擎内工具、调试叠加层、游戏编辑器、简单内部工具、已有渲染循环的图形密集型应用。
  • 适配较弱 / 存疑:
    • 面向终端用户的桌面应用,用户期望原生外观、低空闲 CPU 占用和强平台集成。
    • 移动应用,在这些场景下电量、性能、平台惯例和可访问性都至关重要;几位评论者建议改用原生或基于原生的工具包。

与 Web/React 风格 UI 的关系

  • 一些人把 React、Flutter、SwiftUI、Streamlit 等看作“类即时模式”,因为 UI 会在每次渲染时重新声明;React 的 VDOM 被描述为“在保留模式之上的即时模式”。
  • 其他人指出它们在事件流和状态管理上存在差异,但总体上同意两者在概念上有重叠。

可访问性、完整性与重复造轮子

  • 批评者强调,严肃应用需要本地化、可访问性和丰富的小部件;在 IMGUI 上重建所有这些是一项庞大工程,并且有重复成熟工具包已解决问题的风险。
  • 也有 IMGUI 框架集成可访问性层的例子,说明这虽可行但并不常见。
  • 几位评论者最终认为,两种范式都是工具;IMGUI 的主要优势是在某些类型应用上的开发效率,而不是对保留式 GUI 的通用替代。