菜单

ImGui 上下文和 IO

相关源文件

概述

本文档解释了 Dear ImGui 的上下文(Context)和 IO(Input/Output)系统,它们构成了该库状态管理和与宿主应用程序通信的核心基础。ImGui Context 是包含 ImGui 运行所需所有数据的中央对象,而 IO 系统则提供了 ImGui 与宿主应用程序之间的接口。

有关窗口和布局系统的信息,请参阅 窗口和布局系统

上下文系统

上下文系统旨在将所有 Dear ImGui 状态维护在一个单独的容器对象中,这使得能够

  1. 使用多个独立的 ImGui 实例(每个实例都有自己的设置、字体、颜色、窗口等)
  2. 支持多线程或多进程环境
  3. 封装所有全局状态,避免使用全局变量
  4. 方便进行干净的初始化和清理

来源:imgui.h177-201 imgui_internal.h177-184 imgui.cpp566-1200

上下文创建与管理

上下文 API 简单而强大

来源:imgui.h324-330 imgui.cpp1250-1300

关键上下文函数

  • ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL) - 创建一个新的上下文
  • void DestroyContext(ImGuiContext* ctx = NULL) - 销毁一个上下文(NULL 表示当前上下文)
  • ImGuiContext* GetCurrentContext() - 获取当前上下文
  • void SetCurrentContext(ImGuiContext* ctx) - 设置当前上下文

Dear ImGui 使用一个全局指针 GImGui 来跟踪当前上下文。每个线程通常有自己的上下文,但当切换线程时,你需要显式设置当前上下文。

来源:imgui_internal.h217-220 imgui.cpp1250-1350

IO 系统

IO 系统充当 Dear ImGui 与你的应用程序之间的接口。它分为两个主要结构

  1. ImGuiIO - 核心输入/输出(鼠标、键盘、显示尺寸、计时等)
  2. ImGuiPlatformIO - 特定于平台的注册回调函数和钩子

来源:imgui.h332-339 imgui.cpp1400-1600

ImGuiIO 结构

ImGuiIO 结构通过 ImGui::GetIO() 访问,并作为你的应用程序和 Dear ImGui 之间的主要通信通道。

ImGuiIO 的关键组成部分

  1. 输入状态(每帧由你的应用程序更新)

    • 鼠标位置、按钮、滚轮
    • 键盘按键和修饰键
    • 手柄输入
    • 时间差
  2. 显示信息

    • 显示尺寸
    • DPI 缩放
    • 帧率
  3. 配置

    • ConfigFlags(导航、停靠选项)
    • 后端功能
    • 字体配置
  4. 输出

    • Want* 标志(WantCaptureMouse、WantCaptureKeyboard)
    • 指标和调试信息

来源:imgui.h180-181 imgui.cpp8500-9000

输入处理

自 1.87 版本以来,Dear ImGui 使用了一个基于按键/鼠标事件的 API。每帧,你的平台后端都应该调用类似以下函数:

  • io.AddKeyEvent(ImGuiKey key, bool down)
  • io.AddKeyAnalogEvent(ImGuiKey key, bool down, float v)
  • io.AddMousePosEvent(float x, float y)
  • io.AddMouseButtonEvent(int button, bool down)
  • io.AddMouseWheelEvent(float x, float y)

这使得 ImGui 能够跨所有平台一致地处理输入。直接设置 io.KeysDown[]io.MouseDown[] 的旧方法现已废弃。

来源:imgui.cpp1900-2100

ImGuiPlatformIO 结构

ImGuiPlatformIO,通过 ImGui::GetPlatformIO() 访问,包含特定于平台的注册回调函数和函数

  • 剪贴板函数:

    • Platform_GetClipboardTextFn
    • Platform_SetClipboardTextFn
  • 平台窗口管理(用于视口/停靠)

    • Platform_CreateWindow
    • Platform_DestroyWindow
    • Platform_ShowWindow
    • 以及更多
  • 附加服务:

    • Platform_SetImeDataFn
    • Platform_OpenInShellFn
    • Platform_LocaleDecimalPoint

在大多数情况下,这些函数由平台后端(例如 imgui_impl_win32、imgui_impl_glfw)自动设置。

来源:imgui.h187-188 imgui.cpp9500-9800

帧生命周期和状态管理

ImGui 维护跨帧的状态,重置某些元素同时保留其他元素,以创建连续的用户体验。

来源:imgui.cpp1400-1500 imgui.cpp3000-3200

关键状态管理概念

  1. 跨帧保留:

    • 窗口位置、大小和打开/关闭状态
    • 滚动位置
    • 控件状态(例如,活动输入框)
    • 输入值(例如,滑块位置)
  2. 每帧重置:

    • 悬停状态
    • 大多数项目状态
    • 临时标志
    • 动画状态
  3. 上下文 vs IO:

    • 上下文存储持久状态和 UI 层级结构
    • IO 提供每帧的输入数据和配置
    • 这种分离支持多上下文设计

来源:imgui.cpp3100-3400

多个上下文

Dear ImGui 支持多个上下文,这对于以下方面很有用:

  1. 多个独立的 UI - 不同的窗口或视口具有独立的状态
  2. 多线程 - 每个线程可以拥有自己的 ImGui 上下文
  3. DLL 边界 - 每个 DLL 可以拥有自己的上下文,具有独立内存分配器

使用多个上下文的示例

// In thread/module A:
ImGuiContext* ctx_a = ImGui::CreateContext();
ImGui::SetCurrentContext(ctx_a);
ImGui::GetIO().DisplaySize = ImVec2(1920, 1080);

// In thread/module B:
ImGuiContext* ctx_b = ImGui::CreateContext();
ImGui::SetCurrentContext(ctx_b);
ImGui::GetIO().DisplaySize = ImVec2(800, 600);

// Switch between contexts:
ImGui::SetCurrentContext(ctx_a);
// Use context A...

ImGui::SetCurrentContext(ctx_b);
// Use context B...

使用多个上下文时,需要注意以下几点:

  • 在进行任何 ImGui 调用之前设置当前上下文
  • 字体图集(可以共享或分开)
  • 内存管理(每个上下文都有自己的分配器)

来源:imgui.h324-330 imgui.cpp1250-1350

与后端系统的集成

ImGui Context 和 IO 系统通过一个定义明确的接口连接到平台和渲染器后端

来源:imgui.cpp272-368

典型的集成模式

  1. 初始化:

    // Create context
    ImGui::CreateContext();
    
    // Configure IO
    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
    
    // Initialize backends
    ImGui_ImplWin32_Init(hwnd);
    ImGui_ImplDX11_Init(device, context);
    
  2. 每帧更新:

    // Start frame
    ImGui_ImplDX11_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();
    
    // UI code
    ImGui::Begin("Example Window");
    ImGui::Text("Hello, world!");
    ImGui::End();
    
    // End frame and render
    ImGui::Render();
    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
    
  3. 关机:

    // Cleanup
    ImGui_ImplDX11_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();
    

来源:imgui.cpp272-368

内存管理

ImGui Context 以一种允许以下方式处理内存分配:

  1. 自定义分配器 - 应用程序可以提供自定义内存分配函数
  2. 每个上下文分配 - 每个上下文都有自己的分配器
  3. 区域式分配 - 许多小的、固定大小的分配都经过优化

可以通过以下方式自定义内存分配函数:

大多数 ImGui 容器(ImVector、ImChunkStream 等)在正常使用期间设计为尽量减少分配。

来源:imgui.cpp1200-1220

总结

ImGui Context 和 IO 系统构成了 Dear ImGui 架构的基础

  • ImGuiContext 维护单个 Dear ImGui 实例的所有状态
  • ImGuiIO 提供输入/输出通信的接口
  • ImGuiPlatformIO 处理特定于平台的注册回调函数和服务

它们共同使 Dear ImGui 能够保持

  • 有状态 - 在帧之间保留必要的状态
  • 可移植 - 以最少的代码更改在不同平台之间运行
  • 灵活 - 支持多个上下文和自定义配置
  • 自给自足 - 避免全局状态和外部依赖

理解这些系统对于将 Dear ImGui 正确集成到您的应用程序中至关重要,尤其是在处理多个上下文或自定义后端实现时。

来源:imgui.h324-340 imgui.cpp1200-1600