Skip to content

Commit 8abdd97

Browse files
Add imgui.app Tier-0 facade and adopt unpinned-toolchain UX (#4)
* Add Tier-0 imgui.app facade and app_minimal example imgui.app owns the full GLFW/OpenGL3 lifecycle; consumers supply only a per-frame UI callback. run() is an inline header-style template (UI as a template parameter, no <functional> dependency) and export imports imgui.core. Register src/app.cppm in mcpp.toml sources and add the examples/app_minimal Tier-0 consumer. * CI: set Linux default toolchain as unpinned-model transition step A fresh mcpp bootstrap defaults to a musl-static toolchain that cannot link the host X11/GL runtime on Linux. The package does not pin a toolchain in mcpp.toml; set an explicit environment default in CI on Linux only instead. Remove once mcpp bootstrap defaults to glibc. * Docs: unpinned-toolchain model, three-tier UX, and lock policy Document that the package pins no toolchain (mcpp resolves the environment/default) and that the GL runtime is closed by mcpp/mcpp-index (compat.glx-runtime), not bundled. Add imgui.app to the README modules list as Tier-0 (Tier-1 auto via imgui.backend contract, Tier-2 explicit imgui.backend.<impl>). Rewrite design doc 8.1 to the unpinned model and add the consumer-UX-and-lock sub-design doc. * ci: install gcc 16.1.0 before setting it as default (transition step) --------- Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
1 parent 7e241fa commit 8abdd97

9 files changed

Lines changed: 384 additions & 11 deletions

File tree

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# imgui-m: Consumer UX, Unpinned Toolchain, and Lock Policy
2+
3+
> 创建: 2026-06-03 · Owner: sunrisepeak · 仓库: mcpplibs/imgui-m
4+
> 关联 Master Plan: `/home/speak/workspace/github/agentdocs/2026-06-03-mcpp-ecosystem-architecture-plan.md`
5+
> (本文是该计划工作流 **W3 (imgui-m)** 的子设计文档,见 Master Plan §3.3 / §6)
6+
7+
本文记录 imgui-m 侧的三件事:不锁定工具链(unpinned)的决定与理由、三档递进式
8+
消费者 UX(默认 `imgui.app` / 自动契约 / 显式后端模块)、以及 lockfile 策略
9+
(GL 运行时闭合需要 `compat.glx-runtime`)。所有决定都对齐 Master Plan 的
10+
「Capability → Resolution Plan,两平面 + 三档收敛」统一模型,不为单次验收做特殊设计。
11+
12+
---
13+
14+
## 1. 不锁定工具链(unpinned)
15+
16+
### 决定
17+
18+
本包**不在任何 `mcpp.toml` 中声明 `[toolchain]`**(根包与所有 examples 均不 pin)。
19+
mcpp 解析环境/默认工具链;GL 运行时由 mcpp/mcpp-index 闭合
20+
(`compat.glx-runtime`,Linux 上由 `compat.glfw` 传递依赖引入),**不由本包打包**
21+
22+
### 理由
23+
24+
- **关注点分离 / 两平面**(Master Plan §2.1–2.2)。工具链与 GL 主机运行时属于
25+
*环境/主机平面*,不是包的内容。包在 `mcpp.toml` 里 pin 工具链会把环境决定
26+
硬编码进可移植的包元数据,破坏「同一包在不同环境用合适工具链」的能力。
27+
- **三档模型的"默认档"**:零配置可达,意图而非机制。包不写工具链 = 让 mcpp 用
28+
环境默认;需要时由消费者/CI 在*环境层*显式叠加(见 §4),而不是在包里固化。
29+
- **可移植**:macOS / Windows runner 不需要也不应继承 Linux 的工具链选择;不 pin
30+
让每个平台各自解析本地最优工具链。
31+
32+
---
33+
34+
## 2. 三档递进式消费者 UX
35+
36+
对齐 Master Plan §2.3「默认(零配置) → 自动匹配(意图驱动) → 显式指定(局部叠加)」。
37+
38+
### Tier-0 — 默认 / `imgui.app` facade(最少代码)
39+
40+
`imgui.app` 拥有完整的窗口/上下文生命周期,消费者只提供每帧 UI 回调。
41+
模块接口里以 inline header-style 模板提供 `run`,UI 回调为模板参数,**`<functional>`
42+
依赖**`export import imgui.core;`,因此消费者免费拿到 `ImGui::*` 表面。
43+
44+
```cpp
45+
import imgui.core;
46+
import imgui.app;
47+
48+
int main() {
49+
return ImGui::App::run([] {
50+
ImGui::Begin("hi");
51+
ImGui::TextUnformatted("imgui.app facade");
52+
ImGui::End();
53+
});
54+
}
55+
```
56+
57+
API:
58+
59+
```cpp
60+
export namespace ImGui::App {
61+
struct Options { int width = 1280; int height = 720; const char* title = "mcpp imgui app"; };
62+
template <class Ui> int run(Options opts, Ui&& ui); // 完整生命周期
63+
template <class Ui> int run(Ui&& ui); // 用默认 Options
64+
}
65+
```
66+
67+
`run()` 流程:InitGlfw → CreateWindow(RecommendedGlConfig) → MakeContextCurrent →
68+
SwapInterval(1) → CreateContext/SetCurrentContext → Backend::Init → 循环
69+
(PollEvents / Backend::NewFrame / ImGui::NewFrame / `ui()` / ImGui::Render /
70+
FramebufferSize+Viewport+ClearColor(0.10,0.10,0.12,1)+ClearColorBuffer+
71+
RenderDrawData(GetDrawData)+SwapBuffers)→ 退出时 Shutdown / DestroyContext /
72+
DestroyWindow / TerminateGlfw。成功返回 0,初始化失败返回非 0(并打印
73+
`Backend::LastError`)。
74+
75+
### Tier-1 — 自动 / `imgui.backend` 契约(意图驱动)
76+
77+
针对 `BackendApi` 概念与共享类型写**后端无关**代码;具体后端是哪个由上层选择。
78+
任何满足契约的后端都能代入,消费者代码不变。
79+
80+
```cpp
81+
import imgui.core;
82+
import imgui.backend; // BackendApi 概念 + GlConfig/Error/FbSize + RecommendedGlConfig()
83+
84+
template <ImGui::Backend::BackendApi Backend>
85+
void frame(typename Backend::Window* w) {
86+
Backend::PollEvents();
87+
Backend::NewFrame();
88+
ImGui::NewFrame();
89+
/* ... UI ... */
90+
ImGui::Render();
91+
const auto fb = Backend::FramebufferSize(w);
92+
Backend::Viewport(0, 0, fb.width, fb.height);
93+
Backend::RenderDrawData(ImGui::GetDrawData());
94+
Backend::SwapBuffers(w);
95+
}
96+
```
97+
98+
### Tier-2 — 显式 / `imgui.backend.<impl>` 模块 + alias(完全控制)
99+
100+
显式导入一个具体后端模块并 alias;换后端 = 换 import + 改一行 alias,其余不变。
101+
102+
```cpp
103+
import imgui.core;
104+
import imgui.backend.glfw_opengl3;
105+
using Backend = ImGui::Backend::GlfwOpenGL3; // 换后端只需改这两行
106+
107+
int main() {
108+
Backend::InitGlfw();
109+
auto* window = Backend::CreateWindow(960, 540, "demo");
110+
Backend::MakeContextCurrent(window);
111+
ImGuiContext* ctx = ImGui::CreateContext();
112+
ImGui::SetCurrentContext(ctx);
113+
Backend::Init(window);
114+
while (!Backend::WindowShouldClose(window)) { /* 手写帧循环 */ }
115+
Backend::Shutdown();
116+
ImGui::DestroyContext(ctx);
117+
Backend::DestroyWindow(window);
118+
Backend::TerminateGlfw();
119+
}
120+
```
121+
122+
复杂度单调下沉:Tier-0 最少代码 → Tier-2 最大控制。三档共享同一套
123+
`RecommendedGlConfig()` 跨平台 GL/GLSL 默认与同一后端契约。
124+
125+
---
126+
127+
## 3. Lockfile 策略
128+
129+
### 策略
130+
131+
`mcpp.lock` 必须反映当前 index 的解析结果。GL 运行时闭合依赖 `compat.glx-runtime`
132+
(Linux 上 `compat.glfw` 的传递依赖,把 host libGL/GLX 软链进 install 目录,
133+
供 rpath/RUNPATH 透传)。lock 应随当前 index 重生成,不手工编辑。
134+
135+
重生成方式:仓库根目录 `mcpp update`(清空 lock)→ `mcpp build`(重解析并重写 lock)。
136+
137+
### 实测现状(诚实记录)
138+
139+
- `compat.glx-runtime@2026.06.03` 确已存在于 index
140+
(`compat.glfw.lua` linux `deps` 含 `compat.glx-runtime`),并在 BMI 解析的依赖树中可见。
141+
- 但用 **mcpp 0.0.46** 重生成后,根 `mcpp.lock` 仅记录**直接依赖**
142+
(`compat.glfw` / `compat.imgui` / `compat.opengl`),**未**展开 `compat.glx-runtime`
143+
等传递依赖。即:当前 lock 格式只锁直接依赖,GL 运行时闭合发生在解析/链接期,
144+
而非在 lock 文本里展开。
145+
- 另注:本仓 `.gitignore` 忽略 `mcpp.lock`,故 lock 不入库。
146+
147+
这对应 Master Plan §1 的 **R3**(lock 相对 index 的 glx-runtime 接线 stale)与
148+
W1 的链接闭合修复(R2 rpath)。**完整的传递闭合(让 glx-runtime 显式落到 lock /
149+
RUNPATH)需要 mcpp 核心侧的解析/rpath 工作就绪**;imgui-m 侧已做到「不 pin + 用当前
150+
index 重生成 lock + 文档化策略」。待 mcpp 核心改动落地后,重生成的 lock/链接将
151+
自然纳入 glx-runtime,无需改本包。
152+
153+
---
154+
155+
## 4. CI 过渡步骤(显式环境默认,非特殊设计)
156+
157+
Linux CI 今天会红,根因是 Master Plan **R1**:全新机器 mcpp bootstrap 默认装
158+
musl-static 工具链,无法链接 glibc 世界的 X11/GL 栈。
159+
160+
修复:在 `.github/workflows/ci.yml` 的 "Install project tools" 之后、"Build library"
161+
之前,加**一步**(仅 Linux):
162+
163+
```yaml
164+
- name: Set default toolchain (env)
165+
if: runner.os == 'Linux'
166+
shell: bash
167+
run: mcpp toolchain default gcc@16.1.0
168+
```
169+
170+
为什么这不是"特殊设计":这是在 *CI 环境层* 显式声明环境默认工具链
171+
(三档模型里的"显式档,局部叠加"),与最终架构一致;**包仍不 pin**。它是过渡而非长期:
172+
待 Master Plan W1(mcpp bootstrap 默认改为 glibc 工具链)合并后,此步可直接删除。
173+
macOS / Windows 已通过,保持不变;矩阵与 `continue-on-error` 设置不变。
174+
175+
---
176+
177+
## 5. 与 Master Plan 的映射
178+
179+
| 本文小节 | Master Plan 对应 |
180+
|---|---|
181+
| §1 不 pin 工具链 | §2.1 两平面 / §2.3 三档默认档 / §1 R1 |
182+
| §2 三档 UX | §2.3 三档递进;W3 的 `imgui.app` (Tier-0) |
183+
| §3 lock 策略 | §1 R3(lock stale)/ W1 运行闭合(R2 rpath)/ §6 W3 |
184+
| §4 CI 过渡步骤 | §7 注 100–102(过渡:CI 环境默认,W1 后删) |
185+
186+
本文是 Master Plan 列出的三份子文档之一(imgui-m 侧);另两份分别在 mcpp 核心
187+
(runtime-closure-and-toolchain-defaults)与 mcpp-index(capability-runtime-metadata)。

.agents/docs/2026-06-03-imgui-backend-abstraction-design.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -245,17 +245,17 @@ int main() {
245245

246246
## 8. 跨平台工程化
247247

248-
### 8.1 toolchain
248+
### 8.1 toolchain(不锁定 / unpinned)
249249

250-
`mcpp.toml` 增加 macOS / Windows toolchain 条目(与 mcpp 支持的工具链命名对齐),
251-
例如:
250+
本包**不在任何 `mcpp.toml` 中锁定 toolchain**(没有 `[toolchain]` 段)。
251+
mcpp 解析环境/默认 toolchain;GL 运行时由 mcpp/mcpp-index 闭合
252+
(`compat.glx-runtime`,Linux 上由 `compat.glfw` 传递依赖引入),不由本包打包。
252253

253-
```toml
254-
[toolchain]
255-
linux = "llvm@20.1.7"
256-
macos = "llvm@20.1.7" # 或系统 clang;最终以 mcpp-index 可用项为准
257-
windows = "llvm@20.1.7"
258-
```
254+
> 过渡说明:目前全新机器上 mcpp bootstrap 默认是 musl-static 工具链,无法链接
255+
> 宿主 X11/GL 运行时。CI 在 Linux 上用一个显式环境默认步骤
256+
> (`mcpp toolchain default gcc@16.1.0`)绕开,而不是在 `mcpp.toml` 里锁定。
257+
> 待 mcpp bootstrap 默认改为 glibc 工具链后即可移除该步骤。详见
258+
> `.agents/docs/2026-06-03-consumer-ux-and-lock.md`
259259
260260
### 8.2 GL/GLSL 配置
261261

@@ -291,7 +291,7 @@ windows = "llvm@20.1.7"
291291
- `src/core.cppm` → 补 `ImVec4` 等(§7)
292292
- `tests/backend_test.cpp` → 针对新表面 + `static_assert(BackendApi<...>)`
293293
- `examples/minimal_window``examples/glfw_opengl3` → 统一 `Backend::` 用法 + `RecommendedGlConfig()`
294-
- `mcpp.toml`(toolchain + sources)、`.github/workflows/ci.yml`(矩阵)
294+
- `mcpp.toml`(sources;**不含** toolchain)、`.github/workflows/ci.yml`(矩阵 + Linux 环境默认 toolchain 过渡步骤)
295295
- `docs/architecture.md``README.md` 同步
296296

297297
> 旧的 `imgui.backend.glfw` / `imgui.backend.opengl3` 自由函数命名空间被

.github/workflows/ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ jobs:
6161
xlings update
6262
xlings install
6363
64+
# Transition step: a fresh mcpp bootstrap currently defaults to a
65+
# musl-static toolchain, which cannot link the host X11/GL runtime on
66+
# Linux. The package intentionally does not pin a toolchain in mcpp.toml;
67+
# we set an explicit environment default here instead. Remove this once
68+
# mcpp's bootstrap default becomes a glibc toolchain.
69+
- name: Set default toolchain (env)
70+
if: runner.os == 'Linux'
71+
shell: bash
72+
run: |
73+
mcpp toolchain install gcc 16.1.0
74+
mcpp toolchain default gcc@16.1.0
75+
6476
- name: Show mcpp version
6577
shell: bash
6678
run: mcpp --version

README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ module interfaces for the core API plus GLFW/OpenGL3 backend entry points.
77

88
## Modules
99

10+
Three tiers of consumer UX:
11+
12+
- **Tier-0** `imgui.app` — the convenience facade. Owns the whole window/context
13+
lifecycle; you supply only a per-frame UI callback.
14+
- **Tier-1** automatic via the `imgui.backend` contract — write backend-agnostic
15+
code against the `BackendApi` concept and shared types.
16+
- **Tier-2** explicit `imgui.backend.<impl>` — import a concrete backend module
17+
and alias it (`using Backend = ...;`); swap backend with one import + alias.
18+
19+
- `imgui.app`
20+
- Tier-0 facade. `ImGui::App::run(opts, ui)` / `ImGui::App::run(ui)` drive the
21+
full GLFW/OpenGL3 lifecycle using the default backend. `export import`s
22+
`imgui.core` so a consumer gets the `ImGui::*` surface for free.
1023
- `imgui.core`
1124
- Core Dear ImGui context, frame, widget, and draw-data APIs.
1225
- `imgui.backend`
@@ -39,6 +52,12 @@ The root package depends on:
3952
The repository does not vendor Dear ImGui sources. Source and header files come
4053
from compat packages through mcpp dependency metadata.
4154

55+
### Toolchain and GL runtime
56+
57+
The package does not pin a toolchain; mcpp resolves the environment/default
58+
toolchain. The GL runtime is closed by mcpp/mcpp-index (`compat.glx-runtime`,
59+
pulled in transitively by `compat.glfw` on Linux), not bundled by this package.
60+
4261
## Quick Start
4362

4463
```bash
@@ -86,7 +105,24 @@ mcpp run
86105
imgui = "0.0.1"
87106
```
88107

89-
Then import the modules you need:
108+
Then import the modules you need.
109+
110+
Tier-0 (`imgui.app` facade — least code):
111+
112+
```cpp
113+
import imgui.core;
114+
import imgui.app;
115+
116+
int main() {
117+
return ImGui::App::run([] {
118+
ImGui::Begin("hi");
119+
ImGui::TextUnformatted("imgui.app facade");
120+
ImGui::End();
121+
});
122+
}
123+
```
124+
125+
Tier-2 (explicit backend module + alias — full control):
90126

91127
```cpp
92128
import imgui.core;
@@ -135,6 +171,7 @@ The `0.0.1` release state is verified with mcpp `0.0.44`:
135171
- `mcpp build`
136172
- `mcpp test`
137173
- `cd examples/basic && mcpp run`
174+
- `cd examples/app_minimal && mcpp build`
138175
- `cd examples/minimal_window && mcpp build`
139176
- `cd examples/glfw_opengl3 && mcpp build`
140177

docs/architecture.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ upstream source ownership in compat packages.
55

66
## Goals
77

8+
- Offer a three-tier consumer UX:
9+
- Tier-0 `imgui.app` — convenience facade owning the full lifecycle.
10+
- Tier-1 automatic via the `imgui.backend` contract (`BackendApi` concept).
11+
- Tier-2 explicit `imgui.backend.<impl>` module + alias.
812
- Let consumers use explicit module imports:
13+
- `import imgui.app;` (Tier-0 facade; `export import`s `imgui.core`)
914
- `import imgui.core;`
1015
- `import imgui.backend;` (generic abstraction layer: shared types + contract)
1116
- `import imgui.backend.platform.glfw;`
@@ -27,6 +32,7 @@ upstream source ownership in compat packages.
2732
|-- mcpp.toml
2833
|-- src/
2934
| |-- core.cppm
35+
| |-- app.cppm (Tier-0 imgui.app facade)
3036
| `-- backends/
3137
| |-- backend.cppm (abstraction layer: types + BackendApi)
3238
| |-- platform_glfw.cppm (GLFW platform piece)
@@ -39,6 +45,7 @@ upstream source ownership in compat packages.
3945
| `-- backend_test.cpp
4046
`-- examples/
4147
|-- basic/
48+
|-- app_minimal/
4249
|-- minimal_window/
4350
`-- glfw_opengl3/
4451
```
@@ -52,6 +59,7 @@ backend implementation translation units, and depends on compat packages:
5259
[build]
5360
sources = [
5461
"src/core.cppm",
62+
"src/app.cppm",
5563
"src/backends/backend.cppm",
5664
"src/backends/platform_glfw.cppm",
5765
"src/backends/renderer_opengl3.cppm",
@@ -69,6 +77,10 @@ compat.opengl = "2026.05.31"
6977
The wrapper package does not vendor `third_party/imgui`. Upstream headers,
7078
core sources, and backend files are read from `compat.imgui`.
7179

80+
The package does not pin a toolchain; mcpp resolves the environment/default
81+
toolchain. The GL runtime is closed by mcpp/mcpp-index (`compat.glx-runtime`,
82+
a transitive dependency of `compat.glfw` on Linux), not bundled by this package.
83+
7284
## Module Wrappers
7385

7486
`src/core.cppm` adapts upstream `imgui.h` internally, then exports selected
@@ -80,6 +92,12 @@ internally and export explicit wrapper functions. The implementation `.cpp`
8092
files compile upstream backend sources from `compat.imgui/backends` so consumers
8193
do not need to copy backend sources into their own package.
8294

95+
`src/app.cppm` (`imgui.app`) is the Tier-0 facade. It `export import`s
96+
`imgui.core` and privately imports `imgui.backend.glfw_opengl3`, exposing
97+
`ImGui::App::run(...)` as an inline header-style template (the UI callback is a
98+
template parameter, so there is no `<functional>` dependency). It drives the
99+
full lifecycle so a consumer supplies only a per-frame UI callback.
100+
83101
## Validation
84102

85103
Primary proof points:
@@ -88,6 +106,7 @@ Primary proof points:
88106
mcpp build
89107
mcpp test
90108
cd examples/basic && mcpp run
109+
cd ../app_minimal && mcpp build
91110
cd ../minimal_window && mcpp build
92111
cd ../glfw_opengl3 && mcpp build
93112
```

examples/app_minimal/mcpp.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "imgui-app-minimal"
3+
version = "0.1.0"
4+
description = "Tier-0 imgui.app facade consumer"
5+
license = "MIT"
6+
7+
[dependencies]
8+
imgui = { path = "../.." }

examples/app_minimal/src/main.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import imgui.core;
2+
import imgui.app;
3+
4+
// Tier-0: imgui.app owns the whole lifecycle; we only supply the per-frame UI.
5+
int main() {
6+
return ImGui::App::run([] {
7+
ImGui::Begin("hi");
8+
ImGui::TextUnformatted("imgui.app facade");
9+
ImGui::End();
10+
});
11+
}

0 commit comments

Comments
 (0)