From 54fac5d5df0f80c71cb153003e9e59c832fff2d4 Mon Sep 17 00:00:00 2001 From: ZerxLabBot Date: Tue, 28 Jul 2026 11:07:30 +0000 Subject: [PATCH] fix(desktop): correct context menu position under uiScale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- lib/src/widgets/context_menu.dart | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/src/widgets/context_menu.dart b/lib/src/widgets/context_menu.dart index 62d19ce8..446d4282 100644 --- a/lib/src/widgets/context_menu.dart +++ b/lib/src/widgets/context_menu.dart @@ -70,9 +70,21 @@ void showContextMenu( dividers.length * dividerHeight + 8; // vertical padding + // globalPosition 来自原始指针事件的设备全局坐标,未经界面缩放 + // (uiScale,见 UiScaleWidget)逆变换;Overlay 的 Positioned 坐标系是 + // 该缩放下的逻辑坐标系(与本函数下方 MediaQuery.size 一致,main.dart + // 通过 MediaQuery.copyWith(size: mq.size/scale) 同步缩小)。直接把 + // globalPosition 当 Positioned.left/top 用,uiScale≠1.0 时菜单会按 + // (scale-1) 比例整体偏离鼠标(issue #193)。经 Overlay 自身 RenderBox + // 做一次 globalToLocal 修正——scale==1.0(无变换)时是恒等映射,不影响 + // 现有行为。 + final overlayBox = overlay.context.findRenderObject() as RenderBox?; + final localPosition = overlayBox == null + ? globalPosition + : overlayBox.globalToLocal(globalPosition); final screenSize = MediaQuery.of(context).size; - double left = globalPosition.dx; - double top = globalPosition.dy; + double left = localPosition.dx; + double top = localPosition.dy; if (left + menuWidth > screenSize.width) { left = screenSize.width - menuWidth - 4;