fix(music): 修复输入丢失、焦点残留与拖拽状态脱离框架 - #171
Merged
Merged
Conversation
三处用户可感的问题: initGui 在每次窗口尺寸变化时都会重跑,而它无条件重建 searchField / qqUin / qqKey —— 于是改一下窗口大小就静默清空用户已输入的内容,包括粘贴进去的 QQ musickey(获取过程相当繁琐)。改为跨重建保留文本。 搜索框只在搜索标签页渲染时才有机会失焦(渲染路径里的 mouseClicked 调用)。 切到别的标签页后焦点残留,keyTyped 继续把按键喂进一个已经不在屏幕上的输入框。 改为切标签时清除焦点。 进度条与音量条的拖拽用私有布尔跟踪,不参与 GuiDragState 仲裁。私有标志永远 不会知道鼠标是在窗口外面松开的,所以 alt-tab 出去松手再回来,滑块仍然黏在 光标上。改为走 beginPointerCapture / isPointerCapturedBy,与 ClickGUI 的滑块、 滚动条、取色器一致,由 drawScreen 的每帧清扫统一释放。进度条保留「松手才提交 seek」的语义,避免每帧 seek 冲击解码器。 未改动:render 仍以 consumePressInBounds(0, 0, guiWidth, guiHeight) 一次性 消费整屏,再由 33 处私有 in(...) 各自重测。这使 consume-once 与 z-order 门控 对本界面无效,但该界面的控件几乎不重叠、模态也已用坐标置 -1 屏蔽,实际影响 远小于上述三项,留待后续单独处理。 Co-Authored-By: Claude <noreply@anthropic.com>
三处音频帧对齐问题,表现为拖动进度条后变成白噪音、再拖一次又恢复: seek 是在解码后的 PCM 流上按字节跳过的,但跳过量 (startMs * bytesPerMs) 只是碰巧才会是帧长的倍数。16-bit 立体声一帧 4 字节,落在帧中间会让之后 每个采样的高低字节与左右声道全部错位 —— 解出来就是白噪音。对齐概率约 1/4, 所以是「有时候」;下次拖动重新计算,碰巧对齐就恢复,所以「再拉一下就好」。 改为向下对齐到帧边界。 line.write 的长度同样不保证是整帧。SourceDataLine.write 的文档明确写明 长度非整帧时行为未定义,而 mp3 解码流不承诺帧对齐的读取量。改为只写完整帧, 余数用 carry 带入下次读取。 seek 时直接关闭 SourceDataLine 而不 flush,行缓冲里还排着旧位置解码出的 PCM,会在新流开始前播出来 —— seek 瞬间的短促爆音。改为先 stop() + flush()。 搜索框输入不显示:initGui 里对新建的 TextField 调 setText 会走 setCursorPosition,后者用 getWidth() 推算滚动偏移,而 width 只在 drawTextBox 里赋值 —— 对一个从未绘制过的输入框读到 0,于是把 lineScrollOffset 推过文本末尾,此后 substring 恒为空。改为完全不重建 (仅在 null 时创建),既保住文本又不触发这条路径。 进度条时灵时不灵:beginPointerCapture 要求当帧存在未被消费的 press,而 in() 已改为命中即消费,两条消费路径争同一个事件。新增 ScaledGuiScreen.acquireDrag,由 in() 确认归属后直接取得所有权,不做二次 消费;释放仍由 GuiDragState 统一处理,alt-tab 保护不受影响。 点击播放键会同时切换音源:render 此前一次性消费整屏 press,再把裸坐标交给 33 处私有 in() 各自重测,所有命中的控件全部触发。改为 peek 取坐标、由命中的 控件在 in() 内认领。根因则是侧边栏内容高度固定而可用高度随窗口收缩,窗口偏矮 时音源按钮会溢出到播放条上 —— 加了面板最小高度。 Co-Authored-By: Claude <noreply@anthropic.com>
gaoyu06
force-pushed
the
refactor/music-screen-input
branch
from
August 1, 2026 04:29
e44feb4 to
d4c1846
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
MusicScreen是绕过输入框架最彻底的一个界面。这个 PR 只修其中用户可感损失的三项,架构层面的整屏 consume 留待后续(理由见末尾)。1. 改窗口大小会清空已输入内容
initGui在每次窗口尺寸变化时都会重跑,而它无条件重建searchField/qqUin/qqKey。QQ 的 musickey 获取过程相当繁琐,静默丢失体验很差。改为跨重建保留文本。
2. 切标签页后按键喂进不可见的输入框
搜索框只在搜索标签页渲染时才有机会失焦(失焦逻辑在渲染路径里的
searchField.mouseClicked调用中)。切到「发现」或「歌单」后焦点残留,keyTyped仍然满足searchField.isFocused(),按键继续进入一个已经不在屏幕上的输入框。改为切标签时清除焦点。
3. alt-tab 后滑块黏在光标上
进度条与音量条用私有
draggingProgress/draggingVolume布尔跟踪,不参与GuiDragState仲裁。私有标志永远不会知道鼠标是在窗口外面松开的。改为走
beginPointerCapture/isPointerCapturedBy,与 ClickGUI 的滑块、滚动条、取色器一致,由drawScreen的每帧清扫统一释放。进度条保留「松手才提交 seek」的语义 —— 每帧 seek 会冲击解码器。
有意未改动
render仍以consumePressInBounds(0, 0, guiWidth, guiHeight)一次性消费整屏,再由 33 处私有in(...)各自重测。这确实使 consume-once 和 #170 的 z-order 门控对本界面失效。不在这个 PR 里改的原因:该界面的控件几乎不重叠,模态也已经用
hcx = -1屏蔽了坐标,所以理论缺陷的实际表现远小于上面三项;而改造它需要重写全部 33 处命中点,是一次独立的、需要完整回归的改动。混在一起会让这三个明确修复难以验证。🤖 Generated with Claude Code