Skip to content

modoptions: string 设置项直接给文本框 + 游戏内文本输入层(并修复键位配置界面被盖住) - #5

Open
std-external wants to merge 1 commit into
std-microblock:masterfrom
std-external:feat/string-text-input
Open

std-external wants to merge 1 commit into
std-microblock:masterfrom
std-external:feat/string-text-input

Conversation

@std-external

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

Copy link
Copy Markdown
Contributor

动机

群友反馈:模组设置页里的字符串项(MiaoNet 的名称 / 头衔 / 颜色 / 头像链接等)只显示成一个通用「打开」按钮,值被塞在标题里,既不直观也没法像常规文本框那样编辑。

键位配置界面被设置页盖住、Esc 返回泛白的修复已拆到 #6,本 PR 只含 string 输入。两个 PR 都动了 MaterialModOptions.Update / UpdateInteractions,先合哪个都行,另一个我会 rebase。

改动

1. 字符串项变成常规文本框

  • 字符串项直接显示为行内文本框(当前值 / 「点击输入」占位 / 编辑图标),接受后写回属性并刷新标题
  • 键鼠:直接在行内编辑,走 SDL text input(TextInput.OnInput + TextInputEXT.TextEditing),和 vanilla 改名框同一条路径,支持系统输入法 / 非 ASCII
  • 触屏:走 CeleMod 的 TouchKind: "text" 触控目标拉起系统软键盘
  • 手柄:MInput.ControllerHasFocus 为 true 或 Everest UseKeyboardForTextInput 关闭时,进入自绘 QWERTY(方向键导航 + 确认)
  • 识别三类字符串入口:Everest 自动 string 设置(按命名规则反射)、手写菜单 mod(MiaoNet 这类 Func<string>/Action<string> 闭包)、以及兜底拦截 Overworld.Goto<OuiModOptionString>()

2. 光标与可见空格

  • 两种编辑器(行内 / QWERTY)都维护光标:← → 移动、Home/End 跳首尾、Delete 删光标后、Backspace 删光标前,插入也在光标处
  • QWERTY 界面里按 ↑ 从键盘进入文本光标区(←→ 移动、↓ 回键盘);鼠标点击文本任意位置可直接定位光标
  • 空格渲染为 ·,开头/结尾空格和光标位置都能看见;光标位置按字体实际宽度计算 —— 中文/全角字符算 2 格,不会出现半格错位
  • 鼠标点击按字符边界就近吸附,宽度同样用 SystemTtfFont.MeasureVisible 实测

3. 可复用输入层

Source/TextEntryOverlay.cs:手机式 QWERTY(qwertyuiop / asdfghjkl / zxcvbnm,shift 切大写,123 / #+= / ABC 明确切页,底部 取消/空格/退格/接受,长按空格连打,角标只是提示)。Source/TextEditBuffer.cs 抽出光标/可见空格/宽度窗口逻辑,供行内与 QWERTY 共用。

改了哪些文件

文件 说明
Source/TextEditBuffer.cs 新增:光标、空格标记、按字体宽度取窗口/点击定位
Source/TextEntryOverlay.cs 新增:游戏内 QWERTY 输入层(手柄用)
Source/MaterialModOptions.cs string 行内文本框、光标、输入源分流、系统输入/软键盘接入

验证

  • dotnet build Source/MicroblocksQolUtils.csproj:0 error / 0 warning(Everest stable lib-stripped)
  • dotnet run --project Tests/Capture/Capture.csproj:PASS
  • 本机 Linux 无法跑游戏实机截图;请按下面步骤在 Windows/Android 上验证。

手工验证步骤

  1. 光标:键鼠点开一个 string 项,用 ← → / Home / End 移动光标,在中间插入字符、Backspace 删前一个、Delete 删后一个;鼠标点文本中间光标应跳到最近的字符边界;中文/全角字符前后的光标不偏半格。手柄进 QWERTY 后按 ↑ 进光标区,←→ 移动、↓ 回键盘,确认删除/插入位置一致。
  2. 空格:输入 a (前后各两个空格),开头/结尾空格应显示成 ·,光标能停在每个空格之间;提交后重新打开值仍保留这些空格。
  3. 输入法:键鼠环境下在文本框里用系统输入法输入中文(或粘贴非 ASCII),确认能落进值里并正常保存;手柄的 QWERTY 只保证 ASCII(无系统 IME)。Esc 取消不改值、超长只截停新输入、空值占位保持原样。

… fields

String settings in the material mod options page are now edited like a
regular text box:

- keyboard mouse users edit inline through SDL text input (IME included);
  the self-drawn QWERTY prompt is kept for gamepad input
- both editors keep a caret, so left/right, Home/End and Delete work, and
  backspace removes the character before the caret
- mouse/touch clicks place the caret at the nearest character boundary;
  widths come from the TTF font, so CJK/full-width glyphs count twice
- spaces are drawn as a middle dot so leading/trailing spaces and the
  caret position stay visible

The old key/button config layering fix has moved to its own PR.
@std-external
std-external force-pushed the feat/string-text-input branch from 800d977 to b2b7cea Compare October 26, 2026 15:24
@std-external

Copy link
Copy Markdown
Contributor Author

已 rebase 到最新 master(7c55b36,含 #6 的配置界面层级修复和 #7 的 option 控件重构),force-with-lease 推回 fork,新 head:b2b7cea(历史仍是一个 commit)。

冲突与解决

7 处冲突全在 Source/MaterialModOptions.cs,都是「两边各加一块」或结构体/类型差异,没有需要取舍的逻辑:

位置 master 侧 本 PR 侧 处理
静态字段 optionMetadata、IntSlider min/max 反射字段 OuiModOptionString.OnValueChange / OnExit 反射字段 两段都保留
页面字段 configOverlayOpen、externalOverlays、hiddenOverlays stringSettings、textEntry、行内编辑状态 两段都保留
Update 早期返回 menu 为空 / configOverlayOpen 时 CancelSliderDrag 并交出输入 文本提示层 / 行内编辑时交出输入 保留 master 两个 guard,再接本 PR 两个 guard(config overlay → text entry → inline)
Update 确认键 bool rangeHandled = HandleRangeKeyboard(); string 项按确认键开编辑器 string 分支在前,之后仍是 master 的 range 处理
UpdateInteractions 开头 configOverlayOpen 时 MaterialTouch.Suppress text entry 时 publish 自己的触控目标 suppress 优先,其次 text entry 目标
行级触控目标 只有 Kind == Range 才挂滑条手势(enum 也是 TextMenu.Slider,但走下拉) string 项额外挂 TouchKind: "text" 目标 保留 master 的 range 判定,另加 string 文本目标
内部类型 OptionSnapshot 改为 readonly record struct 新增 StringSettingEntry 类 保留 master 的 struct,同时保留 StringSettingEntry

没有重叠的依据:master 这几次改的是 option 分类、滑条/下拉渲染、配置覆盖层和结构体类型;string 输入走的是 TextMenu.Button + OuiModOptionString 拦截,TryGetOption 只认带 Index/Values 字段的项,string 设置项不会被误判成 switch / dropdown / range。

验证

  • dotnet build Source/MicroblocksQolUtils.csproj -c Release(Everest stable lib-stripped):0 warning / 0 error
  • dotnet run --project Tests/ModOptions/ModOptions.csproj -c Release:OptionRules tests passed (36 assertions)
  • CI:Tests 通过(47s),Windows / Linux / macOS 平台构建进行中
  • 本机跑不了游戏实机,光标 / 空格 / 输入法的手工验证还得按 PR 描述里的步骤在 Windows / Android 上过一遍

顺带清理了 fork

@std-external

Copy link
Copy Markdown
Contributor Author

CI 在这个 head 上全绿:Tests 47s、Windows x64 3m10s、macOS x64 3m55s、Linux x64 8m10s、Android ARM64 2m1s。

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.

1 participant