Skip to content

feat: add PositionFixer component for pixel-perfect positioning#1552

Open
wjyrich wants to merge 1 commit intolinuxdeepin:masterfrom
wjyrich:fix-mohu-icon
Open

feat: add PositionFixer component for pixel-perfect positioning#1552
wjyrich wants to merge 1 commit intolinuxdeepin:masterfrom
wjyrich:fix-mohu-icon

Conversation

@wjyrich
Copy link
Copy Markdown
Contributor

@wjyrich wjyrich commented Apr 14, 2026

Added a new PositionFixer QML component to handle pixel-perfect positioning of items within the dock panel. This component replaces multiple ad-hoc position fixing implementations in AppItem.qml and ShellSurfaceItemProxy.qml with a unified solution. The fixer calculates the correct physical pixel position considering device pixel ratio and applies rounding or ceiling to prevent sub-pixel rendering issues that cause blurriness.

Key changes:

  1. Created PositionFixer C++ class with QML integration
  2. Added PositionFixer to AppletItemButton.qml for applet positioning
  3. Replaced manual position fixing in AppItem.qml with PositionFixer
  4. Replaced manual position fixing in ShellSurfaceItemProxy.qml with PositionFixer
  5. Added CMakeLists.txt entries for new PositionFixer files

The PositionFixer component provides configurable options including useCeil for ceiling operations and useZeroTarget for positioning at origin. It uses a delayed timer to ensure proper timing for position calculations.

Log: Improved icon and surface positioning accuracy in dock panel

Influence:

  1. Test applet icons in dock panel for proper positioning without blurriness
  2. Verify application icons in task manager maintain crisp edges
  3. Check shell surface items (like system tray popups) render at correct pixel boundaries
  4. Test with different display scaling factors (100%, 150%, 200%)
  5. Verify drag-and-drop operations still work correctly
  6. Test launch animations don't interfere with positioning

feat: 添加 PositionFixer 组件实现像素级精确定位

新增 PositionFixer QML 组件,用于处理任务栏面板内项目的像素级精确定位。
该组件取代了 AppItem.qml 和 ShellSurfaceItemProxy.qml 中多个临时位置修 复实现,提供了统一的解决方案。修复器会计算考虑设备像素比的正确物理像素位
置,并应用四舍五入或向上取整操作,防止导致模糊的子像素渲染问题。

主要变更:

  1. 创建了带有 QML 集成的 PositionFixer C++ 类
  2. 在 AppletItemButton.qml 中添加 PositionFixer 用于小程序定位
  3. 在 AppItem.qml 中用 PositionFixer 替换手动位置修复
  4. 在 ShellSurfaceItemProxy.qml 中用 PositionFixer 替换手动位置修复
  5. 在 CMakeLists.txt 中添加新 PositionFixer 文件的条目

PositionFixer 组件提供可配置选项,包括用于向上取整操作的 useCeil 和用于
原点定位的 useZeroTarget。它使用延迟计时器确保位置计算的正确时机。

Log: 提升任务栏面板中图标和表面定位的准确性

Influence:

  1. 测试任务栏面板中的小程序图标定位是否正确且无模糊
  2. 验证任务管理器中的应用图标是否保持清晰边缘
  3. 检查外壳表面项目(如系统托盘弹出窗口)是否在正确的像素边界渲染
  4. 使用不同的显示缩放比例测试(100%、150%、200%)
  5. 验证拖放操作是否仍能正常工作
  6. 测试启动动画是否不会干扰定位

Summary by Sourcery

Introduce a reusable PositionFixer component to centralize pixel-perfect positioning logic for dock items and tray surfaces.

New Features:

  • Add a PositionFixer QML/C++ component to calculate pixel-aligned positions based on the window device pixel ratio.

Enhancements:

  • Replace ad-hoc positioning and timers in AppItem and ShellSurfaceItemProxy with the shared PositionFixer component.
  • Integrate PositionFixer into AppletItemButton to improve applet icon alignment and crispness across different scales.

Build:

  • Register new PositionFixer sources in the dock panel CMake configuration.

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: wjyrich

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 14, 2026

Reviewer's Guide

Introduces a reusable C++/QML PositionFixer helper that centralizes device-pixel-ratio‑aware positioning logic, then replaces ad‑hoc QML timers and mapping code in dock/taskmanager and tray surfaces with this component, and wires it into applet buttons; CMake is updated to build the new helper.

Sequence diagram for PositionFixer pixel-perfect positioning

sequenceDiagram
    participant QmlCaller as QmlCaller
    participant PositionFixer as PositionFixer
    participant QTimer as FixTimer
    participant QQuickWindow as Window
    participant ContentItem as ContentItem
    participant ItemParent as ItemParent

    QmlCaller->>PositionFixer: fix()
    PositionFixer->>QTimer: start(100ms)

    QTimer-->>PositionFixer: timeout()
    PositionFixer->>PositionFixer: forceFix()

    PositionFixer->>PositionFixer: validate m_item, m_container, window
    PositionFixer->>Window: contentItem()
    Window-->>PositionFixer: ContentItem

    PositionFixer->>Window: devicePixelRatio()
    Window-->>PositionFixer: dpr

    PositionFixer->>ContentItem: mapToItem(targetX, targetY)
    ContentItem-->>PositionFixer: scenePos

    PositionFixer->>PositionFixer: physicalX = round(scenePos.x * dpr)
    PositionFixer->>PositionFixer: physicalY = round(scenePos.y * dpr)

    alt useZeroTarget == true
        PositionFixer->>ItemParent: mapFromItem(physicalX / dpr, scenePos.y)
        ItemParent-->>PositionFixer: localPosX
        PositionFixer->>ItemParent: mapFromItem(scenePos.x, physicalY / dpr)
        ItemParent-->>PositionFixer: localPosY
        PositionFixer->>m_item: setX(localPosX.x)
        PositionFixer->>m_item: setY(localPosY.y)
    else useZeroTarget == false
        PositionFixer->>ItemParent: mapFromItem(physicalX / dpr, physicalY / dpr)
        ItemParent-->>PositionFixer: localPos
        PositionFixer->>m_item: setX(localPos.x)
        PositionFixer->>m_item: setY(localPos.y)
    end
Loading

Class diagram for the new PositionFixer helper

classDiagram
    class QQuickItem
    class QTimer
    class QQuickWindow

    class PositionFixer {
        QQuickItem *m_item
        QQuickItem *m_container
        bool m_useZeroTarget
        QTimer *m_timer
        +PositionFixer(parent: QQuickItem*)
        +QQuickItem* item()
        +void setItem(newItem: QQuickItem*)
        +QQuickItem* container()
        +void setContainer(newContainer: QQuickItem*)
        +bool useZeroTarget()
        +void setUseZeroTarget(newUseZeroTarget: bool)
        +void fix()
        +void forceFix()
        +itemChanged()
        +containerChanged()
        +useZeroTargetChanged()
    }

    PositionFixer --|> QQuickItem
    PositionFixer --> QTimer : uses
    PositionFixer --> QQuickItem : item
    PositionFixer --> QQuickItem : container
    PositionFixer --> QQuickWindow : window() and devicePixelRatio()
Loading

File-Level Changes

Change Details Files
Add reusable C++/QML PositionFixer component that snaps items to physical pixel boundaries with a delayed fix operation.
  • Implement dock::PositionFixer as a QQuickItem with QML-exposed properties for target item, container, and useZeroTarget behavior
  • Use a QTimer inside PositionFixer to defer the actual coordinate adjustment via forceFix() by 100 ms
  • In forceFix(), compute scene coordinates relative to window->contentItem(), apply devicePixelRatio rounding, then map back to the item's parent to set final x/y positions
  • Support both centered positioning and origin-based positioning depending on useZeroTarget, with special handling for independent X/Y snapping when useZeroTarget is true
panels/dock/positionfixer.h
panels/dock/positionfixer.cpp
Refactor AppItem icon positioning to use PositionFixer instead of inlined coordinate mapping and timers.
  • Remove custom mapToScene/mapFromScene helpers, manual devicePixelRatio rounding, and a dedicated Timer from the icon Image in AppItem.qml
  • Instantiate PositionFixer bound to the icon and trigger positionFixer.fix() from onIconGlobalPointChanged when not dragging and when launch animation is idle
  • Keep logic that clears anchors.centerIn before applying the new fixed position
panels/dock/taskmanager/package/AppItem.qml
Refactor ShellSurfaceItemProxy positioning to use PositionFixer with origin-based snapping and remove duplicate mapping logic and timer.
  • Add a PositionFixer instance targeting the inner ShellSurfaceItem (impl) with useZeroTarget enabled to snap the popup’s top-left to pixel boundaries
  • Wire the existing fixPosition() function and impl.visibleChanged handler to call positionFixer.fix() directly
  • Delete custom mapToScene/mapFromScene functions, the inline fixPosition implementation using Panel.devicePixelRatio, and its associated QML Timer
panels/dock/tray/ShellSurfaceItemProxy.qml
Introduce PositionFixer usage for applet buttons to ensure crisp applet icon alignment.
  • Expose a computed contentGlobalPoint property in AppletItemButton.qml that walks up the parent chain to derive the content item’s global position
  • Attach a PositionFixer to the control, using the control as both item and container, and invoke fix() when contentGlobalPoint changes
panels/dock/AppletItemButton.qml
Register PositionFixer sources in the dock panel build configuration.
  • Add positionfixer.h and positionfixer.cpp to the panels/dock CMakeLists file list so the helper is compiled into the plugin
panels/dock/CMakeLists.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The description mentions a useCeil option, but the PositionFixer class only exposes useZeroTarget and always uses std::round; if ceiling behavior is still required (e.g. for the QTBUG case), consider adding a dedicated useCeil property and branching the rounding logic accordingly.
  • In ShellSurfaceItemProxy.qml the old implementation used Math.ceil while the new PositionFixer uses std::round, which may subtly change popup positioning compared to the previous QTBUG workaround; it may be safer to preserve the original ceiling behavior via configuration for this use case.
  • The delayed fix() implementation relies solely on a 100 ms QTimer and returns early when container->window() is null, so if fix() is called before the item is in a window the position may never be corrected; consider triggering another fix() on windowChanged or using componentComplete/afterRendering hooks to ensure a final adjustment once the window is ready.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The description mentions a `useCeil` option, but the `PositionFixer` class only exposes `useZeroTarget` and always uses `std::round`; if ceiling behavior is still required (e.g. for the QTBUG case), consider adding a dedicated `useCeil` property and branching the rounding logic accordingly.
- In `ShellSurfaceItemProxy.qml` the old implementation used `Math.ceil` while the new `PositionFixer` uses `std::round`, which may subtly change popup positioning compared to the previous QTBUG workaround; it may be safer to preserve the original ceiling behavior via configuration for this use case.
- The delayed `fix()` implementation relies solely on a 100 ms QTimer and returns early when `container->window()` is null, so if `fix()` is called before the item is in a window the position may never be corrected; consider triggering another `fix()` on `windowChanged` or using `componentComplete`/`afterRendering` hooks to ensure a final adjustment once the window is ready.

## Individual Comments

### Comment 1
<location path="panels/dock/positionfixer.cpp" line_range="94-98" />
<code_context>
+
+    QQuickItem *itemParent = m_item->parentItem() ? m_item->parentItem() : m_container;
+
+    if (m_useZeroTarget) {
+        QPointF localPosX = itemParent->mapFromItem(contentItem, QPointF(physicalX / dpr, scenePos.y()));
+        QPointF localPosY = itemParent->mapFromItem(contentItem, QPointF(scenePos.x(), physicalY / dpr));
+        m_item->setX(localPosX.x());
+        m_item->setY(localPosY.y());
+    } else {
+        QPointF localPos = itemParent->mapFromItem(contentItem, QPointF(physicalX / dpr, physicalY / dpr));
</code_context>
<issue_to_address>
**issue (bug_risk):** The useZeroTarget branch mixes snapped and non-snapped coordinates, which likely deviates from the original axis-independent snapping logic.

In the original QML, X and Y were snapped independently by mapping `(physicalX / dpr, 0)` for X and `(0, physicalY / dpr)` for Y. Here, `useZeroTarget` mixes a snapped axis with the live scene coordinate of the other (`(physicalX / dpr, scenePos.y())`, `(scenePos.x(), physicalY / dpr)`), so the non-snapped axis still affects the mapping and can reintroduce sub-pixel drift.

To keep snapping axis-independent and consistent with the previous behavior, consider mapping with a stable reference on the non-snapped axis, e.g.:
- X: `mapFromItem(contentItem, QPointF(physicalX / dpr, 0))`
- Y: `mapFromItem(contentItem, QPointF(0, physicalY / dpr))`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Added a new PositionFixer QML component to handle pixel-perfect
positioning of items within the dock panel. This component replaces
multiple ad-hoc position fixing implementations in AppItem.qml and
ShellSurfaceItemProxy.qml with a unified solution. The fixer calculates
the correct physical pixel position considering device pixel ratio and
applies rounding or ceiling to prevent sub-pixel rendering issues that
cause blurriness.

Key changes:
1. Created PositionFixer C++ class with QML integration
2. Added PositionFixer to AppletItemButton.qml for applet positioning
3. Replaced manual position fixing in AppItem.qml with PositionFixer
4. Replaced manual position fixing in ShellSurfaceItemProxy.qml with
PositionFixer
5. Added CMakeLists.txt entries for new PositionFixer files

The PositionFixer component provides configurable options including
useCeil for ceiling operations and useZeroTarget for positioning at
origin. It uses a delayed timer to ensure proper timing for position
calculations.

Log: Improved icon and surface positioning accuracy in dock panel

Influence:
1. Test applet icons in dock panel for proper positioning without
blurriness
2. Verify application icons in task manager maintain crisp edges
3. Check shell surface items (like system tray popups) render at correct
pixel boundaries
4. Test with different display scaling factors (100%, 150%, 200%)
5. Verify drag-and-drop operations still work correctly
6. Test launch animations don't interfere with positioning

feat: 添加 PositionFixer 组件实现像素级精确定位

新增 PositionFixer QML 组件,用于处理任务栏面板内项目的像素级精确定位。
该组件取代了 AppItem.qml 和 ShellSurfaceItemProxy.qml 中多个临时位置修
复实现,提供了统一的解决方案。修复器会计算考虑设备像素比的正确物理像素位
置,并应用四舍五入或向上取整操作,防止导致模糊的子像素渲染问题。

主要变更:
1. 创建了带有 QML 集成的 PositionFixer C++ 类
2. 在 AppletItemButton.qml 中添加 PositionFixer 用于小程序定位
3. 在 AppItem.qml 中用 PositionFixer 替换手动位置修复
4. 在 ShellSurfaceItemProxy.qml 中用 PositionFixer 替换手动位置修复
5. 在 CMakeLists.txt 中添加新 PositionFixer 文件的条目

PositionFixer 组件提供可配置选项,包括用于向上取整操作的 useCeil 和用于
原点定位的 useZeroTarget。它使用延迟计时器确保位置计算的正确时机。

Log: 提升任务栏面板中图标和表面定位的准确性

Influence:
1. 测试任务栏面板中的小程序图标定位是否正确且无模糊
2. 验证任务管理器中的应用图标是否保持清晰边缘
3. 检查外壳表面项目(如系统托盘弹出窗口)是否在正确的像素边界渲染
4. 使用不同的显示缩放比例测试(100%、150%、200%)
5. 验证拖放操作是否仍能正常工作
6. 测试启动动画是否不会干扰定位
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.

2 participants