From a451c1d5f787587acb4768c883ca7b57d7112f29 Mon Sep 17 00:00:00 2001 From: Wu JiangYu Date: Tue, 25 Aug 2026 15:21:01 +0800 Subject: [PATCH] refactor: replace PanelPopup with ApplicationWindow for windowed launcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Replace PanelPopup with ApplicationWindow for the windowed launcher mode 2. Refactor screen assignment logic into reusable functions (dockScreen/ assignDockScreen) 3. Simplify fullscreen frame visibility logic in onViewDeactivated 4. Add DLayerShellWindow properties for precise dock-relative positioning 5. Add DWindow blur effects, theming, and border styling 6. Implement blur-aware background with appearance-based opacity 7. Add focus/active state handling with hide timer coordination The windowed launcher previously used PanelPopup which has limited positioning and styling capabilities. The ApplicationWindow approach provides proper window management features like layer shell positioning, native blur effects, and atomic behavior control. The screen assignment refactoring improves code quality by separating screen detection from assignment. Log: Improved windowed launcher mode with better positioning, blur effects, and focus handling Influence: 1. Test windowed launcher display on different dock positions (top/ bottom/left/right) 2. Verify launcher positioning relative to dock and screen edges 3. Test blur background effect in both light and dark themes 4. Verify launcher hides when clicking outside or losing focus 5. Test multi-screen scenarios with different screen configurations 6. Verify windowed launchpad opens and closes correctly 7. Test windowed launchpad with different dock spacing/thickness settings refactor: 将窗口模式启动器从 PanelPopup 替换为 ApplicationWindow 1. 将窗口模式启动器从 PanelPopup 替换为 ApplicationWindow 2. 将屏幕分配逻辑重构为可复用函数(dockScreen/assignDockScreen) 3. 简化 onViewDeactivated 中的全屏框架可见性逻辑 4. 添加 DLayerShellWindow 属性以实现精确的坞站相对定位 5. 添加 DWindow 模糊效果、主题和边框样式 6. 实现基于外观透明度的模糊感知背景 7. 添加焦点/激活状态处理与隐藏定时器协调 窗口模式启动器之前使用 PanelPopup,其定位和样式能力有限。 ApplicationWindow 方案提供了正确的窗口管理功能,如图层壳定位、原生模糊效 果和原子行为控制。屏幕分配重构通过分离屏幕检测与分配来提高代码质量。 Log: 改进窗口模式启动器,优化定位、模糊效果和焦点处理 Influence: 1. 测试不同坞站位置(上/下/左/右)下窗口模式启动器的显示 2. 验证启动器相对于坞站和屏幕边缘的定位 3. 在浅色和深色主题中测试模糊背景效果 4. 验证点击外部或失去焦点时启动器是否正确隐藏 5. 测试不同屏幕配置下的多屏场景 6. 验证窗口模式启动器的打开和关闭功能 7. 测试不同坞站间距/厚度设置下的窗口模式启动器 PMS: TASK-394725 --- .../package/launcheritem.qml | 135 ++++++++++++++---- 1 file changed, 106 insertions(+), 29 deletions(-) diff --git a/shell-launcher-applet/package/launcheritem.qml b/shell-launcher-applet/package/launcheritem.qml index b14ba723..e89d7b43 100644 --- a/shell-launcher-applet/package/launcheritem.qml +++ b/shell-launcher-applet/package/launcheritem.qml @@ -37,7 +37,7 @@ AppletItem { updateLaunchpadPos() } function onViewDeactivated() { - if (LauncherController.currentFrame === "FullscreenFrame" && LauncherController.visible) { + if (LauncherController.visible) { LauncherController.hideWithTimer() } } @@ -70,7 +70,7 @@ AppletItem { } Component.onCompleted: { updateLaunchpadPos() - assignFullscreenFrameScreen() + assignDockScreen(launcher.fullscreenFrame) } function decrementPageIndex(pages) { @@ -156,14 +156,25 @@ AppletItem { LauncherController.visible = false; } - function assignFullscreenFrameScreen() { - const newScreenName = DS.applet("org.deepin.ds.dock").screenName + function dockScreen() { + const dock = DS.applet("org.deepin.ds.dock") + if (!dock) + return null + for (const scr of Qt.application.screens) { - if (scr.name === newScreenName) { - launcher.fullscreenFrame.screen = scr - return - } + if (scr.name === dock.screenName) + return scr } + return null + } + + function assignDockScreen(window) { + if (!window) + return + + const scr = dockScreen() + if (scr && window.screen !== scr) + window.screen = scr } // A singleshot timer @@ -172,7 +183,7 @@ AppletItem { interval: 100 repeat: false onTriggered: { - assignFullscreenFrameScreen() + assignDockScreen(launcher.fullscreenFrame) } } @@ -263,39 +274,105 @@ AppletItem { } } - PanelPopup { + Window { id: windowedModeLauncher - property bool visibility: LauncherController.visible && (LauncherController.currentFrame === "WindowedFrame") - + objectName: "WindowedFrameApplicationWindow" + title: "dde-shell/launchpad" + visible: LauncherController.visible && (LauncherController.currentFrame === "WindowedFrame") width: 610 height: 480 - windowTitle: "dde-shell/launchpad" - popupX: DockPanelPositioner.x - popupY: DockPanelPositioner.y - DockPanelPositioner.bounding: Qt.rect(launcher.itemPos.x + width / 2 * ((Panel.position + 1) % 2), - launcher.itemPos.y + height / 2 * (Panel.position % 2), - width, height) + transientParent: null + color: "transparent" + + readonly property int dockPosition: DesktopIntegration.dockPosition + readonly property bool dockIsHorizontal: dockPosition === Qt.UpArrow || dockPosition === Qt.DownArrow + readonly property bool isDarkTheme: D.DTK.themeType === D.ApplicationHelper.DarkType + readonly property int dockThickness: dockIsHorizontal ? Panel.rootObject.height + : Panel.rootObject.width + readonly property int dockExclusion: dockThickness + DesktopIntegration.dockSpacing + readonly property int iconAlignOffset: Math.max(dockIsHorizontal ? itemPos.x : itemPos.y, + DesktopIntegration.dockSpacing) + + DLayerShellWindow.anchors: { + switch (dockPosition) { + case Qt.RightArrow: + return DLayerShellWindow.AnchorRight | DLayerShellWindow.AnchorTop + case Qt.DownArrow: + return DLayerShellWindow.AnchorBottom | DLayerShellWindow.AnchorLeft + case Qt.UpArrow: + case Qt.LeftArrow: + default: + return DLayerShellWindow.AnchorTop | DLayerShellWindow.AnchorLeft + } + } + DLayerShellWindow.topMargin: dockPosition === Qt.UpArrow ? dockExclusion + : (dockIsHorizontal ? 0 : iconAlignOffset) + DLayerShellWindow.leftMargin: dockPosition === Qt.LeftArrow ? dockExclusion + : (dockIsHorizontal ? iconAlignOffset : 0) + DLayerShellWindow.rightMargin: dockPosition === Qt.RightArrow ? dockExclusion : 0 + DLayerShellWindow.bottomMargin: dockPosition === Qt.DownArrow ? dockExclusion : 0 + DLayerShellWindow.keyboardInteractivity: DLayerShellWindow.KeyboardInteractivityOnDemand + + flags: Qt.Window | Qt.FramelessWindowHint + DWindow.enabled: true + DWindow.windowRadius: D.DTK.platformTheme.windowRadius < 0 ? 12 : D.DTK.platformTheme.windowRadius + DWindow.enableSystemResize: false + DWindow.enableSystemMove: false + DWindow.enableBlurWindow: true + DWindow.shadowOffset: Qt.point(0, 25) + DWindow.shadowColor: isDarkTheme ? Qt.rgba(0, 0, 0, 0.5) : Qt.rgba(0, 0, 0, 0.2) + D.ColorSelector.family: D.Palette.CrystalColor + + StyledBehindWindowBlur { + control: parent + anchors.fill: parent + blendColor: { + const appearance = DS.applet("org.deepin.ds.dde-appearance") + const alpha = appearance && appearance.opacity >= 0 ? appearance.opacity : 0.6 + if (valid) { + return DStyle.Style.control.selectColor(undefined, + Qt.rgba(235 / 255.0, 235 / 255.0, 235 / 255.0, alpha), + Qt.rgba(0, 0, 0, 85 / 255)) + } + return DStyle.Style.control.selectColor(undefined, + DStyle.Style.behindWindowBlur.lightNoBlurColor, + DStyle.Style.behindWindowBlur.darkNoBlurColor) + } + } + + D.InsideBoxBorder { + anchors.fill: parent + radius: WindowManagerHelper.hasComposite ? windowedModeLauncher.DWindow.windowRadius : 0 + color: windowedModeLauncher.isDarkTheme ? Qt.rgba(1, 1, 1, 0.15) : Qt.rgba(1, 1, 1, 0.55) + } WindowedFrame { anchors.fill: parent } - onVisibilityChanged: function() { - if (visibility) { - if (!windowedModeLauncher.visible) { - windowedModeLauncher.open() - } - } else { - windowedModeLauncher.close() + onVisibleChanged: { + if (visible) { + assignDockScreen(windowedModeLauncher) + requestActivate() + LauncherController.closeAllPopups() } } - onPopupVisibleChanged: function() { - if (LauncherController.currentFrame !== "WindowedFrame") return - if (popupVisible !== visibility) { - LauncherController.visible = popupVisible + + onActiveChanged: { + if (LauncherController.currentFrame !== "WindowedFrame") + return + if (active) { + LauncherController.cancelHide() + } else if (!DebugHelper.avoidHideWindow) { + LauncherController.hideWithTimer() } } + + onClosing: { + if (LauncherController.currentFrame === "WindowedFrame") + LauncherController.visible = false + } } DialogWindow {