Skip to content

modoptions: 按设置类型区分开关 / 下拉 / 滑条(修范围项显示下拉、两项 enum 显示开关) - #7

Merged
Dilant merged 7 commits into
std-microblock:masterfrom
std-external:feat/modoptions-option-controls
Oct 24, 2026
Merged

Dilant merged 7 commits into
std-microblock:masterfrom
std-external:feat/modoptions-option-controls

Conversation

@std-external

@std-external std-external commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

问题

模组设置页的控件是按 选项数量 猜的:

  • option.Options.Count == 2 → 开关
  • option.Options.Count >= 3 → 下拉

但 Everest 的类型映射不是这样:

  • bool → TextMenu.OnOff(Option<bool>,2 项)
  • int + [SettingRange] → TextMenu.Slider(每格一项,可能是 3+ 项)
  • enum → TextMenu.Slider(包括只有 2 个值的 enum)
  • TextMenuExt.IntSlider → 大范围整数滑条

结果:范围数值(≥3 项)被画成下拉;只有两个值的 enum 被画成开关。

类型 → 控件对照

设置 / 菜单项 判据 控件
TextMenu.OnOff / Option<bool> 值类型是 bool 开关
TextMenu.Slider 且闭包指向 int/float 属性 数值范围 滑条
TextMenu.Slider 且闭包指向 enum 属性 / enum 数组 enum 下拉
TextMenu.Slider,看不到闭包属性时 项值连续 + 标签是数字/数字前缀 连续数值 → 滑条;否则下拉
Option<int>(手写、非 Slider) 项值连续 + 标签数字 滑条;否则下拉
Option<string> / 其它 Option<T> 不是 bool/int 下拉(包括只有 2 项)
TextMenuExt.IntSlider 显式大范围滑条 滑条(沿用原逻辑)
TextMenu.Button / TextMenu.Setting 无范围/绑定 动作「打开」,不变
TextMenuExt.SubMenu / OptionSubMenu 组合菜单 复合弹层,不变
string 设置 由 Everest 生成 Button 见 #5(本 PR 不动 string)
float / [SettingNumberInput] Everest 生成 Button → OuiNumberEntry 仍是动作「打开」;没有 min/max/step 元数据,不能安全画滑条
int 无 [SettingRange] Everest 不自动生成条目;mod 若用手写 Button 动作,不变

enum 无论 2 项还是多项都走下拉;只有真正 bool 才是开关。

滑条手感(键盘步长与保存节流)

  • 鼠标 / 触屏拖动:按下开始 drag,之后按指针 x 坐标持续更新(指针移出轨道也 clamp 到两端),松开结束;拖动过程中只改内存值,松开时统一 Everest.SaveSettings() 一次,不会每帧写盘。
  • 键盘 / 手柄方向键 / A、D:单按一次永远只移动 1 个最小步长(TextMenu.Slider 和 TextMenuExt.IntSlider 都是 1)。按住超过 0.45s 才开始加速,速率在 1s 内平滑升到 range/12(8~160 步/秒);A/D 和方向键、手柄共用同一套计时,轻推就是细调,长按才快。松开立即复位。
  • 展示:滑条上方保留精确当前值文本;加速只改变每帧步进量,不额外取整,值仍能落到每个整数。
  • 宽度:滑条和下拉共用同一个 OptionControlRect(400px、右边界 rect.Right - 424),滑条轨道在这个容器内缩进;控件宽度、右边界与下拉完全一致。

保存节流(保存图标问题)

  • 滑条拖动/键盘调整只标记 dirty,0.5s 内没有新的变化才写盘;拖动中或方向键按住期间暂停计时,不会每帧/每次松开都 Everest.SaveSettings()。
  • 同一时间只允许一个保存协程;Leave / 暂停菜单收尾会先等正在进行的保存结束(SaveSettingsAfterPending),避免页面被移除后托管协程中断、保存图标停在右下角。
  • 这次改动前是“松开一次就 Add 一个 SaveSettings 协程”;快速连拖会堆积协程,页面切走时可能截断,确实容易让图标卡住。现在改成单次 debounce + 离开前等待。

实现

  • OptionSnapshot 增加 OptionKind(Switch / Dropdown / Range),在 TryGetOption 里分类一次,渲染、确认、鼠标/触屏、下拉都只看这个 kind。
  • 分类优先看 OnValueChange 闭包里的 PropertyInfo(enum / int);闭包被编译器合并、同时抓到多个属性时退回“值连续 + 标签数字”的判断。手写菜单没有属性时走同一个回退。
  • 范围滑条沿用滑条渲染;新增 draggedSlider 状态和 HandleRangeKeyboard,鼠标拖动与键盘/手柄步进互不影响。触屏 slider 手势只给真正的 Range 发布。

改了哪些文件

文件 说明
Source/MaterialModOptions.cs OptionKind 分类;开关/下拉/滑条渲染与输入按 kind 选中;滑条拖动、宽度统一、键盘自适应步长与加速

测试包

验证

自动化已过

  • dotnet build Source/MicroblocksQolUtils.csproj:0 error / 0 warning
  • dotnet run --project Tests/Capture/Capture.csproj:PASS
  • 临时反射 harness(不提交)对 packaged Release DLL 验证:
    • 单按步长常量 = 1;键盘加速曲线在 0.45s 前为 0、之后单调上升、200~2000 范围封顶 150 步/s
    • 滑条/下拉 OptionControlRect 同宽同右边界;slider 轨道完整落在控件内
    • 拖动像素映射 0/25/50/75/100% → 0/25/50/75/100,超出轨道 clamp
    • SettingsSaveDebounce:0.4s 不触发、0.5s 触发一次;拖动/按住阻塞;保存中阻塞;保存结束后可再次触发;新的修改会重置计时
  • 这些覆盖纯逻辑/布局/状态机;游戏内实际绘制、输入焦点、保存图标是否真正消失需要实机确认。

实机手测清单

  1. 装 zip 到 Mods/,打开 Mod Options → 找一个 200~2000 的范围项:单按方向键只变 1;按住 0.45s 后平滑加速;A/D 也能轻推;当前值文本准确。
  2. 鼠标拖动滑条:值连续跟随,松手后约 0.5s 才出现一次保存图标;拖动过程中不出现;连续快速拖动后图标能消失。
  3. 同一页对比范围滑条与 enum 下拉:左右宽度/右边界一致。
  4. 两项 enum 是下拉不是开关;bool 仍是开关;3+ enum 下拉正常;TextMenuExt.IntSlider 正常。
  5. 触屏/鼠标拖动滑条不会误触发其它行;快速拖完立刻退出设置页,右下角不应残留保存图标。
  • 本地反射小测试(临时 harness,不提交)覆盖:int 属性 Slider → Range、enum 属性 Slider → Dropdown、手写 enum Slider → Dropdown、无闭包数字 Slider → Range、连续数字 Option<int> → Range、文字 Option<int> → Dropdown、Option<string> → Dropdown、OnOff → Switch。
  • 实机手工验证:
    1. 范围项(如 mbqol 的 200~2000):显示滑条;鼠标按住拖动时值持续跟随,松手后 0.5s 内没有新改动才保存一次;键盘方向键/A/D 单按只变 1,按住 0.45s 后平滑加速,松开复位;当前值文本准确。
    2. 保存图标:连续拖动/连按后停手,图标只出现一次并在保存结束后消失;拖动中途不出现;快速拖完立刻退出设置页也不应留下常驻图标。
    3. 宽度:同一页的范围滑条和下拉(enum)控件左右边界一致;切换不同 tab 不跑位。
    4. 两项 enum:显示下拉不是开关;bool:仍是开关;3+ 项 enum:下拉正常;TextMenuExt.IntSlider 正常。
    5. 触屏:拖动滑条能连续变化;点按 enum 不会误触 slider 手势。

Everest models booleans as Option<bool>, integer ranges as Slider and
enums as Slider too, so the old option-count heuristic rendered integer
ranges as dropdowns and two-value enums as switches.

Classify every option into Switch / Dropdown / Range once: bool options
are switches, enum-backed or non-numeric Slider/Option<int> values are
dropdowns, and consecutive numeric ranges become sliders. Range rows now
always use the slider control, dropdown rows open the list, and touch
slider targets are only published for real ranges.
- start a slider drag on press and keep updating from the pointer while the
  button is held (mouse and touch); save settings once on release instead
  of every step
- give range sliders and dropdowns the same 400px control box on the right
  edge, with the track inset inside it
- adapt keyboard steps to the range (about 50 presses end to end) and
  accelerate x2/x4/x8 after holding a direction for 0.4s/0.8s/1.2s
@std-external
std-external force-pushed the feat/modoptions-option-controls branch from 444c3f9 to 2ca4c46 Compare October 23, 2026 13:43
std-external and others added 4 commits October 23, 2026 21:59
…bounce

A single direction tap now always moves one minimum step. Holding starts a
smooth ramp after 0.45s up to range/12 steps per second (8..160), with A/D
as well as the arrow keys/gamepad. Slider changes mark settings dirty and
save 0.5s after the last change (paused while dragging or holding), so the
save icon appears once instead of once per release; leaving waits for any
pending save to finish.
@std-external
std-external force-pushed the feat/modoptions-option-controls branch from fbf5997 to 19633b5 Compare October 24, 2026 02:29
@Dilant
Dilant force-pushed the feat/modoptions-option-controls branch from 19633b5 to 7ab759a Compare October 24, 2026 05:43
@Dilant
Dilant merged commit ce45d0d into std-microblock:master Oct 24, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants