From 7b9b34b262fdde8aa447a1caff8048ddadcbf877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Thi=E1=BB=87n=20L=E1=BB=99c?= Date: Thu, 13 Aug 2026 10:30:12 +0700 Subject: [PATCH] feat(dock): separate pinned and running apps (#3025) --- quickshell/Common/SettingsData.qml | 1 + quickshell/Common/settings/SettingsSpec.js | 1 + quickshell/Modules/Dock/DockApps.qml | 66 ++++++++++++++++--- quickshell/Modules/Settings/DockTab.qml | 9 +++ .../translations/settings_search_index.json | 28 ++++++++ 5 files changed, 95 insertions(+), 10 deletions(-) diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 33376ed63..367d234e9 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -862,6 +862,7 @@ Singleton { property bool dockSmartAutoHide: false property bool dockUseOverlayLayer: false property bool dockGroupByApp: false + property bool dockSeparatePinnedAndRunningApps: false property bool dockRestoreSpecialWorkspaceOnClick: false property bool dockOpenOnOverview: false property int dockPosition: SettingsData.Position.Bottom diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index 47e9407a2..0fbdeae6d 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -431,6 +431,7 @@ var SPEC = { dockSmartAutoHide: { def: false }, dockUseOverlayLayer: { def: false }, dockGroupByApp: { def: false }, + dockSeparatePinnedAndRunningApps: { def: false }, dockRestoreSpecialWorkspaceOnClick: { def: false }, dockOpenOnOverview: { def: false }, dockPosition: { def: 1 }, diff --git a/quickshell/Modules/Dock/DockApps.qml b/quickshell/Modules/Dock/DockApps.qml index 63e7c8526..342997099 100644 --- a/quickshell/Modules/Dock/DockApps.qml +++ b/quickshell/Modules/Dock/DockApps.qml @@ -159,18 +159,21 @@ Item { function buildGroupedItems(pinnedApps, sortedToplevels) { const items = []; const appGroups = new Map(); + const separatePinnedAndRunning = SettingsData.dockSeparatePinnedAndRunningApps; - pinnedApps.forEach(rawAppId => { - const appId = Paths.moddedAppId(rawAppId); - const coreAppData = getCoreAppData(appId); - appGroups.set(appId, { - appId: appId, - isPinned: true, - windows: [], - isCoreApp: coreAppData !== null, - coreAppData: coreAppData + if (!separatePinnedAndRunning) { + pinnedApps.forEach(rawAppId => { + const appId = Paths.moddedAppId(rawAppId); + const coreAppData = getCoreAppData(appId); + appGroups.set(appId, { + appId: appId, + isPinned: true, + windows: [], + isCoreApp: coreAppData !== null, + coreAppData: coreAppData + }); }); - }); + } sortedToplevels.forEach((toplevel, index) => { const rawAppId = toplevel.appId || "unknown"; @@ -219,6 +222,24 @@ Item { (group.isPinned ? pinnedGroups : unpinnedGroups).push(item); }); + if (separatePinnedAndRunning) { + pinnedApps.forEach(rawAppId => { + const appId = Paths.moddedAppId(rawAppId); + const coreAppData = getCoreAppData(appId); + pinnedGroups.push({ + uniqueKey: "pinned_" + appId, + type: "pinned", + appId: appId, + toplevel: null, + isPinned: true, + isRunning: false, + isCoreApp: coreAppData !== null, + coreAppData: coreAppData, + isInOverflow: false + }); + }); + } + pinnedGroups.forEach(item => items.push(item)); insertLauncher(items); @@ -239,6 +260,7 @@ Item { const items = []; const runningAppIds = new Set(); const windowItems = []; + const separatePinnedAndRunning = SettingsData.dockSeparatePinnedAndRunningApps; sortedToplevels.forEach((toplevel, index) => { let uniqueKey = "window_" + index; @@ -283,6 +305,21 @@ Item { pinnedApps.forEach(rawAppId => { const appId = Paths.moddedAppId(rawAppId); const coreAppData = getCoreAppData(appId); + if (separatePinnedAndRunning) { + items.push({ + uniqueKey: "pinned_" + appId, + type: "pinned", + appId: appId, + toplevel: null, + isPinned: true, + isRunning: false, + isCoreApp: coreAppData !== null, + coreAppData: coreAppData, + isInOverflow: false + }); + return; + } + const matchIndex = remainingWindowItems.findIndex(item => item.appId === appId); if (matchIndex !== -1) { @@ -688,6 +725,15 @@ Item { function onDockMaxVisibleRunningAppsChanged() { repeater.updateModel(); } + function onDockSeparatePinnedAndRunningAppsChanged() { + root.suppressShiftAnimation = true; + root.draggedIndex = -1; + root.dropTargetIndex = -1; + repeater.updateModel(); + Qt.callLater(() => { + root.suppressShiftAnimation = false; + }); + } function onDockShowTrashChanged() { repeater.updateModel(); } diff --git a/quickshell/Modules/Settings/DockTab.qml b/quickshell/Modules/Settings/DockTab.qml index 83efed8f9..1aa7e1da8 100644 --- a/quickshell/Modules/Settings/DockTab.qml +++ b/quickshell/Modules/Settings/DockTab.qml @@ -174,6 +174,15 @@ Item { onToggled: checked => SettingsData.set("dockGroupByApp", checked) } + SettingsToggleRow { + settingKey: "dockSeparatePinnedAndRunningApps" + tags: ["dock", "pinned", "running", "launcher", "apps"] + text: I18n.tr("Separate Pinned and Running Apps") + description: I18n.tr("Keep pinned apps as launchers and show running windows separately") + checked: SettingsData.dockSeparatePinnedAndRunningApps + onToggled: checked => SettingsData.set("dockSeparatePinnedAndRunningApps", checked) + } + SettingsToggleRow { settingKey: "dockRestoreSpecialWorkspaceOnClick" tags: ["dock", "hyprland", "special", "workspace", "restore"] diff --git a/quickshell/translations/settings_search_index.json b/quickshell/translations/settings_search_index.json index eaf8fe727..e2bd50432 100644 --- a/quickshell/translations/settings_search_index.json +++ b/quickshell/translations/settings_search_index.json @@ -2144,6 +2144,34 @@ "description": "When clicking a dock window in a Hyprland special workspace, bring that special workspace back before focusing the window", "conditionKey": "isHyprland" }, + { + "section": "dockSeparatePinnedAndRunningApps", + "label": "Separate Pinned and Running Apps", + "tabIndex": 5, + "category": "Dock", + "keywords": [ + "active", + "app drawer", + "app menu", + "applications", + "apps", + "dock", + "keep", + "launcher", + "launcher bar", + "launchers", + "open", + "pinned", + "running", + "separate", + "separately", + "show", + "start menu", + "taskbar", + "windows" + ], + "description": "Keep pinned apps as launchers and show running windows separately" + }, { "section": "showDock", "label": "Show Dock",