Visitar URL original
feat(animation): add independent DConfig switches for business animations by deepin-wm · Pull Request #1575 · linuxdeepin/treeland · GitHub
Skip to content

feat(animation): add independent DConfig switches for business animations - #1575

Open
deepin-wm wants to merge 3 commits into
linuxdeepin:masterfrom
deepin-wm:agent/developer/3fc06a53edf0
Open

deepin-wm wants to merge 3 commits into
linuxdeepin:masterfrom
deepin-wm:agent/developer/3fc06a53edf0

Conversation

@deepin-wm

@deepin-wm deepin-wm commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

What

Every business animation in treeland gets a boolean DConfig switch in a dedicated descriptor, misc/dconfig/org.deepin.dde.treeland.animation.json (default true, so current behaviour is unchanged). false means "finish the operation without the transition" — the window still closes, still minimizes, the workspace still switches.

The switches live in their own animation resource rather than in org.deepin.dde.treeland.user.json, and DConfigManager exposes it as WaylandServer.animationConfig (C++: AnimationConfig). QML binds Behavior/Transition enabled to WaylandServer.animationConfig.<key>, C++ reads WaylandServer::animationConfig() and listens to AnimationConfig::valueChanged plus WaylandServer::configChanged (reconnect after a user switch, drop the old connection).

Existing prohibitions stay authoritative: noAnimation, software rendering, enableDelegateAnimation, drag/partial states and the existing call arguments are all still checked first. A config that has not finished initializing is treated as "animation allowed", never as disabled.

Switches (28)

Grouped by feature — one switch per user-visible transition, with closely coupled sub-effects of the same feature sharing it:

Area Keys
windows windowOpen, windowClose, launchpadOpen, launchpadClose, layerShellOpen, layerShellClose
geometry maximize, fullscreen, restoreWindow, splashTransition
minimize / desktop minimize, restoreMinimized, showDesktop, restoreShowDesktop
workspace workspaceSwitch, workspaceBounce, workspaceVisibility
output outputRotation, mirrorOutputRotation
wallpaper / launcher wallpaperCrossFade, wallpaperScale, launchpadCover
alt-tab taskSwitcher
multitask view multitaskview
login / lock screen lockscreen
desktop extras dockPreview, captureToolbar, edgeTilePreview

dockPreview, captureToolbar and edgeTilePreview were previously read from keys that did not exist in any descriptor (so those animations never played); they now have real metadata.

How the direct-completion path works

For every effect with a business callback the existing completion method is reused instead of bypassing it:

  • surfacewrapper.cpp: the switch is checked where the animation object is created, so the existing noAnimation direct-completion branch finishes it (cover content, lock-screen confirmation, deleteLater of the wrapper, updateVisible, splash transition, decoration restore). Geometry changes go through finishGeometryAnimation() — sync XWayland position, apply the final state, clear the pointer; m_geometryAnimationEffect records which key owns the running animation.
  • WorkspaceAnimationController: the switcher layer is not created when the switch is off; switched off mid-flight it lands on the final viewportPos, clears the display layer and sets running=false.
  • WallpaperSwitcherItem: still waits for the new wallpaper to become ready (loading is business, not animation), then shows it and releases the old slot in one step; a superseded fade-out can no longer delete a newer slot.
  • WaylandServer::setWorkspaceVisible: stops the scale/opacity animations and writes the requested target, keeping m_workspaceVisibleTarget for mid-flight completion.
  • Output rotation applies the visual angle and the real transform together and cancels the pending half-way timer of an older request.
  • Login/lock-screen content slides land on their target and report completion through the normal stopped signal; the 300 ms unlock timer stays business.

A switch change only finishes an effect that is currently playing; it never replays a finished one. Short UI interpolations finish naturally. Gesture following (workspace swipe, multitask partial progress) is untouched — only the automatic transition after release is switched.

Verification

  • python3 -m json.tool on both descriptors; every item has value/serial/flags/name/name[zh_CN]/description/description[zh_CN]/permissions/visibility.
  • cmake --preset ci + cmake --build --preset ci (-Wall -Wextra -Werror): pass. Generated build/src/animationconfig.hpp exposes all 28 keys.
  • ctest --preset ci: 95/98 pass. The 3 failures (test_treeland_output_group_manager_v1, test_treeland_wallpaper_desktop_v1, test_wayland_xdg_output_unstable_v1) fail identically on a clean build of this same base commit — no DRM / DisplayManager / D-Bus in this environment.
  • New tests/test_animation_switches (qmltestrunner) covers EdgeTilePreview: geometry lands on the target with the switch off, animates with it on.

Not executed (no DRM/GBM/DisplayManager here): real lock screen, greeter/login, Alt-Tab, multitask view, dock preview, output rotation and wallpaper cross fade play-through; those paths are compile/ctest verified and reviewed, not observed on screen. Use dde-dconfig set -a org.deepin.dde.treeland -r org.deepin.dde.treeland.animation -s /$USER -k <key> -v false to check them on a real session.

Out of scope

  • Dock animations (D01–D09): this tree has no dock implementation and DockPreview.qml is not instantiated by treeland.
  • H03/H04/H05: no definition or runtime entry point found.
  • Input cursor blink, dynamic wallpaper content, video fade-out, theme cursor playback, client/example animations and static radius/shadow/blur/glass switches: untouched.

Summary by Sourcery

Provide independent user-configurable switches for business animations while preserving the completion of all underlying operations when transitions are disabled.

New Features:

  • Add 28 independent per-effect animation switches through a dedicated user-level DConfig descriptor exposed as WaylandServer.animationConfig.

Bug Fixes:

  • Ensure disabled transitions complete their underlying window, workspace, wallpaper, output, login, and desktop operations immediately without leaving stale visual state.
  • Add valid configuration metadata for dock preview, capture toolbar, and edge-tile preview animations.

Enhancements:

  • Apply animation settings dynamically across QML and C++ while preserving existing animation prohibitions, gesture behavior, and user-switch configuration handling.

Build:

  • Generate and install the new AnimationConfig descriptor and C++ wrapper.

Documentation:

  • Update Treeland DConfig guidance to document the dedicated animation configuration and its user-switch lifecycle.

Tests:

  • Add QML coverage for disabling, enabling, and replacing the edge-tile preview animation configuration.

@sourcery-ai

sourcery-ai Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Schema and migrations

Define the per-user animation switches and their defaults as the source of truth for the generated configuration.

  • misc/dconfig/org.deepin.dde.treeland.animation.json L1-303

API

Expose the cached per-user AnimationConfig through DConfigManager and WaylandServer, including initialization and user-switch notifications.

  • src/core/waylandserver.h L52-424 (6 small edits)
  • src/dconfig/dconfigmanager.h L13, L42-43, L74-75

Uses of the API

Apply individual switches across window, geometry, minimize, and show-desktop paths while preserving their existing completion behavior.

  • src/surface/surfacewrapper.cpp L217-264, L2255-2267, L2332-2340 (+6 small edits)

Use the workspace switches to skip or finish slide, bounce, and visibility transitions without preventing workspace changes.

  • src/core/waylandserver.cpp L338-341
  • src/workspace/workspace.cpp L251, L261, L282-287
  • src/workspace/workspaceanimationcontroller.cpp L53-66, L208-215, L224-230 (+1 small edits)

Wire switches into wallpaper, output rotation, task-switcher, multitask-view, launcher-cover, and lock-screen effects.

  • src/core/qml/Effects/LaunchpadCover.qml L11-92 (4 small edits)
  • src/core/qml/MirrorOutput.qml L7, L121-148
  • src/core/qml/SourceOutput.qml L71-98 (+4 small edits)
  • src/core/qml/TaskSwitcher.qml L48-55, L665-668 (+2 small edits)
  • src/core/qml/TaskWindowPreview.qml L12, L52, L76
  • src/plugins/lockscreen/qml/ControlAction.qml L13, L133, L139
  • src/plugins/lockscreen/qml/Greeter.qml L32-92 (4 small edits)
  • src/plugins/lockscreen/qml/LoginAnimation.qml L23-26
  • src/plugins/multitaskview/qml/MultitaskviewProxy.qml L26-34, L82, L96
  • src/plugins/multitaskview/qml/WindowSelectionGrid.qml L209-214 (+4 small edits)
  • src/plugins/multitaskview/qml/WorkspaceSelectionList.qml L329
  • src/wallpaper/wallpaperswitcheritem.cpp L190-212, L218-242 (+2 small edits)
  • src/wallpaper/wallpaperswitcheritem.h L65-66, L75

Gate edge-tile, capture-toolbar, and dock-preview animations, including their direct-settle behavior when disabled.

  • src/core/qml/CaptureSelectorLayer.qml L1, L78-84
  • src/core/qml/DockPreview.qml L232-244 (+4 small edits)
  • src/core/qml/DockPreview/DockPreviewSlot.qml L421
  • src/core/qml/EdgeTilePreview.qml L22, L49-61

Implementation

Bind the initial and active user animation resources and implement workspace-visibility direct completion.

  • src/core/waylandserver.cpp L236-257 (+3 small edits)

Create and cache user-scoped animation configuration resources and select the initial resource during setup.

  • src/dconfig/dconfigmanager.cpp L8, L113-133, L209

Declare the surface-wrapper helpers that select per-effect switches.

  • src/surface/surfacewrapper.h L583-587

Add the workspace controller’s switch queries and direct-completion behavior.

  • src/workspace/workspaceanimationcontroller.h L41-45

Configuration

Install and generate the animation descriptor and include its wrapper in the Treeland QML module.

  • misc/dconfig/CMakeLists.txt L4
  • src/CMakeLists.txt L91-97, L145

Documentation

Document the animation configuration’s user-scoped access path and initialization and user-switch behavior.

  • .agents/skills/treeland-dconfig/SKILL.md L8, L86

Tests

Add QML coverage for disabled and enabled edge-tile animation, configuration replacement, and re-enabling.

  • tests/CMakeLists.txt L21-22
  • tests/test_animation_switches/CMakeLists.txt L1-17
  • tests/test_animation_switches/imports/Treeland/WaylandServer.qml L1-12
  • tests/test_animation_switches/imports/Treeland/qmldir L1-3
  • tests/test_animation_switches/tst_switches.qml L1-96

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

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

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/dconfig/dconfigmanager.cpp" line_range="121" />
<code_context>
+    if (auto *config = m_animationConfigs.value(userName))
+        return config;
+
+    auto *config = AnimationConfig::createByName(QStringLiteral("org.deepin.dde.treeland.animation"),
+                                                QStringLiteral("org.deepin.dde.treeland"),
+                                                configSubpath(userName),
</code_context>
<issue_to_address>
**Customized switches reset to enabled**

When a user has customized one or more animation switches in the previous user configuration before upgrading, `animationConfig()` creates a separate resource under `org.deepin.dde.treeland.animation` without copying the prior per-user animation values; its new keys therefore use their `true` defaults, so users who had disabled animations see them turn back on after upgrading.

Migrate the old per-user values into the new animation resource, mapping consolidated switches appropriately, or explicitly preserve compatible legacy values during initialization.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/dconfig/dconfigmanager.cpp Resolved
@deepin-wm
deepin-wm force-pushed the agent/developer/3fc06a53edf0 branch from 5309cda to 7d68a3b Compare November 6, 2026 01:21
@deepin-wm

Copy link
Copy Markdown
Contributor Author

Ready for review and merge. Base is current master (adf53a028), no conflicts, mergeable = MERGEABLE.

Checks: commitlint, CLAssistant, cppcheck, license-check, Sourcery pass. The one red container (Arch) job fails in src/greeter/greeterproxy.cpp (LastLogin / SessionDirs not members of DDM::DaemonMessages) — that is container/DDM version drift, it fails the same way on other open PRs (#1581, #1572) and is unrelated to this change.

Branch protection needs an approving review from someone with write access; the PR author (deepin-wm) does not have write on linuxdeepin/treeland and cannot self-approve or merge. @zccrs could you take a look and, if it looks fine, merge with Rebase (the repo does not allow merge commits)? Keeping all 6 commits preserves the original authors.

@deepin-wm
deepin-wm force-pushed the agent/developer/3fc06a53edf0 branch 3 times, most recently from d5374e7 to fcb0989 Compare November 6, 2026 07:25
@wineee
wineee requested a lite review from Copilot November 6, 2026 07:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Active moderate findings show that disabling or changing animation settings during in-progress or pending transitions can leave stale animations and callbacks running instead of completing the current effect.

3 open issues
What changed in this PR

This pull request adds 28 per-user DConfig switches for Treeland’s business animations. Animation transitions can be skipped while the underlying operations—such as closing, minimizing, or switching workspaces—still complete.

Changes:

  • Adds and installs the dedicated animation DConfig descriptor and generated AnimationConfig integration.
  • Applies animation switches across C++, QML, window, workspace, wallpaper, output, lock-screen, multitask, launcher, Dock, and preview paths.
  • Adds QML coverage for enabled and disabled edge-tile preview animation behavior.
File Reviewed change
tests/​test_animation_switches/​tst_switches.qml Tests enabled and disabled edge-tile preview behavior.
tests/​test_animation_switches/​CMakeLists.txt Registers the animation-switch QML test.
tests/​CMakeLists.txt Adds the animation test directory.
src/​workspace/​workspaceanimationcontroller.h Declares workspace animation-switch helpers.
src/​workspace/​workspaceanimationcontroller.cpp Applies workspace slide and bounce settings.
src/​workspace/​workspace.cpp Skips switcher creation when workspace animation is disabled.
src/​wallpaper/​wallpaperswitcheritem.h Updates wallpaper transition and slot-management declarations.
src/​wallpaper/​wallpaperswitcheritem.cpp Adds configurable wallpaper cross-fade handling. Moderate issue (2 votes): the switch is captured before wallpaper readiness and is not re-evaluated if disabled during loading. Moderate issue (1 vote): active cross-fades are not stopped and released when the setting changes to disabled.
src/​surface/​surfacewrapper.h Declares per-effect surface animation helpers.
src/​surface/​surfacewrapper.cpp Applies switches to window, geometry, minimize, show-desktop, and splash animations. Moderate issue (1 vote): the direct geometry-completion path does not restore m_xwaylandPositionFromSurface. Moderate issue (3 votes): the disabled minimize/restore path can return without stopping an existing animation.
src/​plugins/​multitaskview/​qml/​WorkspaceSelectionList.qml Gates workspace thumbnail displacement animation.
src/​plugins/​multitaskview/​qml/​WindowSelectionGrid.qml Gates multitask layout and delegate animations.
src/​plugins/​multitaskview/​qml/​MultitaskviewProxy.qml Controls multitask entry and exit transitions.
src/​plugins/​lockscreen/​qml/​LoginAnimation.qml Applies the lock-screen animation switch to login slides.
src/​plugins/​lockscreen/​qml/​Greeter.qml Gates lock-screen background transitions.
src/​plugins/​lockscreen/​qml/​ControlAction.qml Gates lock-screen control resizing animations.
src/​dconfig/​dconfigmanager.h Adds storage and accessors for per-user animation configurations.
src/​dconfig/​dconfigmanager.cpp Creates and caches per-user AnimationConfig instances.
src/​core/​waylandserver.h Exposes AnimationConfig through the server property and change signal.
src/​core/​waylandserver.cpp Binds animation configuration during initialization and user switching. Moderate issue (4 votes): it does not observe AnimationConfig::valueChanged or complete active animations when settings change mid-flight.
src/​core/​qml/​TaskWindowPreview.qml Gates task preview transitions.
src/​core/​qml/​TaskSwitcher.qml Controls task-switcher panel and surface animations.
src/​core/​qml/​SourceOutput.qml Adds primary output rotation and wallpaper-scale switches.
src/​core/​qml/​MirrorOutput.qml Adds mirror-output rotation handling.
src/​core/​qml/​Effects/​LaunchpadCover.qml Gates launcher cover transitions.
src/​core/​qml/​EdgeTilePreview.qml Gates edge-tile preview geometry animation.
src/​core/​qml/​DockPreview.qml Adds the Dock preview switch. Moderate issue (1 vote): several Dock preview behaviors and transitions do not consult enableAnimation, so disabling dockPreview does not suppress all animations.
src/​core/​qml/​CaptureSelectorLayer.qml Adds capture-toolbar animation control. Moderate issue (1 vote): the asynchronously initialized setting is read only once, so a stored false value can still allow the animation to start.
src/​CMakeLists.txt Generates and bundles AnimationConfig.
misc/​dconfig/​org.deepin.dde.treeland.animation.json Defines the 28 user-facing animation settings and metadata.
misc/​dconfig/​CMakeLists.txt Installs the animation descriptor.
.agents/​skills/​treeland-dconfig/​SKILL.md Documents animation DConfig usage, scope, and lifecycle.

This pull request requires changes.

Review effort: Lite

Comment on lines +244 to +247
connect(config, &AnimationConfig::configInitializeSucceed,
this, &WaylandServer::animationConfigChanged);
connect(config, &AnimationConfig::configInitializeFailed,
this, &WaylandServer::animationConfigChanged);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

设计意图如此 允许动画自然结束,动画进行中不做约束

Comment thread src/surface/surfacewrapper.cpp Outdated
Comment on lines +2161 to +2162
if (!minimizeAnimationEnabled(direction))
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

如果上一个动画在运行 那么就让到自然的结束 保持原来的行为 为什么这样设计 因为 要保证动画中能够控制那么,代码会膨胀就像你建议的这样

Comment on lines +209 to +211
[this, newSlot = QPointer<WallpaperSlot>(newSlot), oldSlot, animated]() {
if (newSlot && m_currentSlot == newSlot)
startFadeIn(newSlot, oldSlot, animated);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

当前操作继续完成,后续操作读取新配置

"value": true,
"serial": 0,
"flags": [],
"name": "Splash transition animation",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

不加这个开关,启动画面是一个整体,靠 enablePrelaunchSplash 开关

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

已经去掉了

install(FILES
"org.deepin.dde.treeland.json"
"org.deepin.dde.treeland.app.json"
"org.deepin.dde.treeland.animation.json"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

为什么不放user配置里

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

将动画单独归类了 他仍然是按照用户subpath隔离的用户配置 不是全局配置


Item {
id: root
readonly property bool enableAnimation: WaylandServer.animationConfig.launchpadCover

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

没有特效还要创建这个qml吗

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

现在只是关闭了他的过渡动画 不是覆盖层本身。仍然需要最终的遮罩和壁纸缩放状态

Comment thread src/surface/surfacewrapper.cpp Outdated
{
auto *server = WaylandServer::instance();
auto *config = server ? server->animationConfig() : nullptr;
if (!config || !config->isInitializeSucceeded())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

不判断 isInitializeSucceeded, 失败返回默认值,而非true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

已经修复了

@deepin-wm deepin-wm left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

代码审查:四维度 + qt-cpp-review 流程

审查范围 agent/developer/3fc06a53edf0(10 commits,32 files,+791/−74)。方法:qt_review_lint.py --diff=HEAD~10(0 findings)+ 逐文件带上下文深审 + 实跑新增 QML 测试(4 passed)。

结论:通过,无阻塞缺陷。 28 个开关元数据齐全(value/serial/flags/name/name[zh_CN]/description/description[zh_CN]/permissions/visibility 全有,json.tool 通过),全部有接入点,没有孤儿键;直接完成路径统一复用既有业务收尾(onShowAnimationFinished / onHideAnimationFinished / completeSplashTransition / finishGeometryAnimation…),没有另起一套收尾逻辑。showDesktop → restoreShowDesktop、minimize → restoreMinimized 这两个容易搞反的映射我逐个对齐了 showdesktop.cpp:58 与 ShowDesktopAnimation.qml 的语义,是对的。

合入前建议处理下面 1 项描述性问题 + 3 项质量问题(已在代码处逐条标注):

  1. PR 描述写的是一套已经不存在的实现。 描述里「C++ listens to AnimationConfig::valueChanged plus WaylandServer::configChanged」「m_geometryAnimationEffect records which key owns the running animation」「geometry changes go through finishGeometryAnimation()」「setWorkspaceVisible … keeping m_workspaceVisibleTarget for mid-flight completion」「switched off mid-flight it lands on the final viewportPos」「A switch change only finishes an effect that is currently playing」——这些机制在最终代码里 grep 全为空,最后 3 个 commit(1749c9fa、fcb09894)已整体移除,实际语义是「只在操作入口读取开关,播放中的效果自然播完」。
    • 为什么要改:这段描述就是 QA 的验收口径("播放中改配置会不会当场完成"是可直接观察的行为),也是后来人改这块代码时的契约。请更新描述(推荐,代码行为更简单也更安全),或者恢复实现。
  2. 「配置未初始化视为允许」的守卫复制了 4 份(surfacewrapper.cpp / workspaceanimationcontroller.cpp / wallpaperswitcheritem.cpp / waylandserver.cpp),建议收敛成一个公共函数。
  3. SurfaceWrapper::windowAnimationEnabled() 重新实现了一遍 layer 分类规则,和 createNewOrClose() 的判定会分叉。
  4. 新增测试验证不到它想验证的契约(字符串替换生产 QML),28 个开关只覆盖 1 个,C++ 侧零覆盖。

其余为建议级:旋转动画 alwaysRunToEnd 与 stop() 的竞态、TaskSwitcher 里给 layout 子项赋 y、dockPreview 键在树内无消费者、这几个内部判定函数放在 Q_SLOTS 区。

验证情况:org.deepin.dde.treeland.animation.json 28 键字段齐全;user.json 已无 *AnimationEnabled 残留;debian/treeland-data.install 的 usr/share/dsg/configs/* 已覆盖新描述文件安装。CI:deepin container build/tests、cppcheck、commitlint、CLA 全绿;Arch container 红,但失败点是 src/greeter/greeterproxy.cpp:527 'LastLogin' is not a member of 'DDM::DaemonMessages',与本次改动无关(本 PR 未触碰 greeterproxy),属 Arch 容器 DDM 头文件版本不匹配。未在真机验证锁屏 / greeter / Alt-Tab / 多任务 / 输出旋转 / 壁纸渐变(本机无 DRM/DDM),这几条仅静态评审 + ctest 覆盖。

本评审由 郭垚(groveer)侧的代码审查助手执行。

Comment thread src/surface/surfacewrapper.cpp Outdated
Comment on lines +208 to +218
// Each animated effect reads its own AnimationConfig switch. An uninitialized
// config is not a valid "disabled" answer, so keep the default animated behavior
// until the real value is available.
bool SurfaceWrapper::animationEnabled(bool (AnimationConfig::*key)() const) const
{
auto *server = WaylandServer::instance();
auto *config = server ? server->animationConfig() : nullptr;
if (!config || !config->isInitializeSucceeded())
return true;
return (config->*key)();
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

同一段「未初始化不算关闭」的守卫,代码里重复了 4 份

这段逻辑在下面几处各写了一遍(字面几乎一样):

  • WorkspaceAnimationController::animationConfigEnabled() — src/workspace/workspaceanimationcontroller.cpp:48
  • WallpaperSwitcherItem::crossFadeEnabled() — src/wallpaper/wallpaperswitcheritem.cpp:250
  • WaylandServer::setWorkspaceVisible() — src/core/waylandserver.cpp:322

以后任何一处调整这个语义(例如改成"Init 失败也视为关闭"),都会悄悄只改一处,另外三处继续走旧语义。建议收敛成一个函数(在生成类上加 enabled(bool (AnimationConfig::*)() const) 包装,或放一个 free function),四个调用点各少 4 行。

逻辑本身没问题:config == nullptr / !isInitializeSucceeded() 时返回 true,与生成的 getter 默认值(bool p_xxx { true },来自描述文件)一致。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

已经修复了

Comment thread src/surface/surfacewrapper.cpp Outdated
if (!layerItem || !layerSurface)
return true;
const auto scope = QString(layerItem->layerSurface()->handle()->scope);
if (scope == "dde-shell/launchpad")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

这里的 layer 分类是第二套实现,和 createNewOrClose() 会分叉

createNewOrClose() 判断启动器用的是 isLaunchpad()(→ layerScope() == LaunchpadScope),这里用的是字面量 "dde-shell/launchpad"。两处规则(scope 字符串 + getExclusiveZoneEdge() != None)以后只要改一处,就会出现「用 launchpad 的开关去门控 layerShell 动画」这类静默错配 —— 而且没有任何测试能发现(这个分支需要真实 layer surface 才能走到)。

建议直接复用既有的 isLaunchpad() / layerScope()。

顺带确认一个语义:dde-shell/quick-launchpad(isQuickLaunchpad())现在落到 layerShellOpen/Close,而它视觉上属于启动器。这是有意为之吗?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

已经修复

Comment thread src/surface/surfacewrapper.h Outdated

// Per-effect animation switches. Each animated effect reads its own
// AnimationConfig key so products can combine them freely.
bool animationEnabled(bool (AnimationConfig::*key)() const) const;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

这几个是纯内部判定函数,放在 public Q_SLOTS: 区段会被 moc 收进槽表;但它们带 bool (AnimationConfig::*)() const 参数,永远不可能被 QMetaObject::invokeMethod / QML / queued connection 调用。

建议移到普通 public 方法区(updateHasActiveCapability() 这类非槽函数所在的位置),让 slots 区只保留真正的槽。

&WorkspaceAnimationController::finishAnimation);
}

bool WorkspaceAnimationController::animationConfigEnabled(bool (AnimationConfig::*key)() const) const

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

这是那份「未初始化视为允许」守卫的第 2 份拷贝(见 src/surface/surfacewrapper.cpp 的评论):建议三处合并成一个公共函数,避免语义分叉。

另外 animationConfigEnabled() 是内部判定,放在头文件的 public Q_SLOTS:(workspaceanimationcontroller.h:76)也会被 moc 当成槽;它不可能被元对象系统调用,更适合放在普通 public 方法区(和上面 :42 的 slideEnabled()/bounceEnabled() 放一起)。

Comment on lines +210 to +216
if (!slideEnabled()) {
// Finish synchronously: land on the destination and leave running=false,
// which also dismisses the workspace switcher layer.
m_slideAnimation->stop();
setViewportPos(m_animationDestination);
finishAnimation();
return;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

注释里的因果说反了一半

"leave running=false, which also dismisses the workspace switcher layer" —— finishAnimation() 只做 Q_EMIT finished(); setRunning(false);,它自己不碰 switcher。switcher 之所以消失,是 WorkspaceSwitcher.qml:10 的 visible: ...animationController.running 变成 false,触发 Workspace::createSwitcher() 里连的 visibleChanged → deleteLater()。

建议把链路写准,否则后来人会以为 finishAnimation() 有清理职责,改动时很容易把 switcher 的回收弄丢。

代码行为本身是对的(我核了手势路径:ShortcutRunner::updateWorkspaceSwipe() 先 createSwitcher() + setRunning(true),finishWorkspaceSwipe() 再调到这里,switcher 会在同一帧被隐藏并回收)。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

已经修复

Comment on lines +85 to +93
setTransform.scheduleTransform = orientation

if (!WaylandServer.animationConfig.outputRotation) {
// Apply the visual value and the real transform together instead
// of waiting for the half-way timer.
setTransform.stop()
rotationAnimator.stop()
rotation = rotationAngle(orientation)
screenViewport.rotateOutput(orientation)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

alwaysRunToEnd: true 与 stop() 的竞态

rotationAnimator 声明了 alwaysRunToEnd: true(:56),这里先 rotationAnimator.stop() 再 rotation = rotationAngle(orientation)。

触发条件:输出旋转动画(200 ms)正在播放时把 outputRotation 关掉,并在这一轮结束前再次触发旋转 —— stop() 仍会跑完当前迭代并写入旧的 rotationAnimator.to,把刚赋值的新角度覆盖掉。结果是画面角度与实际 screenViewport.rotateOutput() 应用的 transform 不一致,直到下一次旋转才恢复。

建议关闭分支里先 rotationAnimator.to = rotationAngle(orientation)(让在跑的这轮收敛到正确值)再 stop() / 赋值,或者去掉 alwaysRunToEnd。

src/core/qml/MirrorOutput.qml:136 是同一处问题(mirrorOutputRotation)。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

已经修复

Comment thread src/core/qml/TaskSwitcher.qml Outdated
switchItemAnimation.start()
} else {
switchItemAnimation.stop()
switchItem.y = switchItemAnimation.fromeY

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

switchItem 是 ColumnLayout 的子项,y 由布局管理;这里显式赋值会在下一次布局时被覆盖(Qt 还会打 layout 警告)。因为 switchItemAnimation.fromeY 本来就是 Component.onCompleted 里记录的布局落点(:288),视觉结果一致,所以这行实际不生效。

建议直接删掉(布局落点就是目标位置),或改成不依赖布局管理的写法。

id: root
property int aniDuration: 350
property bool enableAnimation: aniDuration > 0
property bool enableAnimation: aniDuration > 0 && WaylandServer.animationConfig.dockPreview

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

dockPreview 这个键在 treeland 树内没有消费者:DockPreview.qml 没有在 qmlengine.cpp 注册,也没有任何实例化点(PR 描述里也把它列为 out of scope)。

也就是说 28 个开关里有 1 个,用户关掉之后没有任何可观察效果。建议二选一:等 dock 侧真正接入后再发这个键;或者在描述文件的 description 里写明「当前树内无效果」,否则 QA 会把它当 bug 报回来。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

有加载 会实例化 qmlengine会加载这个

request.send()
verify(request.responseText.length > 0)
// Supply the compositor configuration without starting a Wayland server.
preview = Qt.createQmlObject(request.responseText.replace(/WaylandServer/g, "testCase.server"), testCase)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

这个测试验证不到它想验证的契约

把生产 QML 源码读出来做 replace(/WaylandServer/g, "testCase.server") 再 Qt.createQmlObject,副作用是真实的 WaylandServer.animationConfig 这条链完全没被触及。因此本次改动最容易犯的几类错 —— 键名写错、漏 import Treeland、WaylandServer.animationConfig 为 null —— 这个测试永远不会报出来。一个具体例子:EdgeTilePreview.qml 至今没有 import Treeland,靠的是既有那行 WaylandServer.config.activeColor 才没暴露问题,而这个测试给不出任何信号。

另外 28 个开关只覆盖了 edgeTilePreview 一个,C++ 侧(surface wrapper 的直接完成分支、workspace 控制器、壁纸槽位回收)零覆盖,而那才是真正有状态、有所有权风险的代码。

最低成本的补法:

  • windowAnimationEnabled() / geometryAnimationEnabled() / minimizeAnimationEnabled() / showDesktopAnimationEnabled() 都是纯映射,抽一个不需要 compositor 的单测验证「State / direction → 键」的映射表即可(顺带把 show=true → restoreShowDesktop 这个容易改反的语义钉住);
  • QML 侧改为在 import path 上放一个 Treeland stub 单例,而不是改源码文本。

(另:cleanup() 里直接 preview.destroy(),init() 失败时会二次报错淹没根因,建议加 null 判断。)

@deepin-wm
deepin-wm force-pushed the agent/developer/3fc06a53edf0 branch 4 times, most recently from e063229 to 0127ebf Compare November 10, 2026 13:25

@Groveer Groveer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

补充两条阻塞级问题(行内评论已定位到对应代码)。另外,PR 描述里声明的 splashTransition 开关在最终实现中缺失:misc/dconfig/org.deepin.dde.treeland.animation.json 与生成的 AnimationConfig 只有 27 个键,startPrelaunchSplashHideSequence() 也仍然无条件创建几何动画,建议补齐或修正描述中的开关清单。

property bool enableRadius: true
property bool enableAnimation: GraphicsInfo.api !== GraphicsInfo.Software

&& WaylandServer.animationConfig.taskSwitcher

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1:关闭任务切换动画后,面板中选中的窗口不会被激活

这个开关同时决定了 exit() 的收尾分支,但真正的窗口激活只写在 previewWindows.beginAnimation() 里(TaskSwitcher.qml:342 的 WaylandServer.forceActivateSurface(surface, reason))。当 taskSwitcher=false 时 playSurfaceAnimation 为 false,exit() 走 else 分支(TaskSwitcher.qml:657-664),只关闭面板并置 switchOn = false,选中的窗口不会被激活。

复现:打开 3 个以上窗口,设 taskSwitcher=false,长按 Alt-Tab 打开面板并移动到非当前窗口,松开 Alt。面板退出,但激活/焦点仍是原窗口。(快速轻按 Alt-Tab 的预切换路径可能掩盖该问题,所以容易漏测。)

已核对的调用链:

  • SurfaceFilterProxyModel::setActiveIndex() 只保存索引并发信号,不做激活;
  • WaylandServer::deleteTaskSwitch() 只销毁面板并恢复模式;
  • 对生产 QML 的 exit()/beginAnimation() 做隔离执行:启用动画时 forceActivateSurface 调用 1 次,禁用时 0 次。

建议:把“激活选中窗口”从动画分支中提取出来,禁用动画时也执行;并补一条回归测试——禁用 taskSwitcher 后退出 Alt-Tab,必须激活面板中选中的窗口。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

已经修复

Comment thread src/surface/surfacewrapper.cpp Outdated
// The minimize/restore state and visibility are already committed by the
// caller; disabling the animation only skips the shrink/expand transition.
auto *config = WaylandServer::instance()->animationConfig();
if (config && !(direction == OPEN_ANIMATION ? config->restoreMinimized() : config->minimize()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2:禁用恢复动画时,快速恢复窗口会留下正在播放的最小化动画

这个提前返回发生在 m_minimizeAnimation 的处理之前。如果 minimize=true、restoreMinimized=false,用户最小化窗口后在最小化动画结束前立刻恢复:业务状态已经由 minimize()/restoreFromMinimized() 更新,但这里直接 return,既没有 redirect() 也没有终止旧动画。旧动画继续朝最小化方向播放,而 MinimizeAnimation.qml:118 使用的是 hideSource: true,真实窗口在旧动画结束前一直被快照遮住,结束时突然出现。

注意这个场景不依赖“运行中修改配置”,只要两个开关取不同值就能触发。

建议:禁用新请求的动画时,先走现有的清理路径(断开连接并 deleteLater() 旧动画,必要时 redirect 到目标),然后再返回;并补一个“最小化过程中立即恢复”的测试。

@Groveer

Groveer commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

关于这 6 笔提交的拆分

先说明:本地这 6 笔与 PR 上的 6 笔逐笔代码树一致(commit SHA 不同),下面的结论基于代码内容而非提交顺序的猜测。

结论:不建议按现在的边界保留这 6 笔。问题不是“6 笔太多”,而是其中几笔记的是开发返工过程,而不是完整、可验证的逻辑单元。建议按最终职责重组为 3 笔。

现状

提交 内容 性质
feat: per-effect DConfig switches for simple UI animations 第一批 UI 动画开关 功能
feat: per-effect switches for business-driven animations 业务动画开关 功能主体
refactor: move switches into a dedicated user configuration 配置迁入独立描述文件 基础设施
fix: complete effect switches and share component flags 补漏 + 组件内复用 flag 对前两笔的修复
refactor: group animation switches by feature 13 个细分开关合并为 3 个 对前两笔的返工
test: load unmodified preview QML through a stub module EdgeTilePreview 测试 测试

中间态确实不完整(有具体证据)

前三笔在读取当时并不存在的配置键,直到第 4 笔才补齐:

  • WaylandServer.config.captureToolbarAnimationEnabled
  • WaylandServer.config.edgeTilePreviewAnimationEnabled
  • WaylandServer.config.workspaceThumbnailReorderAnimationEnabled

第 3 笔还把旧动画键从 org.deepin.dde.treeland.user.json 全部删除,但上面这些旧入口仍然指向已删除的键名,第 4 笔才换成 WaylandServer.animationConfig.*。

这不是单纯的提交风格问题:历史中的中间提交会出现配置无效、部分原有动画退化的状态。也就是说单看任何一笔中间提交,既不能独立回退,也不能独立验证。

另外,第 5 笔把 13 个细分开关合并成 3 个,本身是在推翻第 2 笔的设计粒度;最终粒度应当一开始就体现在实现里,而不是“先加再删”。

建议重组为 3 笔

  1. 新增最终的用户级动画配置基础设施
    最终的 27 个键与功能分组、org.deepin.dde.treeland.animation.json、安装与 dtk_add_config_to_cpp 规则、DConfigManager::animationConfig()、WaylandServer.animationConfig / animationConfigChanged、以及 treeland-dconfig skill 说明。

  2. 接入业务动画开关,并保证禁用时业务完成
    窗口开关/关闭、几何变化、最小化与恢复、显示桌面、工作区切换与边界回弹、壁纸交叉淡化、输出旋转、任务切换、多任务视图、登录与锁屏;包含必要的收尾修复(本次行内评论的 P1/P2 属于这一笔)和对应的回归测试。

  3. 接入独立 UI 组件开关与测试
    平铺预览、截图工具条、Dock 预览等与主流程解耦的效果,以及 tests/test_animation_switches。

为什么是 3 笔

  • 第 1 笔是其余两笔的编译前提,必须在前,且可独立 review:只加配置,不改变任何运行时行为。
  • 第 2 笔是用户可感知的核心行为变更,需要每一处关闭都对应一个“业务仍然完成”的收尾,因此必须自洽、带测试。
  • 第 3 笔是互不耦合的组件效果,回退风险最低,可以单独 cherry-pick。

拆成这 3 笔后,每笔都满足:能编译、行为完整、可独立验证、可单独回退。现在的 6 笔只有最后一笔满足这个标准。

操作方式(供参考)

如果认可这个边界,可以用交互式 rebase 把第 4、5 笔 squash 进对应功能提交,把测试挪到所属功能提交里,再按上面的职责重排;重点是最终历史干净,而不是保留返工过程。


本次 review 的验证情况:cmake --preset ci 下 treeland、lockscreen、multitaskview 目标构建通过;test_animation_switches 4 个场景 / 6 项全通过;JSON 元数据与 git diff --check 通过。未逐笔编译 6 个中间快照。

@deepin-wm
deepin-wm force-pushed the agent/developer/3fc06a53edf0 branch 2 times, most recently from 0f3ba82 to da372a8 Compare November 11, 2026 03:26
Groveer
Groveer previously approved these changes Nov 11, 2026
Introduce the dedicated animation configuration that carries every effect
switch, with the final key set and feature grouping from the start.

- misc/dconfig/org.deepin.dde.treeland.animation.json: 27 boolean keys,
  default true, grouped by feature (login and lock screen, window open and
  close, launcher, layer shell, geometry changes, minimize and restore, show
  desktop, launcher cover, wallpaper, workspace, task switcher, multitask
  view, output rotation, wallpaper cross fade, dock preview, capture toolbar,
  edge tile preview).
- Install the metadata next to the existing user configuration and generate
  the AnimationConfig wrapper through dtk_add_config_to_cpp.
- DConfigManager::animationConfig(userName) creates and caches one resource
  per user, with the same appId and subpath as the user configuration, so the
  keys stay per-user and never become global settings; initialAnimationConfig()
  hands the startup resource to the compositor.
- WaylandServer::animationConfig() exposes it to C++ and QML, and
  animationConfigChanged is emitted when the configuration is bound, the
  initialization finishes or the current user changes.
- Document the new entry in the treeland-dconfig skill.

Only the infrastructure is added here: no effect reads a switch yet, so every
existing animation keeps its current behaviour.

Log: WM-632
Signed-off-by: pengwenhao <pengwenhao@uniontech.com>

PMS: TASK-398405
Wire each switch where its animated effect starts, and let the disabled path
run the same business completion as the finished handler, so turning an effect
off only skips the transition and never the operation.

Window and surface effects (surfacewrapper.cpp):
- windowOpen / windowClose, launchpadOpen / launchpadClose and
  layerShellOpen / layerShellClose, each selected by type and direction.
- maximize / fullscreen / restoreWindow geometry changes.
- minimize / restoreMinimized, showDesktop / restoreShowDesktop.
The direct paths reuse the existing completion slots, so cover content, lock
screen confirmation, wrapper deletion, XWayland position sync, decoration
restore and snapshot cleanup still run exactly once. A disabled request also
releases a minimize transition started by the opposite request, which
otherwise keeps its hideSource snapshot over the real window until it ends.

Workspace (workspace.cpp, workspaceanimationcontroller.cpp):
- workspaceSwitch and workspaceBounce land on the final viewportPos with
  running=false, so the switcher layer still exits.
- workspaceVisibility (setWorkspaceVisible) stops the scale and opacity
  animations and writes this request's target, keeping the layer window
  hiding business.

Other modules:
- wallpaperCrossFade still waits for the new wallpaper to be ready, then swaps
  and releases the old slot; an overtaken fade can no longer delete the newer
  slot. wallpaperScale covers the launcher and lock screen wallpaper zoom.
- outputRotation and mirrorOutputRotation apply the visual rotation and the
  real transform together, and cancel the pending half-way timer.
- taskSwitcher covers mask, panel, thumbnails, window restore and exit; a
  disabled exit activates the window selected in the panel, because that
  activation used to happen only inside the animated path.
- multitaskview covers entry, exit, window layout, empty hint and workspace
  thumbnail reorder.
- login and lock screen: content slide, background transition and control
  button resize; the launcher cover keeps its final mask and wallpaper scale.

Gesture following (workspace swipe, multitask partial) stays interactive; only
the automatic transition after release is switched.

Log: WM-632
Signed-off-by: pengwenhao <pengwenhao@uniontech.com>

PMS: TASK-398405
The effects outside the window and session flow get their own keys, with the
test that loads them.

- edgeTilePreview: EdgeTilePreview applies targetGeometry directly when the
  switch is off, keeping the source geometry and the drag interaction.
- captureToolbar: the toolbar starts its fade explicitly on completion, so
  the switch can skip it while capture and source selection stay untouched.
- dockPreview: the popover reads the key through its enableAnimation flag, so
  the shell fade, frame scale, hover border and slot transitions all settle at
  their final value; without the fade the completion runs directly instead of
  waiting for an animation that never plays.
- tests/test_animation_switches: load the unmodified EdgeTilePreview.qml from
  a build-tree Treeland stub module and check the disabled path, a
  configuration change and re-enabling.

Log: WM-632
Signed-off-by: pengwenhao <pengwenhao@uniontech.com>

PMS: TASK-398405
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: deepin-wm

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

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: deepin-wm

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

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.

6 participants