imgui 运行时面板
imgui 运行时面板
imgui 是 ScriptX 里最适合做“运行时调试面板”的一组 API。它不是单个控件,而是一整套窗口 builder、状态容器、布局指令、控件回调和显示模式管理能力。
这页我按源码当前真实行为来写,重点讲清楚这几件事:
- 每个 API 到底能传什么。
- 省略参数时到底会回退到“默认值”还是“当前值”。
- 哪些是纯别名,哪些只是看起来像别名。
- 哪些值会写进
state,后面可以用set/get/state继续控制。 - 哪些地方第一次接触最容易写错。
先记住 8 条:
imgui.window(id, options?)返回的是窗口 builder,同一个id会复用同一份窗口模型。- 大多数 builder 方法返回的也是 builder 本身,所以可以链式写;但
show()、close()、isShown()、get()、state()不一样。 clear()只清内容,不清状态。close()只隐藏窗口,不自动删状态。- 只要你后面准备
set/get/state,最好给交互控件显式传key。 size()/position()省略参数时会回到默认尺寸 / 默认坐标;resize()/x()/y()才更像“在当前值基础上改一部分”。- 布尔参数最稳妥的写法始终是
true/false,不要把"false"当成真正的false。 show()返回的是boolean,不是 builder,所以不要写成imgui.window(...).show().text(...)这种链式。
imgui.window(id, options?)
创建或获取一个窗口 builder。
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | 窗口实例名。不能为空;同一个 id 会复用同一份窗口模型。 |
options | object | 否 | 初始窗口配置。 |
返回值
- 返回一个窗口 builder。
- builder 上还会带一个可直接读取的
id属性,值就是你传入的窗口id。
最小例子
const panel = imgui.window("debug");
panel
.title("ScriptX Debug")
.size(360, 520)
.text(`pkg=${lpparam.packageName}`)
.show();
窗口为什么会“复用”
同一个运行时里,只要 id 一样,下面这些东西都会继续沿用:
- 窗口配置
- 已写入的状态值
- 之前追加过的内容,除非你显式
clear()
const panel = imgui.window("demo");
panel.checkbox("Enable", "enable_key", true);
panel.show();
panel.set("enable_key", false);
panel.close();
const panel2 = imgui.window("demo");
log(panel2.get("enable_key", true)); // false
options 支持的字段
源码当前支持这些字段:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | 窗口 id | 初始标题。 |
width / w | number | 约 332dp 换算后的像素值 | 初始宽度。 |
height / h | number | 约 500dp 换算后的像素值 | 初始高度。 |
minWidth | number | 280 | 最小宽度。 |
minHeight | number | 180 | 最小高度。 |
maxWidth | number | 0 | 最大宽度;0 表示不限。 |
maxHeight | number | 0 | 最大高度;0 表示不限。 |
minSize | number[] | 无 | 同时设置最小宽高,至少要有两个元素。 |
maxSize | number[] | 无 | 同时设置最大宽高,至少要有两个元素。 |
x | number | 约 20dp 换算后的像素值 | 初始 X 坐标。 |
y | number | 约 100dp 换算后的像素值 | 初始 Y 坐标。 |
posX | number | 跟 x 一起解析 | 仅 options 支持的 X 别名。 |
posY | number | 跟 y 一起解析 | 仅 options 支持的 Y 别名。 |
alpha | number | 1.0 | 最终会被限制到 0.2 ~ 1.0。 |
theme | string | modern_dark | 主题名;空字符串会回到默认主题。 |
fontScale | number | 1.40 | 最终限制到 1.0 ~ 3.2。 |
uiScale | number | 1.14 | 最终限制到 0.85 ~ 2.4。 |
widgetScale | number | 1.14 | uiScale 的选项别名。 |
scale / zoom | number | 无 | 同时设置 fontScale 和 uiScale。 |
closable | boolean | true | 是否允许关闭。 |
windowCollapsible / collapsibleWindow | boolean | false | 是否允许标题栏折叠。 |
resizable | boolean | true | 是否允许拖拽改变大小。 |
displayMode | string | in_app | 显示模式。 |
outsideApp / showOutsideApp / systemOverlay | boolean | false | true 时切到系统悬浮层;false 时切回 in_app。 |
accessibilityOverlay / a11yOverlay | boolean | false | true 时切到无障碍悬浮层;false 时切回 in_app。 |
secure / antiCapture | boolean | false | 是否开启防截屏安全窗口。 |
touchPassthrough / passthrough / clickThrough / touchThrough | boolean | false | 是否开启触摸穿透。 |
onClose | function | 无 | 关闭回调。 |
布尔值到底怎么解析
imgui 这一层的布尔值不是“随便传字符串都能猜对”。当前源码规则是:
| 传入值 | 实际结果 |
|---|---|
true | true |
false | false |
1 | true |
0 | false |
"true" | true |
"1" | true |
"false" | 不会直接当成 false,通常会回退到默认值 / 当前值 |
| 其他字符串 | 通常回退到默认值 / 当前值 |
所以最稳妥的写法只有一种:
resizable: true
closable: false
一个比较完整的初始化例子
const panel = imgui.window("network", {
title: "Network Panel",
width: 380,
height: 560,
minWidth: 320,
minHeight: 220,
maxWidth: 900,
maxHeight: 0,
x: 24,
y: 96,
alpha: 0.96,
theme: "rose_glass",
fontScale: 1.5,
uiScale: 1.2,
windowCollapsible: true,
resizable: true,
displayMode: "in_app"
});
imgui.close(id)
按窗口 id 关闭指定窗口。
返回值
booleantrue:找到了对应窗口并触发关闭逻辑。false:id为空,或者当前没有这个窗口。
例子
imgui.close("debug");
imgui.closeAll()
关闭当前运行时里所有已登记的 imgui 窗口。
返回值
number- 返回本次关闭了多少个窗口。
例子
const closed = imgui.closeAll();
log(`closed=${closed}`);
imgui.isShown(id)
按窗口 id 查询窗口当前是否正在显示。
返回值
boolean
例子
if (!imgui.isShown("debug")) {
imgui.window("debug").text("hello").show();
}
imgui.themes()
返回当前内置主题名列表。
返回值
string[]
当前内置主题
[
"modern_dark",
"vibrant_night",
"crystal_clear",
"cyan_dusk",
"orange_dark",
"purple_dream",
"blue",
"pink",
"imgui_dark",
"imgui_light",
"imgui_classic",
"rose_prism",
"rose_glass",
"sakura_night",
"pearl_mist",
"petal_dawn",
"velvet_rose",
"crystal_rose",
"midnight_petal",
"milk_tea_rose"
]
例子
log(JSON.stringify(imgui.themes(), null, 2));
builder.show()
真正请求把当前窗口显示出来。
返回值
booleantrue:本次显示请求成功进入显示流程。false:显示失败,常见原因是当前模式权限不满足、活动窗口不可用、或者混用了不同displayMode。
例子
const shown = imgui
.window("debug")
.text("hello")
.show();
log(`shown=${shown}`);
builder.close()
关闭当前 builder 对应的窗口。
返回值
boolean
说明
- 会触发
onClose回调。 - 只隐藏窗口,不清状态。
例子
const panel = imgui.window("debug");
panel.close();
builder.isShown()
判断当前 builder 对应的窗口是否正在显示。
返回值
boolean
例子
const panel = imgui.window("debug");
if (!panel.isShown()) {
panel.show();
}
builder.clear()
清空当前窗口已经追加过的内容项。
返回值
- 返回 builder 本身,可继续链式调用。
非常重要
clear()只清items- 不清
state - 不重置窗口配置
例子
const panel = imgui.window("demo");
panel.text("old").show();
panel.clear();
panel.text("new").show();
builder.set(key, value)
直接写入一个状态槽位。
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
key | string | 是 | 不能为空。 |
value | any | 否 | 写入的值会先做 Rhino unwrap。 |
返回值
- 返回 builder 本身。
例子
panel.set("enable_hook", true);
panel.set("base_url", "https://example.com");
panel.set("accent_color", "#FF46BD87");
builder.get(key, fallback?)
读取一个状态槽位。
返回值
- 如果状态存在,返回实际值。
- 如果状态不存在,返回
fallback。 - 如果
key为空,也直接返回fallback。
例子
const enabled = panel.get("enable_hook", false);
const host = panel.get("host_input", "127.0.0.1");
builder.state(key?)
读取状态快照。
调用形式
| 写法 | 返回值 |
|---|---|
state("some_key") | 单个状态值,找不到时可能是 null |
state() | 整个状态对象 |
例子
log(JSON.stringify(panel.state(), null, 2));
log(panel.state("enable_hook"));
builder.title(text)
设置窗口标题。
返回值
- 返回 builder 本身。
例子
panel.title("HTTP Debug");
builder.size(width, height)
设置窗口尺寸。
返回值
- 返回 builder 本身。
重要细节
size()省略参数时,不是保留当前值。- 缺失的宽高会回退到默认尺寸:约
332dp x 500dp换算后的运行时像素值。
例子
panel.size(420, 640);
builder.resize(width, height) / builder.resizeTo(width, height)
按当前窗口尺寸做“部分更新式”改动。
返回值
- 返回 builder 本身。
和 size() 的区别
size()缺参时回到默认尺寸。resize()/resizeTo()缺参时保留当前宽高。
例子
panel.resize(500); // 只改宽度,高度保持当前值
panel.resizeTo(null, 720); // 只改高度,宽度保持当前值
builder.width(value) / builder.w(value)
只改窗口宽度。
返回值
- 返回 builder 本身。
例子
panel.width(460);
panel.w(520);
builder.height(value) / builder.h(value)
只改窗口高度。
返回值
- 返回 builder 本身。
例子
panel.height(640);
panel.h(720);
builder.minSize(width, height)
设置最小尺寸。
规则
- 内部会把最小宽高限制到至少
1。 - 如果你设置的最小值已经大于当前最大值,最大值会被一起抬上来。
例子
panel.minSize(320, 220);
builder.maxSize(width, height)
设置最大尺寸。
规则
0表示这一轴不设上限。- 如果设置的最大值小于当前最小值,最小值会一起被压下来。
例子
panel.maxSize(900, 0);
builder.minWidth(value)
只改最小宽度。
例子
panel.minWidth(300);
builder.minHeight(value)
只改最小高度。
例子
panel.minHeight(200);
builder.maxWidth(value)
只改最大宽度。
例子
panel.maxWidth(960);
builder.maxHeight(value)
只改最大高度。
例子
panel.maxHeight(0);
builder.position(x, y) / builder.pos(x, y) / builder.moveTo(x, y)
设置窗口位置。
重要细节
- 这组方法省略参数时,缺失轴会回到默认位置。
- 默认位置是约
20dp, 100dp换算后的运行时像素值。 - 它不像
x()/y()那样保留另一轴当前值。
例子
panel.position(24, 96);
panel.pos(40, 120);
panel.moveTo(60, 140);
builder.x(value)
只改 X 坐标,Y 保持当前值。
例子
panel.x(80);
builder.y(value)
只改 Y 坐标,X 保持当前值。
例子
panel.y(180);
builder.alpha(value)
设置窗口透明度。
规则
- 内部会限制到
0.2 ~ 1.0。
例子
panel.alpha(0.92);
builder.fontScale(value)
设置字体缩放。
规则
- 内部会限制到
1.0 ~ 3.2。
例子
panel.fontScale(1.5);
builder.uiScale(value) / builder.widgetScale(value)
设置控件和界面整体缩放。
规则
- 内部会限制到
0.85 ~ 2.4。 widgetScale()是纯别名。
例子
panel.uiScale(1.2);
panel.widgetScale(1.1);
builder.scale(value) / builder.zoom(value)
同时设置 fontScale 和 uiScale。
规则
- 这不是单独第三套缩放,而是一次同时改两项。
例子
panel.scale(1.3);
panel.zoom(1.15);
builder.theme(name)
设置主题。
规则
- 传空字符串会回到默认主题
modern_dark。 - 最稳妥的做法是先用
imgui.themes()看允许值。
例子
panel.theme("rose_glass");
builder.closable(flag)
设置窗口是否允许关闭。
例子
panel.closable(false);
builder.windowCollapsible(flag) / builder.collapsibleWindow(flag)
设置标题栏是否允许折叠窗口内容。
例子
panel.windowCollapsible(true);
builder.resizable(flag)
设置窗口是否允许拖拽调整大小。
例子
panel.resizable(true);
builder.displayMode(mode)
显式设置窗口显示模式。
推荐值
| 推荐写法 | 实际模式 |
|---|---|
"in_app" | IN_APP |
"system_overlay" | SYSTEM_OVERLAY |
"accessibility_overlay" | ACCESSIBILITY_OVERLAY |
还能识别的别名
| 模式 | 兼容别名 |
|---|---|
in_app | app, inapp, inside, inside_app, within_app |
system_overlay | overlay, system, application_overlay, outside, outside_app, global |
accessibility_overlay | accessibility, a11y, a11y_overlay, service_overlay |
写错会怎样
- 不会直接抛异常。
- 会保留当前模式。
例子
panel.displayMode("system_overlay");
builder.outsideApp(flag) / builder.showOutsideApp(flag) / builder.systemOverlay(flag)
快捷切换到系统悬浮层模式。
规则
true:切到system_overlayfalse:直接回到in_app- 不是“回到上一个模式”
例子
panel.systemOverlay(true);
builder.accessibilityOverlay(flag) / builder.a11yOverlay(flag)
快捷切换到无障碍悬浮层模式。
规则
true:切到accessibility_overlayfalse:直接回到in_app
例子
panel.accessibilityOverlay(true);
builder.secure(flag) / builder.antiCapture(flag)
设置窗口是否开启防截屏安全标记。
例子
panel.secure(true);
builder.touchPassthrough(flag) / builder.passthrough(flag) / builder.clickThrough(flag) / builder.touchThrough(flag)
设置窗口是否允许触摸穿透。
重要说明
- 在
system_overlay下配合touchPassthrough(true)时,运行时可能为了可信触摸链路把交互宿主提升到无障碍 overlay。 - 如果你只是普通调试面板,先不要开它。
例子
panel
.displayMode("system_overlay")
.touchPassthrough(true);
builder.onClose(fn)
注册关闭回调。
回调签名
() => {}
例子
panel.onClose(() => {
log("panel closed");
});
builder.text(text)
追加普通文本。
例子
panel.text("hello");
builder.textWrapped(text) / builder.wrappedText(text)
追加自动换行文本。
适合场景
- 长日志
- 多行说明
- 宽度不固定的调试输出
例子
panel.textWrapped("This is a long line that should wrap automatically.");
builder.textColored(text, color) / builder.coloredText(text, color)
追加彩色文本。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
text | string | 要显示的文本 |
color | string | 颜色字符串 |
颜色写法
这组方法当前最稳妥的是传字符串颜色,不要传数组。推荐用:
#RRGGBB#AARRGGBB0xRRGGBB0xAARRGGBB
例子
panel.textColored("Warning", "#FFFF8844");
builder.bullet(text)
追加带项目符号的文本。
例子
panel.bullet("Item A");
builder.textDisabled(text) / builder.disabledText(text)
追加灰态文本。
例子
panel.textDisabled("readonly hint");
builder.separator()
追加一条分隔线。
例子
panel.separator();
builder.separatorText(text)
追加带标题的分隔线。
例子
panel.separatorText("Network");
builder.spacing(count?)
追加空白间距。
规则
- 默认是
1 - 小于
1会被修正成1
例子
panel.spacing();
panel.spacing(2);
builder.newLine()
显式换一行。
例子
panel.text("A").newLine().text("B");
builder.sameLine(spacing?)
让后一个控件继续排在同一行。
两种写法
| 写法 | 含义 |
|---|---|
sameLine(spacing) | 只指定控件间距 |
sameLine(offsetX, spacing) | 指定从当前行起点算起的偏移和间距 |
例子
panel.button("A");
panel.sameLine(12);
panel.button("B");
panel.sameLine(0, 16);
panel.smallButton("C");
builder.dummy(width, height)
插入一个不可见占位块。
默认值
width = 0height = 0
例子
panel.dummy(0, 12);
builder.indent(width?)
增加缩进。
规则
- 默认
0 - 传
0时让 ImGui 使用默认缩进宽度
例子
panel.indent();
panel.text("inside");
panel.unindent();
builder.unindent(width?)
减少缩进。
例子
panel.unindent();
builder.cursorPos(x, y)
设置下一个控件的局部游标位置。
例子
panel.cursorPos(18, 52).button("Jump");
builder.cursorX(x)
只设置下一个控件的 X 位置。
例子
panel.cursorX(220).text("Right");
builder.cursorY(y)
只设置下一个控件的 Y 位置。
例子
panel.cursorY(120).text("Lower");
builder.itemWidth(width)
设置“下一个输入类控件”的宽度。
重要细节
- 这一项底层对应
SetNextItemWidth - 它只影响后续最近的那个输入控件,不是整个窗口永久宽度
例子
panel
.itemWidth(180)
.inputText("Name", "name", "jsxhook");
builder.beginGroup()
手动开始一个 group 容器。
说明
- 必须和
endGroup()配对。 - 适合你想自己精确控制 begin / end 的场景。
例子
panel.beginGroup();
panel.text("Line 1");
panel.text("Line 2");
panel.endGroup();
builder.endGroup()
结束手动 group 容器。
例子
panel.endGroup();
builder.group(fn)
回调式 group 容器,会自动补齐 begin / end。
回调参数
- 回调里拿到的是原窗口 builder
例子
panel.group(ui => {
ui.text("Line 1");
ui.text("Line 2");
});
builder.beginDisabled(flag?)
手动开始一个禁用态容器。
默认值
flag默认是true
例子
panel.beginDisabled(true);
panel.button("Disabled");
panel.endDisabled();
builder.endDisabled()
结束手动禁用态容器。
builder.disabled(flag?, fn?)
回调式禁用态容器。
说明
flag默认true- 省掉
flag时可以直接传回调
例子
panel.disabled(true, ui => {
ui.button("Disabled button");
ui.inputText("Readonly-like", "x");
});
builder.beginChild(id, width?, height?, options?)
手动开始一个子区域。
默认值
| 字段 | 默认值 |
|---|---|
width | 0 |
height | 180 |
border | true |
options 支持字段
labelwidthheightborderbuild
例子
panel.beginChild("logs", 0, 220, { border: true });
panel.textWrapped("Long content...");
panel.endChild();
builder.endChild()
结束手动 child 容器。
builder.child(id, width?, height?, options?, fn)
回调式 child 容器。
例子
panel.child("logs", {
width: 0,
height: 220,
border: true
}, ui => {
ui.textWrapped("Long content...");
});
builder.beginCollapsible(label, ...)
手动开始一个折叠容器。
常用参数形式
panel.beginCollapsible("HTTP Tools");
panel.beginCollapsible("HTTP Tools", "http_tools");
panel.beginCollapsible("HTTP Tools", { defaultOpen: true });
options 支持字段
labeldefaultOpenbuild
说明
- 这一组和
tree/treeNode/collapsingHeader底层走的是同一套折叠容器实现。
builder.endCollapsible()
结束手动折叠容器。
builder.collapsible(label, ..., fn)
回调式折叠容器。
例子
panel.collapsible("HTTP Tools", {
defaultOpen: true
}, ui => {
ui.button("Ping");
ui.button("POST");
});
builder.beginTree(label, ...)
beginCollapsible() 的树状命名版本,底层行为一致。
builder.endTree()
结束手动 tree 容器。
builder.tree(label, ..., fn)
回调式 tree 容器。
例子
panel.tree("Classes", ui => {
ui.bullet("com.demo.MainActivity");
});
builder.beginTreeNode(label, ...)
beginCollapsible() 的 tree node 命名版本,底层行为一致。
builder.endTreeNode()
结束手动 tree node 容器。
builder.treeNode(label, ..., fn)
回调式 tree node 容器。
builder.collapsingHeader(label, ..., fn)
折叠头写法,底层同样还是折叠容器。
例子
panel.collapsingHeader("Advanced", {
defaultOpen: false
}, ui => {
ui.text("More settings...");
});
builder.beginColumns(id?, count, options?)
手动开始分栏布局。
默认值
| 字段 | 默认值 |
|---|---|
id | 自动生成 |
count | 2 |
border | false |
options 支持字段
countborderbuild
例子
panel.beginColumns("cols", 2, { border: true });
panel.text("Left");
panel.nextColumn();
panel.text("Right");
panel.endColumns();
builder.nextColumn()
跳到下一列。
说明
- 只有在
columns容器里才有意义。
builder.endColumns()
结束手动 columns 容器。
builder.columns(id?, count, options?, fn)
回调式分栏布局。
例子
panel.columns("cols", 2, { border: true }, ui => {
ui.text("Left");
ui.nextColumn();
ui.text("Right");
});
builder.beginTable(id, headersOrCount, options?)
手动开始表格。
参数规则
| 参数 | 说明 |
|---|---|
id | 必填,不能为空 |
headersOrCount | 可以是列标题数组,也可以是列数 |
options 支持字段
headerscolumnCountcountshowHeadersheadersRowborderrowBgresizablestretchbuild
默认值
| 字段 | 默认值 |
|---|---|
border | true |
rowBg | true |
resizable | true |
stretch | true |
showHeaders | 如果你传了标题数组,默认 true;否则默认 false |
例子
panel.beginTable("users", ["Name", "Role"], {
showHeaders: true,
border: true,
rowBg: true
});
panel.text("Alice");
panel.nextRow();
panel.text("Admin");
panel.endTable();
builder.nextRow()
在表格里切到下一行。
说明
- 只在 table 容器内有意义。
builder.tableNextRow()
nextRow() 的别名。
builder.endTable()
结束手动 table 容器。
builder.table(id, headersOrCount, options?, fn)
回调式表格。
例子
panel.table("users", ["Name", "Role"], {
showHeaders: true,
border: true,
rowBg: true,
resizable: true,
stretch: true
}, ui => {
ui.text("Alice");
ui.nextRow();
ui.text("Admin");
});
builder.beginTabBar(id?)
手动开始 tab bar。
规则
id可省略。- 省略时会自动生成内部 id。
例子
panel.beginTabBar("tabs");
panel.beginTab("General");
panel.text("General page");
panel.endTab();
panel.endTabBar();
builder.endTabBar()
结束手动 tab bar。
builder.beginTab(label, options?)
手动开始一个 tab。
说明
- 第一个参数是显示用
label - 如果第二个参数是字符串,会当作显式 key
options目前主要支持label和build
builder.endTab()
结束手动 tab。
builder.tabs(id?, fn)
回调式 tab 构建器。
这是 imgui 里最容易误会的一点
tabs() 最外层回调拿到的不是原窗口 builder,而是一个“只负责建 tab 的对象”。
这个对象当前主要有一个方法:
tab(label, key?, options?, build?)
而真正的窗口 builder,会在每个 tab(...) 的回调里再传回来。
例子
panel.tabs("panel_tabs", tabs => {
tabs.tab("General", ui => {
ui.text("General page");
});
tabs.tab("Network", ui => {
ui.text("Network page");
});
});
builder.button(label, ...)
普通按钮。
常见调用形式
panel.button("Ping", onClick);
panel.button("Ping", "ping_btn", onClick);
panel.button("Ping", "ping_btn", 140, 42, onClick);
panel.button("Ping", "ping_btn", {
width: 140,
height: 42,
onClick(state) {}
});
回调签名
(state) => {}
说明
- 按钮本身不保存“是否按下”的持久状态。
- 但如果你显式传了
key,仍然可以用这个key去定位这个控件本身。
builder.smallButton(label, ...)
紧凑按钮。
重要细节
- 它底层虽然复用了
button()的参数解析器, - 但真正创建控件时会把
width和height强制写成0 - 所以你给
smallButton()传宽高,当前实现里不会生效
例子
panel.smallButton("Refresh", state => {
log("refresh");
});
builder.checkbox(label, ...)
布尔复选框。
常见调用形式
panel.checkbox("Enable Hook");
panel.checkbox("Enable Hook", true);
panel.checkbox("Enable Hook", "enable_hook", true);
panel.checkbox("Enable Hook", "enable_hook", {
defaultValue: true,
onChange(value, state) {}
});
写进 state 的值
boolean
回调签名
(value, state) => {}
builder.toggle(label, ...) / builder.switch(label, ...)
开关样式布尔控件。
说明
- 这两组当前底层都走
ImGuiSwitchItem - 行为基本一样
- 差别主要只是自动生成 key 时的前缀不同
例子
panel.toggle("Overlay", "overlay_enabled", false, (value, state) => {
log(`overlay=${value}`);
});
builder.selectable(label, ...)
可选中项。
写进 state 的值
- 也是
boolean - 表示“这一项是否选中”,不是索引
例子
panel.selectable("Select Me", "selected_flag", false, (value, state) => {
log(`selected=${value}`);
});
builder.inputText(label, ...)
文本输入框。
常见调用形式
panel.inputText("Host");
panel.inputText("Host", "127.0.0.1");
panel.inputText("Host", "host_input", "127.0.0.1");
panel.inputText("Host", "host_input", "127.0.0.1", onChange);
panel.inputText("Host", "host_input", "127.0.0.1", {
hint: "Input host",
submitEnter: true,
onChange(text, state) {},
onSubmit(text, state) {}
});
options 支持字段
labelhintdefaultValuepasswordmultilinedecimalreadOnlysubmitEnteronChangeonSubmit
特别注意
- 行内函数参数位只会被当作
onChange。 onSubmit只能从options.onSubmit里传。multiline、password、decimal、readOnly、submitEnter底层是 flag 组合。
写进 state 的值
string
例子
panel.inputText("Host", "host_input", "127.0.0.1", {
hint: "Input host",
submitEnter: true,
onChange(text, state) {
log(`change=${text}`);
},
onSubmit(text, state) {
log(`submit=${text}`);
}
});
builder.inputInt(label, ...)
整数输入框。
默认值
| 字段 | 默认值 |
|---|---|
defaultValue | 0 |
step | 1 |
stepFast | 100 |
options 支持字段
labeldefaultValuestepstepFastonChange
写进 state 的值
- 整数
例子
panel.inputInt("Retry Count", "retry_count", 3, {
step: 1,
stepFast: 10,
onChange(value, state) {
log(value);
}
});
builder.inputFloat(label, ...)
浮点输入框。
默认值
| 字段 | 默认值 |
|---|---|
defaultValue | 0 |
step | 0 |
stepFast | 0 |
说明
- 如果你想有明显步进感,建议显式传
step/stepFast。
例子
panel.inputFloat("Alpha", "alpha_value", 0.8, {
step: 0.1,
stepFast: 0.5,
onChange(value, state) {
log(value);
}
});
builder.sliderInt(label, ...)
整数滑块。
参数规则
minValuemaxValuedefaultValue?callback?或options?
重要细节
- 如果你把最小值和最大值写反了,源码会自动归一化。
例子
panel.sliderInt("Level", "level_key", 0, 100, 20, (value, state) => {
log(value);
});
builder.sliderFloat(label, ...)
浮点滑块。
默认范围
- 如果你真的没传范围,默认是
0 ~ 1
例子
panel.sliderFloat("Scale", "scale_key", 0.5, 2.0, 1.1, (value, state) => {
log(value);
});
builder.sliderAngle(label, ...)
角度滑块。
默认范围
- 默认
-360 ~ 360 - 默认值是
0
options 特有字段
minDegreesmaxDegreesdefaultValueonChange
写进 state 的值
- 浮点数,单位是“度”
例子
panel.sliderAngle("Rotate", "angle_key", -180, 180, 0, (value, state) => {
log(value);
});
builder.dragInt(label, ...)
整数拖拽控件。
重要细节
- 这一组的
minValue和maxValue来自位置参数。 - 当前实现里,
options并不会重新覆盖minValue/maxValue。 options主要用来补label、defaultValue、speed、onChange。
默认值
| 字段 | 默认值 |
|---|---|
minValue | 0 |
maxValue | 1 |
speed | 1 |
例子
panel.dragInt("Offset X", "offset_x", -100, 100, 0, 1, (value, state) => {
log(value);
});
builder.dragFloat(label, ...)
浮点拖拽控件。
默认值
| 字段 | 默认值 |
|---|---|
minValue | 0 |
maxValue | 1 |
speed | 0.01 |
例子
panel.dragFloat("Opacity", "opacity_drag", 0, 1, 0.7, 0.01, (value, state) => {
log(value);
});
builder.combo(label, ...)
下拉单选框。
必要条件
- 选项数组不能为空,否则会直接抛错。
写进 state 的值
- 当前选中项索引
例子
const items = ["main", "push", "tool"];
panel.combo("Process", "process_index", items, 0, (index, state) => {
log(`index=${index}`);
log(`label=${items[index]}`);
});
builder.listBox(label, ...)
列表框单选控件。
附加字段
visibleItems
默认值
visibleItems默认4
例子
const langs = ["zh-CN", "en-US", "ja-JP", "ko-KR"];
panel.listBox("Language", "lang_index", langs, 0, {
visibleItems: 4,
onChange(index, state) {
log(langs[index]);
}
});
builder.radioGroup(label, ...)
单选组。
写进 state 的值
- 当前选中项索引
例子
const modes = ["Off", "Lite", "Full"];
panel.radioGroup("Mode", "mode_index", modes, 1, (index, state) => {
log(modes[index]);
});
builder.colorEdit(label, ...)
颜色编辑器。
可接受的默认颜色写法
#RGB#ARGB#RRGGBB#AARRGGBB0xRRGGBB0xAARRGGBB[r, g, b][r, g, b, a]
数组既支持:
0 ~ 1浮点0 ~ 255数值
options 支持字段
labeldefaultValuealphaalphaBarinputspickerHueWheelhueWheelonChange
很关键的一个点
alpha: true时,状态值更常见是#AARRGGBBalpha: false时,状态值更常见是#RRGGBB
回调签名
(hex, rgba, state) => {}
例子
panel.colorEdit("Accent", "accent_color", "#FF46BD87", {
alpha: true,
alphaBar: true,
inputs: true,
pickerHueWheel: true,
onChange(hex, rgba, state) {
log(hex);
log(JSON.stringify(Array.from(rgba)));
}
});
builder.colorPicker(label, ...)
颜色选择器。
行为
- 参数和
colorEdit()基本同一套 - 只是 UI 形态不同
例子
panel.colorPicker("Background", "bg_color", [255, 32, 32, 220], {
alpha: true,
onChange(hex, rgba, state) {
log(`hex=${hex}`);
}
});
builder.progress(...) / builder.progressBar(...)
显示一个进度条。
最常见写法
panel.progress(0.68, "68%");
panel.progressBar(0.9, "sync");
数值范围
- 内部会限制到
0 ~ 1
一个容易漏掉的点
这组控件不是纯死数据显示。当前实现允许你显式绑一个状态 key,让进度条优先读 state[key]。
状态绑定写法
源码当前复用了“前两个字符串视为显式 key 形态”的解析规则。由于进度条本身没有独立 label,这种写法里第一个字符串更像占位说明,真正显示出来的文本还是靠 overlay:
panel.progress("download", "download_progress", 0.35, "下载 {percent}");
panel.set("download_progress", 0.8);
overlay 的小细节
- 如果
overlay里带{percent},渲染时会自动替换成百分比。 - 如果你绑了
key,但overlay传空字符串,会自动显示当前百分比。
更实用的例子
panel
.progress("download", "download_progress", 0.15, "下载 {percent}")
.button("推进进度", state => {
const current = panel.get("download_progress", 0.15);
panel.set("download_progress", Math.min(current + 0.1, 1));
});
builder.plotLines(label, values, options?)
绘制折线图。
必要条件
label不能为空values必须是非空数字数组,否则会直接抛错
options 支持字段
labeloverlayminScalemaxScaleheight
默认值
height默认96minScale/maxScale不传时按自动缩放处理
例子
panel.plotLines("CPU", [0.1, 0.4, 0.2, 0.8, 0.5], {
overlay: "avg",
minScale: 0,
maxScale: 1,
height: 96
});
builder.plotHistogram(label, values, options?)
绘制柱状图。
规则
- 参数和
plotLines()基本同一套
例子
panel.plotHistogram("FPS", [58, 60, 61, 57, 62], {
overlay: "fps",
minScale: 0,
maxScale: 90,
height: 100
});
最后给一个比较常用、也比较像真实调试面板的组合例子:
const panel = imgui.window("network-panel", {
title: "Network Debug",
width: 400,
height: 620,
x: 24,
y: 96,
theme: "rose_glass",
windowCollapsible: true,
resizable: true
});
const processes = ["main", "push", "tool"];
panel.clear();
panel
.text(`pkg=${lpparam.packageName}`)
.text(`proc=${lpparam.processName}`)
.separatorText("Request")
.checkbox("Enable Hook", "enable_hook", true)
.inputText("Base URL", "base_url", "https://httpbin.org", {
hint: "Input request base url"
})
.combo("Process", "process_index", processes, 0)
.sliderFloat("Alpha", "alpha_value", 0.2, 1.0, 0.92, value => {
panel.alpha(value);
})
.colorEdit("Accent", "accent_color", "#FF46BD87", {
alpha: true
})
.progress("sync", "sync_progress", 0.25, "同步 {percent}")
.button("Advance", state => {
panel.set(
"sync_progress",
Math.min(panel.get("sync_progress", 0.25) + 0.1, 1)
);
})
.sameLine(12)
.button("Reset", state => {
panel.set("enable_hook", true);
panel.set("base_url", "https://httpbin.org");
panel.set("process_index", 0);
panel.set("accent_color", "#FF46BD87");
panel.set("sync_progress", 0.25);
})
.separatorText("State Snapshot")
.button("Dump State", state => {
log(JSON.stringify(panel.state(), null, 2));
})
.show();
