HOOZi文档
Skip to content

gui

菜单 UI + 配置写入(唯一写路径)。容器创建后保存返回的 对象 引用,通过其方法挂载子元素。


顶层创建 + 查找

gui.tab(name) → Tab

参数name : string —— 同时作为 id 和 label。

gui.sub_tab(parent, id, label) → Tab

参数parent : Tab, id : string, label : string

gui.window(id, title, w, h, flags?) → Window

参数id : string, title : string, w : number, h : number, flags? : int = 0

flagsgui.WINDOW.* 按位 OR / 直接相加组合。常用:

常量作用
NoTitleBar无标题栏
NoResize禁止拖角缩放
NoMove禁止拖动
NoScrollbar / NoScrollWithMouse无滚动条 / 禁滚轮滚动
NoCollapse不可折叠
NoBackground透明(无底/无边框线)
AlwaysAutoResize按内容自适应大小
NoSavedSettings不记忆位置/大小
NoMouseInputs / NoInputs鼠标穿透 / 完全不接收输入
NoFocusOnAppearing / NoBringToFrontOnFocus出现/点击不抢焦点
NoDecoration= NoTitleBar+NoResize+NoScrollbar+NoCollapse(无边框 HUD 常用)

无边框可拖 HUD:gui.window("hud", "", 250, 112, gui.WINDOW.NoDecoration + gui.WINDOW.NoFocusOnAppearing)。 窗口内自绘进度条/图形用 Window:custom(id, proto, h)on_render(self,x,y,w,h)(窗口内容区屏幕坐标,随窗口移动);纯展示 custom(无鼠标 hook)不吃输入,无边框窗口可直接拖动 body 定位

gui.find(path) → GUIElement | nil

参数path : string —— full_path(点号分隔,如 "aimbot.master"

返回:派生类型 对象(Checkbox / Slider / Combobox / Window / Groupbox / Tab / …)。nil 表示路径不存在。

gui.children(path) → table<GUIElement>

参数path : string —— 容器 full_path(Tab / Groupbox / Window / SettingsPopup)

返回:该容器直接子控件数组(类型透明,每项可直接 :get()/:set()/:set_hint())。非容器或路径不存在返回空表 {}。遍历某容器下控件(如做联动显隐)时用。

gui.get(path, fallback?) → value | fallback

参数path : string, fallback? : any = nil

内部 = gui.find(path):get(),带 nil 检查。

gui.set(path, value)

参数path : string, value : any

内部 = gui.find(path):set(value)

gui.show(paths) / gui.hide(paths)

参数paths : table<string> —— 控件 full_path 列表。

gui.set_enabled(paths, state) / gui.set_visible(paths, state)

参数paths : table<string>, state : bool


主菜单状态

gui.is_visible() → bool

菜单当前是否打开(同步查询,跟 event.on("menu_toggled", fn) 等价但无需自维护 bool)。

gui.list_hotkeys() → table<GUIElement>

返实际绑了热键的全部 Checkbox + KeybindControl (vk = 0 的未绑控件不返)。顺序按内部插入序,跟 menu 显示顺序相同。

返的元素是类型透明的 (Checkbox / KeybindControl userdata),可直接调子类方法:

  • :get_full_path() → string — 用于 input.is_active(path) 查当前激活态
  • :get_label() → string — 显示用 label
  • :get() → bool — checkbox 当前值

input.format(path) 拿 "Ctrl+F1" 这种显示字符串。

lua
-- 自定义热键 HUD:列当前所有热键控件 + 激活态 + 显示键
for _, elem in ipairs(gui.list_hotkeys()) do
    local path = elem:get_full_path()
    local key  = input.format(path)             -- "Ctrl+F1"
    local on   = input.is_active(path)          -- 当前激活?
    log.info((on and "[ON]  " or "[off] ") .. elem:get_label() .. "  " .. key)
end

跟 fatality gui.GetHotkeyList() 对齐。


gui.get_main_window() → table { x, y, w, h, visible }

主菜单 ImGui 窗口的屏幕位置 + 大小 + 可见性。

字段说明
x, y屏幕左上角坐标(像素,跟 draw.* 同坐标系)
w, h当前显示大小(含淡入淡出动画过程的实时大小)
visible等价 gui.is_visible()

菜单完全关闭时 x/y/w/h 保留上次值,visible=false。菜单可拖动,x/y 会跟用户拖动更新(如拖到副屏)。

lua
-- 附加浮窗跟随主菜单右侧
local win = gui.window("dock", "工具栏", 200, 400)
event.on("frame_update", function()
    local m = gui.get_main_window()
    if m.visible then
        win:set_pos(m.x + m.w + 10, m.y)
        win:show()
    else
        win:hide()
    end
end)

-- 只在菜单关时画 ESP(避免菜单遮挡)
event.on("frame_update", function()
    if gui.is_visible() then return end
    draw.text(...)
end)

Scenario-aware paths(自动链接图标)

任何 menu 控件的 full_path 中段含 .hidden. / .visible. / .knocked. / .teammate. 之一,引擎自动识别为 ESP scenario 字段,控件左侧画上链接图标(⛓️ linked / ⛓️‍💥 unlinked),切换时把当前值广播给其他 3 个 scenario 路径。链接状态持久化进 cfg.visual.esp_linked_fields,跨 plan 保存。

示例

lua
local tab = gui.tab("MyESP")
local scenarios = {"hidden", "visible", "knocked", "teammate"}
for _, sc in ipairs(scenarios) do
    local g = tab:group(sc, sc, 0, 0, 0, 0)
    g:color_edit("tint", "Tint", 1, 0, 0, 1)
    g:slider_float("radius", "Radius", 1, 20, 5)
end
-- 4 个 "MyESP.<sc>.tint" / "MyESP.<sc>.radius" 自动获得链接图标
-- 点 ⛓️‍💥 → ⛓️ 时把当前 scenario 的值同步到其他 3 个路径

读 scenario 值的两种姿态

lua
-- (A) ESP 绘制回调里:ctx.scenario 是引擎按"当前玩家"算好的(per-player)
esp.add({
    id = "my_module",
    fn = function(ctx)
        local sc_name = ({[0]="hidden",[1]="visible",[2]="knocked",[3]="teammate"})[ctx.scenario]
        local tint = gui.get("MyESP." .. sc_name .. ".tint")
        return { text = "x", text_color = tint }
    end,
})

-- (B) 非绘制上下文:脚本明示要读哪个 scenario
local visible_tint = gui.get("MyESP.visible.tint")

不存在全局"当前 scenario" —— ESP 4 个 scenario 是对每个玩家的状态分类,N 个玩家可同时处于不同 scenario。esp.scenario 是菜单预览用的索引,不是任何玩家的 scenario,绘制逻辑请用 ctx.scenario 而非 esp.scenario

限制:Lua 控件的链接传播只在用户点链接图标"切到链接"那一刻执行一次广播;之后改源值不会自动同步给其他 3 个路径(real-time per-frame propagation TODO)。C++ BIND_SCENARIO_FIELD 绑定的控件没有这个限制(每次写入都走 post_write 自动广播)。


武器配置读写

武器配置统一通过 gui.set / gui.get 访问,有两种路径,编辑同一份 WeaponConfig

路径形态编辑哪把武器用例
菜单路径aimbot.legit.fov.dead菜单当前选中武器(跟 UI 一致)跟 UI 交互一致,开"显示组件路径"复制即用
存储路径aimbot.weapon.<id>.aim.fov_dead指定 <id> 那把(无视 UI 选中)批量改多把武器、跑预设、当前未存活也能写

菜单 tooltip:开"显示组件路径"后,per-weapon 控件 hover 时显示 Storage: aimbot.weapon.<id>.<field> + Menu: aimbot.legit.<...>,右键复制存储路径。batch(多选)模式额外显示 (batch: N weapons)。全局控件(非 per-weapon)只显单行 menu 路径。

菜单路径(推荐日常用)

lua
gui.set("aimbot.legit.fov.dead",  0.5)   -- 当前选中武器:死区
gui.set("aimbot.legit.fov.hip",   12.0)  -- 当前选中武器:腰射 FOV
gui.set("aimbot.legit.smoothadv.kp", 1.8)

菜单选了什么武器,写到那把。default 选中时改 default;r301 选中时改 r301。

两个注意点

  • batch 模式只改第一把:UI 多选时 Lua 走菜单路径只写 selected_weapon_ids 的第一把,不像手拖 slider 那样真批量。要批量改请改用存储路径加循环。
  • @weapon_id 后缀强制指定aimbot.legit.fov.dead@r301 绕过 UI 选中,临时把这一行的 groupbox bind 到 r301。一次性写多把武器时这个比循环存储路径更接近 UI 心智模型。

存储路径(指定武器)

格式:aimbot.weapon.<weapon_id>.<sub_path>

  • <weapon_id>:字符串武器 ID,如 "r301" / "flatline""default" 是 baseline
  • <sub_path>:WeaponConfig 字段(嵌套 dot + 数组 1-based [n]
lua
-- 指定武器:写死 ID(不受 UI 选中影响)
gui.set("aimbot.weapon.r301.aim.fov_hip", 12.5)
gui.set("aimbot.weapon.flatline.aim.fov_hip", 12.5)
gui.set("aimbot.weapon.default.filter_tags[3]", true)

-- 嵌套字段 / 数组
gui.set("aimbot.weapon.r301.aim.pid.kp", 1.8)
gui.get("aimbot.weapon.r301.aim.max_dist_range[2]")

字段名跟 C++ 端 SCHEMA 同步,添 / 改 / 删字段后这里自动跟,wiki 不列字段穷举。

写入失败语义:以下三种情况都 silently no-op,不抛 Lua 错、不发布 config 快照:

  • 武器 ID 未加进 armory(如 aimbot.weapon.unknown.aim.fov_hip)—— 不会静默 fallback 到 default
  • 字段不存在(typo,如 aimbot.weapon.r301.aim.fov_hipx
  • 数组下标越界 / 类型错(如把 string 写进 float 字段)

读取(gui.get)同样:上述情况返 nil(或 fallback 参数)。

gui.get_active_override_path() → string

返当前手持武器实际生效的配置存储路径前缀。语义跟 aim 线程内部解析 (resolve_active_weapon) 完全对齐:

当前状态返回
手持武器在编组 + 编组在 armory"aimbot.weapon.<group_id>"
手持武器独立 + 在 armory"aimbot.weapon.<weapon_id>"
手持武器未启用武器库(不在 armory)"aimbot.weapon.default"
ClientData snapshot 不可用(DMA 未连 / 加载中)""
lua
local p = gui.get_active_override_path()
if p ~= "" then
    gui.set(p .. ".aim.bone_target", 2)  -- 改当前生效配置
end

跟"菜单选中"不同:取的是游戏内手持的武器(lp.weapon_enum 经 enum_to_weapon_id 查表 + armory 校验),菜单里选 default 不影响。空串只在玩家死亡 / 加载中等 client_data 不可用时出现——其它任何活着的状态都至少能返 aimbot.weapon.default

gui.get_selected_weapon() → string

返回武器库当前选中配置的武器 id(不是手持武器;手持武器用 gui.get_active_override_path())。配 aimbot.weapon.<wid>.lua.<id> 寻址 per-weapon Lua 值用。未选时返回 "default"

lua
local wid = gui.get_selected_weapon()
gui.set("aimbot.weapon." .. wid .. ".lua.my_gain", 1.5)

通知(gui.notify)

gui.notify 是子表,按 : 调用:

gui.notify:info(text, dur?) / :success / :warn / :error

参数text : string, dur? : number = 4.0(持续秒)

lua
gui.notify:info("hello")
gui.notify:success("已应用预设", 2.5)
gui.notify:warn("距离过远", 1.5)
gui.notify:error("HTTP 失败:" .. err, 3.0)
level适合
info普通操作反馈
success绿正向确认
warn软警告
error错误告警

容器方法(Tab / Window / Groupbox 共享)

:group(id, label, x, y, w, h) → Groupbox

参数id : string, label : string, x/y/w/h : number

:button(id, label, callback?) → Button

参数id : string, label : string, callback? : function()

:checkbox(id, label, default) → Checkbox

参数id : string, label : string, default : bool

:slider(id, label, min_v, max_v, def) → Slider (整型)

参数id : string, label : string, min_v / max_v / def : int

:slider_float(id, label, min_v, max_v, def) → SliderFloat

参数id : string, label : string, min_v / max_v / def : number

:range_slider_float(id, label, min_v, max_v, def_v1, def_v2, min_range?) → RangeSliderFloat

参数:5 个 number + min_range? : number = 0

:range_slider_int(id, label, min_v, max_v, def_v1, def_v2, min_range?) → RangeSliderInt

参数:5 个 int + min_range? : int = 0

参数id : string, label : string, items : table<string>, def : int (1-based 默认索引;binding 层 def-1 转 0-based 内部存储。gui.get/set 直接读写内部字段所以是 0-based int)

(usertype 名仍叫 Combobox——历史命名,控件方法节用这个名字。多选版本是 :multi_dropdown。)

:input_text(id, label, def) → InputText

参数id : string, label : string, def : string

:color_edit(id, label, r, g, b, a?) → ColorEdit

参数id : string, label : string, r / g / b : number, a? : number = 1.0

:multi_dropdown(id, label, items, defaults?) → MultiDropdown

参数id : string, label : string, items : table, defaults? : table<bool>

:keybind_control(id, label, def_key?, def_mode?) → KeybindControl

参数id : string, label : string, def_key? : int = 0, def_mode? : int = 0

:live_table(id, headers, provider, height?) (Window / Groupbox only)

参数id : string, headers : table<string>, provider : function() → table<row>, height? : number = 0

row 形态:{cell1, cell2, ...} 数组 或 {cells = {...}, color = {r,g,b,a}}

:tips(id, text, icon?, color?) (Window / Groupbox / SettingsPopup)

参数id : string, text : string, icon? : string, color? : table<r,g,b,a>

:settings(id, label, icon?) → SettingsPopup

参数id : string, label : string(齿轮左侧文字,可空), icon? : string(齿轮图标,默认 ⚙)

齿轮折叠容器(对标 fatality gui.Settings):行上 label ⚙,点齿轮弹 popup,子控件画在 popup 里。详见下方「设置齿轮容器」。


自定义控件 (LuaControlProto 风格)

container:custom(id, proto, height?) → CustomControl

参数:

  • id : string — 控件 id
  • proto : table — 控件原型,持有 on_* lifecycle callback 跟 initial_data
  • height? : number = 24 — 控件高度像素

返回 CustomControl userdata。允许同一个 proto 实例化多个控件,initial_data 自动深拷贝避免共享。

proto 表里所有 lifecycle 字段都可选:

字段签名触发时机
initial_datatable实例 data 初始值,深拷贝到 self.data
on_renderfn(self, x, y, w, h)每帧绘制,box 是 (x, y) → (x+w, y+h) 屏幕坐标
on_first_renderfn(self)首帧 (data 从持久化加载完后调)
on_mouse_downfn(self, btn)鼠标按下 (0=left, 1=right, 2=middle, 3=x1, 4=x2),hover 时触发
on_mouse_upfn(self, btn)鼠标松开 (不要求 hover)
on_mouse_movefn(self, dx, dy)hover 时鼠标坐标 (相对控件左上角)
on_focusfn(self)控件 active (持有鼠标) 边沿
on_blurfn(self)active 失去边沿
on_keyfn(self, imgui_key)hover 时键盘按下边沿 (no-repeat)
on_resetfn(self)self:reset() 主动调时

CustomControl 实例方法

方法说明
self.data : table实例数据,read/write。整体替换或 self.data.foo = x 都 OK
self:lock_input()标记输入锁定 (持有鼠标时 ImGui 自动锁,这里只是 hint)
self:unlock_input()释放锁
self:is_hovered() → bool上一帧鼠标是否 hover 在控件内
self:is_focused() → bool上一帧是否持有鼠标 (active)
self:is_input_locked() → boollock_input 状态查询
self:get_height() → number / :set_height(h)控件高度
self:reset()触发 on_reset callback

持久化

self.data 自动保存到 cfg.lua.<full_path> (跟 lua-created checkbox 同套机制)。脚本 reload / plan 切换都恢复上次 data。 on_first_render 在 data 加载完后调,允许用持久化数据初始化。

lua
-- Counter — 点击 +1 右键 -1,关菜单重开 count 不丢
local Counter = {
    initial_data = { count = 0 },
    on_render = function(self, x, y, w, h)
        local bg = self:is_hovered() and draw.rgba(0.25, 0.30, 0.40, 1)
                                       or draw.rgba(0.15, 0.18, 0.22, 1)
        draw.rect(x, y, x+w, y+h, bg, { filled = true, rounding = 3 })
        draw.text(x + 8, y + 8, draw.rgba(1, 1, 1, 1),
                  "Count: " .. tostring(self.data.count), { outline = true })
    end,
    on_mouse_down = function(self, btn)
        if btn == 0 then self.data.count = self.data.count + 1
        elseif btn == 1 then self.data.count = self.data.count - 1 end
    end,
}

local grp = gui.tab("misc"):group("g", "demo", 0, 0, 350, 0)
grp:custom("counter1", Counter, 28)

完整 demo 见 out/Debug/scripts/custom_control_demo.lua (Counter / Pulse / Bar 3 例)。


设置齿轮容器 (SettingsPopup)

container:settings(id, label, icon?) → SettingsPopup 创建一个齿轮折叠容器(对标 fatality gui.Settings / cogwheel):行上显示 label ⚙,点齿轮弹 popup,子控件画在 popup 里。齿轮 + popup 全由 C++ 框架绘制,Lua 只负责创建 + 加子控件。

适合把"次要 / 高级设置"折叠进齿轮——主菜单只留一行,不用一堆参数铺开,也省去按选择逐帧 set_visible 显隐。

子控件工厂(与 Groupbox 同套;子控件的持久化 / 热键 / per-weapon 写入全自动): button / checkbox / slider / slider_float / dropdown / input_text / color_edit / range_slider_float / range_slider_int / multi_dropdown / keybind_control / tips / custom

实例方法

方法说明
s:add_child(elem) / s:remove_child(elem) / s:clear_children() / s:remove_children_by_owner(path)子控件增删(同其它容器, 但 remove_child / clear_children 返回 self 可链式)
s:set_gear_icon(icon) → self换齿轮图标
s:set_popup_width(px) → selfpopup 逻辑宽度(默认 260)
lua
local grp = gui.tab("misc"):group("g", "demo", 0, 0, 350, 0)
local adv = grp:settings("adv", "高级设置")            -- 行上 "高级设置 ⚙"
adv:slider("speed", "速度", 0, 100, 50)
adv:slider_float("ratio", "比例", 0.0, 1.0, 0.5):set_format("%.2f")
adv:checkbox("smooth", "平滑", true)
adv:set_popup_width(220)

容器通用方法

Tab / Groupbox / Window 共有的子节点操作。多数脚本不用碰 —— 控件创建用 :checkbox / :slider / :group 等更简洁;这套低层 API 给动态增删场景(hot-reload 自维护、运行时按条件加控件)。

:add_child(elem)

手动挂一个 GUI element, 无返回值。

:remove_child(elem)

删一个直接子节点, 传元素对象(不是 id 字符串), 无返回值。

:remove_children_by_owner(script_path)

删除该 owner 创建的全部子节点, 无返回值。脚本卸载会自动跑这条, 手动调通常不必要。

:clear_children()

清空所有子节点。

Tab:is_lua_tab() → bool

是否是 Lua 脚本通过 gui.tab(...) 创建的(区分 cpp 内置 tab)。脚本想避开内置 tab 时用。


Window 独有方法

Window:is_open() → bool / Window:set_open(bool)

Window:bind_visible(checkbox) → Window

参数checkbox : Checkbox —— 引用任一 Checkbox 对象(来自 :checkbox(...) 返回值)

每帧自动同步 Window.open = checkbox.value,省去手写 live_table provider 里 set_open 的麻烦。


Checkbox / Slider / Combobox 等控件方法

Checkbox / Slider / SliderFloat 多热键

2026-05-25 UX 重设计:每个 Checkbox / Slider 都支持多条热键,右键控件打开 popup 管理。Switch / Slider 右上角显示首条热键 + 计数 (Ctrl+F1 +2)。Popup 内每条热键含 [主键][模式][🗑],Slider 多一个 [⚙] 设触发时的独立 value (拨回该值)。

lua
-- Checkbox 多热键: 任一激活 → checkbox = true
local cb = group:checkbox("aim", "Aimbot", false)
cb:add_hotkey({ vk = 0x70, mode = 1 })                          -- F1 Hold
cb:add_hotkey({ vk = 0x71, mode = 0, modifier = 0x11 })          -- Ctrl+F2 Toggle
cb:clear_hotkeys()                                                -- 清空

-- Slider 多热键 + 独立 value: 触发 F3 把 slider 写为 50
local sl = group:slider("fov", "FOV", 0, 100, 30)
sl:add_hotkey({ vk = 0x72, mode = 0, value = 50 })               -- F3 Toggle → write 50
sl:add_hotkey({ vk = 0x73, mode = 0, value = 80 })               -- F4 Toggle → write 80

热键模式:0=Toggle / 1=Hold / 2=Always。Modifier 用 0x10=Shift / 0x11=Ctrl / 0x12=Alt。

Checkbox:set_keybind(vk, mode, modifier?)

参数

  • vk : int — 主键虚拟键码
  • mode : int — 0=Toggle / 1=Hold / 2=Always
  • modifier : int? — 可选修饰键,VK_CONTROL (0x11) / VK_SHIFT (0x10) / VK_MENU (0x12,即 Alt);不传或 0 = 无修饰键

示例

lua
cb:set_keybind(0x70, 0)             -- F1 单键,Toggle
cb:set_keybind(0x70, 1, 0x11)       -- Ctrl + F1,Hold

Checkbox:set_colors(table) / Checkbox:get_colors() → table

为带 keybind 状态的 checkbox 配色(当前/inactive/hover/...),table 见源码。

:set_hint(text) → self

有此方法的控件:Checkbox / Slider / SliderFloat / RangeSliderFloat / RangeSliderInt / InputText / KeybindControl。Combobox / MultiDropdown / ColorEdit / Button 没有 :set_hint

:set_width(px) → self / :get_width() → number

参数px : number —— 控件像素宽;0 或省略 = 自动吃满整行。

GUIElement 基类方法,所有控件可调;实际生效的是 Slider / SliderFloat / Combobox(其余控件存而不用)。grp:slider("id","label",0,100,5):set_width(120)

:set_per_weapon(enable?) → self / :is_per_weapon() → bool

参数enable? : bool = true

让控件值按"武器库当前选中武器"分别存储(仅对 Lua 控件生效)。开启后切换武器库选中的武器,控件自动显示那把武器的值;未单独设过的武器回退到 default 武器的值。链式置于末尾(返回基类,后面只接 :set_visible 等基类方法)。

值存在 aimbot.weapon.<wid>.lua.<控件id>,脚本可经此路径 gui.get/set 点名任意武器读写;切换武器时控件自动显示选中武器的值,未单独设过的武器回退 default 武器的值。

仅值类控件(checkbox / slider / slider_float / combobox / input_text)生效;带 keybind 或 colors 的控件不支持 per-weapon。同一寻址下控件 id 须唯一。

lua
local grp = gui.find("aimbot.legit.smoothadv")
grp:slider_float("lua_gain", "Lua 增益", 0, 5, 1):set_format("%.2f"):set_per_weapon(true)

Slider:set_format(fmt) → Slider / SliderFloat:set_format / RangeSliderFloat:set_format / RangeSliderInt:set_format

Combobox:on_change(fn) → Combobox

参数fn : function(idx_1based : int, name : string) —— 1-based 索引 + 选中项字符串

Combobox:add_item(item) → Combobox / add_items(items) / remove_item(idx_or_name) / clear_items()

Combobox:get_selected_name() → string / get_items() → table<string>

InputText:set_size(vec2)

Button:set_size(vec2) / set_icon(string)

ColorEdit:set_colors(table) / set_alpha(bool)

MultiDropdown:add_item(item, default?) / add_items(items) / remove_item(idx_or_name) / clear_items() / get_selected_names() → table<string>


GUIElement 基类方法(所有控件继承)

:get_id() → string / :get_owner() → string / :get_label() → string / :set_label(str)

:is_enabled() → bool / :set_enabled(bool) / :is_visible() → bool / :set_visible(bool)

:get() → value / :set(value) / :has_value() → bool

路径访问该控件当前值的统一接口(与 gui.get / gui.set 等价)。

:bind_weapon(...) / :get_target_weapon(...)

控件级武器绑定(高级用户)。


示例

lua
local tab = gui.tab("MyTab")
local g   = tab:group("g", "我的分组", 0, 0, 0, 0)

local cb = g:checkbox("enable", "启用", false)
            :set_hint("勾上后开启功能")

local sl = g:slider_float("scale", "Scale", 0.0, 5.0, 1.0)
            :set_format("%.2fx")

local cb_theme = g:dropdown("theme", "主题",
    { "Default", "Dark", "Light" }, 1)
    :on_change(function(idx_1based, name)
        log.info("主题切到:" .. name)
    end)

g:button("reset", "复位", function()
    gui.set("MyTab.g.enable", false)
    gui.set("MyTab.g.scale",  1.0)
end)