Skip to content

Conversation

@Ivy233
Copy link
Contributor

@Ivy233 Ivy233 commented Jan 19, 2026

通过改进位置切换逻辑,实现平滑的过渡动画:

  1. 在切换位置时,先保存新位置并临时恢复到旧位置
  2. 在旧位置播放隐藏动画
  3. 切换到新位置后,从隐藏状态播放显示动画
  4. 使用状态标志精确控制动画流程,避免闪烁和显示异常

这样可以确保任务栏在位置切换时不会出现空白区域、内容溢出或突然跳变的问题。

Log: 修复任务栏位置切换时的显示异常
PMS: BUG-346777

Summary by Sourcery

Improve dock position change handling to provide smooth hide/show transitions and avoid visual glitches when moving the taskbar.

Bug Fixes:

  • Fix visual glitches such as blank areas and incorrect visibility when changing the dock/taskbar position, especially with auto-hide enabled.

Enhancements:

  • Refine dock animation flow to distinguish between normal show/hide and position-change transitions, coordinating them via explicit state flags.
  • Centralize dock position change handling in a dedicated connections helper that manages restoring the old position, applying the new one, and triggering the appropriate animations.

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Ivy233

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

sourcery-ai bot commented Jan 19, 2026

Reviewer's Guide

Refines the dock’s position-change handling by decoupling menu actions from animation callbacks and introducing a robust, state-driven animation flow that hides the dock at the old position, switches logical position, and then shows it at the new position without visual glitches.

Sequence diagram for dock position change animation flow

sequenceDiagram
    actor User
    participant MenuItem
    participant Applet
    participant Panel
    participant positionChangeConnections
    participant dockAnimation
    participant dock
    participant hideShowAnimation

    User->>MenuItem: select new position value
    MenuItem->>Applet: set position value
    Applet->>Panel: update position
    Panel-->>positionChangeConnections: onPositionChanged

    activate positionChangeConnections
    positionChangeConnections->>positionChangeConnections: check isRestoringPosition
    alt isRestoringPosition true
        positionChangeConnections-->>positionChangeConnections: return (ignore change)
    else isRestoringPosition false
        positionChangeConnections->>positionChangeConnections: savedNewPosition = Panel.position
        positionChangeConnections->>positionChangeConnections: isRestoringPosition = true
        positionChangeConnections->>Applet: position = previousPosition
        positionChangeConnections->>positionChangeConnections: isRestoringPosition = false
        positionChangeConnections->>dockAnimation: stop
        positionChangeConnections->>hideShowAnimation: stop
        positionChangeConnections->>dockAnimation: isPositionChanging = true
        positionChangeConnections->>Panel: read hideState
        positionChangeConnections->>dock: read visible
        alt Panel.hideState is Hide and dock not visible
            positionChangeConnections->>dockAnimation: isPositionChanging = false
            positionChangeConnections->>positionChangeConnections: handlePositionChangeAfterHide
        else dock is visible
            positionChangeConnections->>dockAnimation: startAnimation(false)
        end
    end
    deactivate positionChangeConnections

    rect rgb(230,230,255)
    dockAnimation-->>dockAnimation: onStopped
    alt isPositionChanging true and isShowing false
        dockAnimation->>dockAnimation: isPositionChanging = false
        dockAnimation->>positionChangeConnections: handlePositionChangeAfterHide
    else isShowing true
        dockAnimation->>Panel: read hideState
        alt Panel.hideState is Hide
            dockAnimation->>hideTimer: start
        end
    end
    end

    rect rgb(230,255,230)
    positionChangeConnections->>positionChangeConnections: handlePositionChangeAfterHide
    positionChangeConnections->>positionChangeConnections: update previousPosition
    positionChangeConnections->>Applet: position = savedNewPosition
    positionChangeConnections->>positionChangeConnections: savedNewPosition = -1
    positionChangeConnections->>Panel: changeDragAreaAnchor
    positionChangeConnections->>Panel: requestClosePopup
    positionChangeConnections->>dockAnimation: configure dockTransform for hidden offset
    positionChangeConnections->>dockAnimation: startAnimation(true)
    end
Loading

Updated class diagram for dockAnimation and positionChangeConnections

classDiagram
    class DockAnimation {
        bool useTransformBasedAnimation
        bool isShowing
        bool isPositionChanging
        var target
        string animProperty
        startAnimation(show)
        stop()
        onStopped()
    }

    class PositionChangeConnections {
        int previousPosition
        int savedNewPosition
        bool isRestoringPosition
        onPositionChanged()
        handlePositionChangeAfterHide()
        onDockSizeChanged()
        onCompleted()
    }

    class Dock {
        bool visible
        bool useColumnLayout
        int width
        int height
    }

    class DockTransform {
        int x
        int y
    }

    class Panel {
        int position
        int dockSize
        int hideState
        requestClosePopup()
        changeDragAreaAnchor()
    }

    class Applet {
        int position
    }

    DockAnimation --> Dock : animates
    DockAnimation --> DockTransform : uses
    PositionChangeConnections --> DockAnimation : controls
    PositionChangeConnections --> Panel : updates
    PositionChangeConnections --> Applet : restores_and_applies_position
    PositionChangeConnections --> DockTransform : sets_hidden_offset
    Panel --> Dock : configures_size
Loading

Flow diagram for dock position change and animation states

flowchart TD
    A[User changes dock position via menu] --> B[Panel.position changes]
    B --> C[onPositionChanged in positionChangeConnections]

    C --> D{isRestoringPosition}
    D -->|true| E[Return, ignore change]
    D -->|false| F[Save savedNewPosition from Panel.position]

    F --> G[Set isRestoringPosition = true]
    G --> H[Restore Applet.position to previousPosition]
    H --> I[Set isRestoringPosition = false]

    I --> J[Stop dockAnimation and hideShowAnimation]
    J --> K[Set dockAnimation.isPositionChanging = true]

    K --> L{Panel.hideState is Hide and dock not visible}
    L -->|true| M[Set dockAnimation.isPositionChanging = false]
    M --> N[handlePositionChangeAfterHide]
    L -->|false| O["startAnimation(false) for hide"]

    O --> P[dockAnimation onStopped]
    P --> Q{isPositionChanging and not isShowing}
    Q -->|true| R[Set isPositionChanging = false]
    R --> N
    Q -->|false| S{isShowing and Panel.hideState is Hide}
    S -->|true| T[start hideTimer]
    S -->|false| U[End]

    N --> V{savedNewPosition is valid}
    V -->|false| U
    V -->|true| W[Set previousPosition = savedNewPosition]
    W --> X[Apply Applet.position = savedNewPosition]
    X --> Y[Reset savedNewPosition = -1]
    Y --> Z[changeDragAreaAnchor and requestClosePopup]
    Z --> AA[Set dockTransform to hidden offset based on position]
    AA --> AB["startAnimation(true) for show"]
    AB --> AC[dockAnimation onStopped with isShowing true]
    AC --> AD{Panel.hideState is Hide}
    AD -->|true| T
    AD -->|false| U
Loading

File-Level Changes

Change Details Files
Introduce state-driven dock animation flow with explicit handling for position-change sequences.
  • Add isPositionChanging flag to the dockAnimation object to distinguish between normal show/hide and position-change-driven animations.
  • Extend dockAnimation.onStopped to either continue the position-change workflow after a hide animation or trigger auto-hide after a show animation when appropriate.
  • Ensure running animations are stopped and relevant hide/show animations are coordinated around position changes.
panels/dock/package/main.qml
Rework position menu handling so it no longer couples the UI toggle directly to animation callbacks.
  • Simplify ActionButton.onTriggered to only update the underlying Applet property and keep the checked state bound to that property.
  • Remove the previous positionChangeCallback pattern that connected to dockAnimation.onStopped and started animations from the menu layer.
panels/dock/package/main.qml
Centralize and harden dock position-change logic using a dedicated Connections block that orchestrates hide-at-old / show-at-new behavior.
  • Track previousPosition, savedNewPosition, and an isRestoringPosition guard to safely perform temporary position restoration without re-entrancy issues.
  • On position change, temporarily restore the dock to its previous position, stop any running animations, mark a position-change in progress, and either directly finalize the change when the dock is already hidden or start a hide animation otherwise.
  • Implement handlePositionChangeAfterHide to apply the new position, update drag area anchors, close popups, preset transform offsets to the hidden state based on dock orientation, and then run the show animation from the correct off-screen position.
  • Initialize previousPosition on component completion so the first position change has a valid baseline.
panels/dock/package/main.qml

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

@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 left some high level feedback:

  • The dockAnimation.isPositionChanging flag is only reset in onStopped and the early-return path for hidden docks; if the animation is interrupted (e.g., by another position change or a manual stop()), this flag may remain true and affect later animations—consider centralizing state reset so all code paths that stop animations clear this flag consistently.
  • In the Connections handler for onPositionChanged, the isRestoringPosition flag is set and immediately cleared after assigning Applet.position; this relies on synchronous behavior of the signal emission—if this logic ever changes or other async work is added, it may become brittle, so consider scoping the restore logic into a helper that manages the flag with a clearer critical section.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `dockAnimation.isPositionChanging` flag is only reset in `onStopped` and the early-return path for hidden docks; if the animation is interrupted (e.g., by another position change or a manual `stop()`), this flag may remain true and affect later animations—consider centralizing state reset so all code paths that stop animations clear this flag consistently.
- In the `Connections` handler for `onPositionChanged`, the `isRestoringPosition` flag is set and immediately cleared after assigning `Applet.position`; this relies on synchronous behavior of the signal emission—if this logic ever changes or other async work is added, it may become brittle, so consider scoping the restore logic into a helper that manages the flag with a clearer critical section.

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.

@Ivy233 Ivy233 force-pushed the fix-dock-position-transform branch from 9b5c6fe to fffc65c Compare January 20, 2026 06:41
@Ivy233 Ivy233 changed the title fix: 修复任务栏位置切换时的显示异常 fix: Fixed display issues when switching taskbar positions Jan 21, 2026
Improve position switching logic with smooth transition animations:
1. Save new position and temporarily restore to old position when switching
2. Play hide animation at old position
3. Switch to new position and play show animation from hidden state
4. Use state flags to precisely control animation flow and avoid flickering

This ensures the dock doesn't show blank areas, content overflow, or sudden jumps during position changes.

Log: Fix display issues when switching dock position
PMS: BUG-346777
@Ivy233 Ivy233 force-pushed the fix-dock-position-transform branch from fffc65c to 72c38ac Compare January 21, 2026 02:20
@deepin-ci-robot
Copy link

deepin pr auto review

这段代码主要是为了修复Dock面板在切换位置(如从底部切换到左侧)时的动画和显示逻辑问题。代码整体逻辑清晰,但存在一些可以优化的地方,特别是在逻辑严谨性、性能和代码风格上。

以下是详细的审查意见和改进建议:

1. 代码逻辑与语法审查

问题 1:竞态条件与状态管理

  • 分析:在 ConnectionsonPositionChanged 中,使用了 isRestoringPosition 标志位来防止递归调用(即修改 Applet.position 回调触发 onPositionChanged)。虽然逻辑上可行,但在 Applet.position = previousPositionisRestoringPosition = false 之间,如果Qt的事件循环处理了其他事件,可能会导致状态不一致。
  • 建议:虽然很难完全避免这种异步回调问题,但建议确保 Applet.position 的赋值是同步的,或者确保在这段代码执行期间不会有其他干扰逻辑。目前的实现在QML环境下通常是安全的,但需注意如果未来引入复杂的异步操作。

问题 2:dockTransform 的重置时机

  • 分析:在 handlePositionChangeAfterHide 中,根据 useTransformBasedAnimation 设置了 dockTransform 的初始偏移量(hideOffset),然后调用 startAnimation(true)。这看起来是为了让Dock从“隐藏”的状态滑入。
  • 风险:如果 startAnimation(true) 内部没有处理 dockTransform 的重置逻辑,或者动画属性绑定混乱,可能会导致Dock位置跳变。
  • 建议:确保 startAnimation 函数内部对于 useTransformBasedAnimation 为 true 的情况,能够正确处理起始状态。目前的代码在 handlePositionChangeAfterHide 中显式设置了初始状态,这是好的做法。

问题 3:savedNewPosition 的初始化与重置

  • 分析savedNewPosition 初始化为 -1,并在 handlePositionChangeAfterHide 中重置为 -1。这是一种“哨兵值”用法。
  • 建议:逻辑正确。但为了代码健壮性,建议在 onPositionChanged 开始处也检查 savedNewPosition,防止在极端情况下(例如动画被强制打断)状态未重置导致逻辑错误。

2. 代码质量与可读性

问题 1:魔法数字

  • 代码savedNewPosition: -1
  • 建议:虽然 -1 很常见,但为了代码清晰,可以定义一个常量或枚举,例如 property int invalidPosition: -1,然后使用 savedNewPosition: invalidPosition

问题 2:函数职责

  • 分析handlePositionChangeAfterHide 承担了多项职责:更新位置、更新锚点、关闭弹窗、设置变换属性、启动动画。
  • 建议:虽然QML通常倾向于轻量级脚本,但可以考虑将“设置变换属性”的逻辑提取为一个辅助函数,或者注释清晰,以提高可读性。

问题 3:注释缺失

  • 分析dockAnimation.isPositionChanging 是新增的状态标志,但在定义处缺少注释说明其用途。
  • 建议:在 dockAnimation 对象中为 isPositionChanging 添加注释,解释它用于区分普通的隐藏/显示动画和位置切换过程中的隐藏动画。

3. 代码性能

问题 1:频繁的属性绑定

  • 分析:在 MutuallyExclusiveMenuItemComponent.onCompleted 中使用了 Qt.binding
    checked = Qt.binding(function() { return Applet[prop] === value; });
  • 建议:这是正确的做法,确保了 checked 状态随 Applet[prop] 动态更新。性能影响可忽略,因为UI属性绑定的开销通常很小。

问题 2:动画停止操作

  • 代码dockAnimation.stop(); hideShowAnimation.stop();
  • 建议:在切换位置时强制停止动画是必要的,防止动画冲突。这部分逻辑没有性能问题。

4. 代码安全

问题 1:潜在的无限循环或卡死

  • 分析:在 onPositionChanged 中,代码修改了 Applet.position。虽然使用了 isRestoringPosition 标志位,但如果 Panel.position 的信号发送机制出现异常,或者底层C++实现有bug,可能导致逻辑死锁。
  • 建议:目前的实现是标准的QML防御性编程。无需额外修改,但需测试在快速连续点击位置切换按钮时的行为。

5. 综合改进代码示例

基于以上分析,以下是优化后的代码片段建议:

    Connections {
        id: positionChangeConnections
        property int previousPosition: Panel.position
        property int savedNewPosition: -1
        property bool isRestoringPosition: false

        function onPositionChanged() {
            // 忽略由我们自己的恢复操作触发的位置变化
            if (isRestoringPosition) {
                return;
            }

            // 保存新位置
            savedNewPosition = Panel.position;

            // 设置标志以忽略下一次位置变化(由恢复操作触发)
            isRestoringPosition = true;
            
            // 暂时恢复到旧位置以执行隐藏动画
            Applet.position = previousPosition;
            
            // 清除标志
            isRestoringPosition = false;

            // 停止任何正在运行的动画
            dockAnimation.stop();
            hideShowAnimation.stop();

            // 标记我们正在改变位置
            dockAnimation.isPositionChanging = true;

            // 检查Dock当前是否已隐藏
            if (Panel.hideState === Dock.Hide && !dock.visible) {
                // 如果已隐藏,直接处理位置变更,无需隐藏动画
                dockAnimation.isPositionChanging = false;
                handlePositionChangeAfterHide();
            } else {
                // 否则,在旧位置启动隐藏动画
                dockAnimation.startAnimation(false);
            }
        }

        function handlePositionChangeAfterHide() {
            if (savedNewPosition === -1) return;

            // 应用位置变更
            previousPosition = savedNewPosition;
            Applet.position = savedNewPosition;
            savedNewPosition = -1;

            changeDragAreaAnchor();
            Panel.requestClosePopup();

            // 在显示前将变换设置为隐藏位置
            prepareTransformForAnimation();

            // 启动显示动画
            dockAnimation.startAnimation(true);
        }

        // 辅助函数:准备变换属性,使动画从隐藏状态开始
        function prepareTransformForAnimation() {
            if (dockAnimation.useTransformBasedAnimation) {
                // 计算隐藏偏移量:左侧/顶部为负,右侧/底部为正
                var hideOffset = (Applet.position === Dock.Left || Applet.position === Dock.Top) 
                                 ? -Panel.dockSize 
                                 : Panel.dockSize;
                
                if (dock.useColumnLayout) {
                    dockTransform.x = hideOffset;
                    dockTransform.y = 0;
                } else {
                    dockTransform.x = 0;
                    dockTransform.y = hideOffset;
                }
            } else {
                // 如果不使用变换,确保重置
                dockTransform.x = 0;
                dockTransform.y = 0;
            }
        }

        Component.onCompleted: {
            previousPosition = Panel.position
        }
        
        function onDockSizeChanged() {
            dock.dockSize = Panel.dockSize
        }
    }

总结

这段代码的修改是为了实现更平滑的Dock位置切换动画。主要改进点在于:

  1. 明确状态标志:通过 isPositionChanging 明确区分动画类型。
  2. 逻辑解耦:将位置变更的“隐藏”和“显示”阶段清晰地分开处理。
  3. 防御性编程:使用 isRestoringPosition 防止信号循环触发。

建议采纳上述关于注释魔法数字函数提取的建议,以提高代码的可维护性。逻辑本身在QML环境下是合理且安全的。

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