fix(desktop): correct context menu position under uiScale - #213
Merged
Conversation
showContextMenu positioned the Overlay entry using the raw GestureDetector globalPosition (real device coordinates from the pointer event), while main.dart wraps the app's Navigator/Overlay in UiScaleWidget whenever uiScale != 1.0, laying out that subtree (and Overlay's own local coordinate space) at screen size / scale and painting it back up by scale. Positioned inside that Overlay is relative to the scaled-down local frame, so feeding it the unscaled global tap position produced an offset proportional to (scale - 1) times the distance from the window origin — the further from the top-left the click, the further the menu drifted from the cursor. Convert globalPosition through the Overlay's own RenderBox via globalToLocal before using it as left/top. This is a no-op when uiScale == 1.0 (no transform in the ancestor chain), and correctly inverts the RenderUiScale paint transform otherwise. showContextMenu is the single entry point for every right-click menu in the app (task rows, groups, sidebar, queues, header bar), so the fix covers all of them. Fixes #193
Closed
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.
Repro
用户在 Windows 客户端 v0.2.3 将「设置 → 外观 → 界面缩放」(
uiScale,lib/src/theme/theme_provider.dart,允许 0.8–1.5)调至非 1.0 后,右键任意任务行弹出的操作菜单(showTaskContextMenu,lib/src/widgets/task_list_item.dart)出现在远离鼠标点击处的位置,越靠窗口右下角点击偏移越大(见截图)。机器人环境无法构建/运行 Flutter,本 PR 走静态复现:沿
GestureDetector.onTapDown→showTaskContextMenu→showContextMenu(lib/src/widgets/context_menu.dart)的调用链读代码,结合lib/main.dart:1011-1019的uiScale接线,推导出的偏移量公式(点击点坐标 × (scale-1))与截图中箭头指示的偏移在数量级和方向上吻合(uiScale≈1.2 时预测偏移 ≈(198,46)px,截图观测约 (190,50)px)。Cause
lib/main.dart:1011-1019:uiScale != 1.0时,WidgetsApp.builder用UiScaleWidget(lib/src/widgets/ui_scale_widget.dart)包裹整个 Navigator/Overlay 子树——子树在constraints.biggest / scale的缩小逻辑尺寸下布局,paint时再整体放大scale倍;同时用MediaQuery.copyWith(size: mq.size / scale)把MediaQuery.size同步缩小,以匹配 Overlay 缩放子树内的本地坐标系。lib/src/widgets/context_menu.dart的showContextMenu却把TapDownDetails.globalPosition(原始指针事件的设备全局坐标,不受RenderUiScale变换影响)直接当作插入Overlay的Positioned(left, top)使用——但Positioned是相对 Overlay 缩小后的本地坐标系布局的,两者单位不一致,产生随点击点位置成比例放大的偏移。showContextMenu是全应用唯一的右键菜单入口(任务行/任务组/侧栏/队列/表头列设置等均复用它),因此该缺陷影响所有右键菜单。Fix
lib/src/widgets/context_menu.dart:在计算left/top前,经Overlay.of(context).context.findRenderObject()取 Overlay 自身RenderBox,用globalToLocal把设备全局坐标转换为 Overlay 本地坐标,再据此定位菜单(uiScale == 1.0时祖先链无变换,globalToLocal是恒等映射,不改变现有行为)。Verification
未在机器人环境中构建或测试 —— 运行环境无法承载本项目的 Flutter 工具链。本改动经静态审查:
lib/main.dart的uiScale接线(UiScaleWidget+MediaQuery.copyWith)与lib/src/widgets/ui_scale_widget.dart的RenderUiScale(performLayout/paint/applyPaintTransform),确认applyPaintTransform正确暴露了缩放矩阵,globalToLocal沿祖先链求逆能正确还原该变换。showMenu()的标准写法一致(用Overlay.of(context).context.findRenderObject() as RenderBox做坐标系转换基准),未引入新依赖或新错误处理路径。showContextMenu是仓库内唯一的右键菜单构造函数(task_list_item.dart/task_group_card.dart/sidebar.dart/header_bar.dart/task_list.dart/manifest_select_view.dart/new_download_dialog.dart/quick_download_form.dart均复用它),单点修复覆盖全部调用方,未发现遗漏。uiScale == 1.0(默认值)路径未改变行为:无UiScaleWidget包裹时祖先链无变换,globalToLocal退化为恒等映射。请维护者执行验证:
flutter analyze已知未覆盖:未新增自动化测试(该坐标变换依赖真实渲染管线的
RenderBox.globalToLocal,机器人环境无法运行 widget test 验证断言正确性;建议维护者补充一个挂载MaterialApp+ 非 1.0uiScale场景、模拟点击、断言 Overlay 菜单Positioned.left/top的 widget test)。Fixes #193