Skip to content

fix(input): handle touch and mouse transitions - #3223

Open
NihilDigit wants to merge 1 commit into
open-ani:mainfrom
NihilDigit:fix/gesture-family-by-input-device
Open

fix(input): handle touch and mouse transitions#3223
NihilDigit wants to merge 1 commit into
open-ani:mainfrom
NihilDigit:fix/gesture-family-by-input-device

Conversation

@NihilDigit

@NihilDigit NihilDigit commented Jul 27, 2026

Copy link
Copy Markdown
Member

GestureFamily 此前由平台决定:桌面 MOUSE,Android 与 iOS TOUCH。但输入方式是运行时属性,带触屏的 Windows 二合一、外接鼠标的 Android 平板都会在触摸与鼠标之间来回切换,EpisodeVideo.kt 里的 TODO 记录了这个限制。本 PR 改为按最近实际使用的指针设备决定手势约定。

纯触摸设备只产生触摸事件,纯键鼠设备只产生鼠标事件,两者行为与改动前一致。受影响的只有混合设备。

行为变化

Windows 二合一,用触摸操作:

交互 之前 现在
单击画面 暂停/播放 显示或隐藏控制器
双击画面 进入全屏 暂停/播放
横滑 seek
右半屏竖滑 调节音量
中间竖滑 进入或退出全屏
长按 快进
追番页、条目详情页的 tab 无法滑动切换 可滑动切换
评论区、播放历史 无法下拉刷新 可下拉刷新

亮度调节在桌面仍然没有,brightnessControllerNoOpLevelController

Android 平板,接鼠标操作:

交互 之前 现在
单击画面 显示或隐藏控制器 暂停/播放
双击画面 暂停/播放 进入全屏
滚轮 调节音量
拖动 Pager、下拉刷新 响应鼠标 只响应触摸

组合期门控为什么不可行

GestureFamily 跟随运行时输入方式,再照旧用它决定挂哪些修饰符,会在两处失败。

手势识别期间写快照状态会触发重组,打断正在识别的手势。在按下事件里提交输入源,点击直接丢失。

不在按下时提交,组合期读到的值就滞后一次。切换设备后的第一次手势仍按上一个设备的约定处理,用户要操作两次才生效。

因此输入源状态按用途拆开,手势门控从组合期下移到事件层。

实现

输入源状态

ActiveInputSourceState 拆出三个字段:

类型 更新时机 用途
current 快照状态 按下以外的事件 组合期挂载决策
latest 普通字段 含按下 事件回调中读取,不滞后
hasSeenMouse 快照状态,单调 观察到鼠标事件 常驻 UI

trackActiveInputSource 在根节点观察指针类型,不消费事件。Enter/Exit 即使用户正在用手指也会带着 PointerType.Mouse 到达(鼠标指针仍停在窗口里),因此只采信 PressMoveReleaseScroll

手势按 down 事件过滤

修饰符无条件挂载,是否响应取决于本次手势 down 事件的 PointerType

  • 滑动 seek、滑动调节音量与亮度、上下滑进入全屏统一经由 touchDragOnly()
  • 长按快进传入 requiredPointerType = Touch
  • Pager 用 touchHorizontalScrollOnly()。它只消费 nested scroll 的目标方向,不消费原始指针位移,因此页面内滚动条与 Slider 的鼠标拖动不受影响。
  • 下拉刷新新增 touchOnly 参数,在嵌套滚动链上拦截非触摸的下拉。

是否挂载只取决于能力,即亮度与音量 controller 是否为 NoOpLevelController。切换设备后的第一次滑动不会因为修饰符尚未挂上而丢失。

Stylus 与 Eraser 归一为 Touch,笔与手指采用同一套约定。

追番页的分页此前是 if (isMobile()) 的结构性分支,桌面端不创建 HorizontalPager,标签点击里的 animateScrollToPage 因此一直在空转。现在统一挂载。

常驻 UI 按设备有没有鼠标

全屏音量控件与控制器自动隐藏改用 hasPointerDevice,它读 hasSeenMouse,观察到一次鼠标事件即为真且不再变回。按当前输入方式判断会让这些控件随用户在手指与鼠标之间切换而反复显隐。

键盘音量键的作用目标改按平台判断。桌面没有系统级 AudioManager,音量由 mediamp 提供,这是平台差异而非输入方式差异。

滚轮调节音量取消 family 门控,滚轮只可能来自鼠标。

桌面光标修饰符

cursorVisibility 两个分支此前链长不同:visible 为真时只有 testTag,为假时多一个 pointerHoverIcon。它包住整个播放器,输入里又含 detached slider 的可见性,于是控制器隐藏时开始的横滑 seek 会翻转它,重建下游节点,取消掉刚刚请求 slider 的那次手势。现在两个分支保持相同结构,只切换 PointerIcon

GestureFamily 的收缩

只保留点击与双击约定和 autoHideController。滑动、长按、滚轮的六个门控字段不再有引用,一并移除。

已知限制

Windows 上笔(PT_PEN)仍由系统合成 AWT 鼠标序列,桌面端因此把笔当鼠标处理。WindowsPointerInput 只桥接 PT_TOUCH,代码中留有 TODO。Android 上笔走 Touch 约定。

验证

新增 PointerTypeDragFilterTestPullToRefreshInputTest,扩充 EpisodeVideoControllerTest

手动验证:Windows 二合一设备,触摸与鼠标交替操作播放器。

@NihilDigit
NihilDigit force-pushed the fix/gesture-family-by-input-device branch 4 times, most recently from 45e1d78 to 792ad32 Compare July 30, 2026 04:44
Input method is a runtime property, not a platform one: a Windows 2-in-1
with a touchscreen and an Android tablet with a mouse attached both switch
back and forth. Track the pointer type actually in use and derive the
gesture family from it, falling back to the platform default until the
first pointer event arrives.

Gate gestures on the down event's pointer type rather than by swapping
modifiers at composition time. Committing the family on press would
recompose and interrupt the gesture being recognised, so the first
interaction after a switch was lost; mounting stays unconditional and the
filtering happens per event.

Controls that stay on screen — the volume control, the controller
auto-hide — key off whether the device has a mouse at all instead, so they
don't flicker as the user alternates between finger and mouse.

Keep the desktop cursor modifier's chain shape identical across its two
branches. It wraps the whole player and reads the detached slider's
visibility, so a swipe-to-seek begun while the controller was hidden
flipped it, rebuilt the nodes below it and cancelled the very gesture that
had requested the slider.

GestureFamily is left holding only the click/double-click conventions plus
autoHideController; the swipe, long-press and scroll flags no longer gate
anything and are removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@NihilDigit
NihilDigit force-pushed the fix/gesture-family-by-input-device branch from 326cf80 to 76e4514 Compare July 30, 2026 05:34
@NihilDigit
NihilDigit marked this pull request as ready for review July 30, 2026 05:37
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