From 7241877995cd6f60774c966dbbd269ab8c4ada91 Mon Sep 17 00:00:00 2001 From: purian23 Date: Sat, 17 Jan 2026 22:20:15 -0500 Subject: [PATCH] feat: Intelligent Dock Auto-hide --- quickshell/Common/SettingsData.qml | 1 + quickshell/Common/settings/SettingsSpec.js | 1 + quickshell/Modules/Dock/Dock.qml | 139 ++++++++++++++++++++- quickshell/Modules/Settings/DockTab.qml | 24 +++- quickshell/translations/en.json | 12 ++ quickshell/translations/template.json | 14 +++ 6 files changed, 188 insertions(+), 3 deletions(-) diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 30258e9f..45e56927 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -366,6 +366,7 @@ Singleton { property bool showDock: false property bool dockAutoHide: false + property bool dockSmartAutoHide: false property bool dockGroupByApp: 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 77693d13..ecab83d5 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -231,6 +231,7 @@ var SPEC = { showDock: { def: false }, dockAutoHide: { def: false }, + dockSmartAutoHide: { def: false }, dockGroupByApp: { def: false }, dockOpenOnOverview: { def: false }, dockPosition: { def: 1 }, diff --git a/quickshell/Modules/Dock/Dock.qml b/quickshell/Modules/Dock/Dock.qml index 497ef829..ae13b4a8 100644 --- a/quickshell/Modules/Dock/Dock.qml +++ b/quickshell/Modules/Dock/Dock.qml @@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Shapes import Quickshell +import Quickshell.Hyprland import Quickshell.Wayland import qs.Common import qs.Services @@ -28,7 +29,7 @@ Variants { } property var modelData: item - property bool autoHide: SettingsData.dockAutoHide + property bool autoHide: SettingsData.dockAutoHide || SettingsData.dockSmartAutoHide property real backgroundTransparency: SettingsData.dockTransparency property bool groupByApp: SettingsData.dockGroupByApp readonly property int borderThickness: SettingsData.dockBorderEnabled ? SettingsData.dockBorderThickness : 0 @@ -111,6 +112,133 @@ Variants { property bool contextMenuOpen: (dockVariants.contextMenu && dockVariants.contextMenu.visible && dockVariants.contextMenu.screen === modelData) property bool revealSticky: false + readonly property bool shouldHideForWindows: { + if (!SettingsData.dockSmartAutoHide) + return false; + if (!CompositorService.isNiri && !CompositorService.isHyprland) + return false; + + const screenName = dock.modelData?.name ?? ""; + const dockThickness = effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin; + const screenWidth = dock.screen?.width ?? 0; + const screenHeight = dock.screen?.height ?? 0; + + if (CompositorService.isNiri) { + NiriService.windows; + + let currentWorkspaceId = null; + for (let i = 0; i < NiriService.allWorkspaces.length; i++) { + const ws = NiriService.allWorkspaces[i]; + if (ws.output === screenName && ws.is_active) { + currentWorkspaceId = ws.id; + break; + } + } + + if (currentWorkspaceId === null) + return false; + + for (let i = 0; i < NiriService.windows.length; i++) { + const win = NiriService.windows[i]; + if (win.workspace_id !== currentWorkspaceId) + continue; + + // Get window position and size from layout data + const tilePos = win.layout?.tile_pos_in_workspace_view; + const winSize = win.layout?.window_size || win.layout?.tile_size; + + if (tilePos && winSize) { + const winX = tilePos[0]; + const winY = tilePos[1]; + const winW = winSize[0]; + const winH = winSize[1]; + + switch (SettingsData.dockPosition) { + case SettingsData.Position.Top: + if (winY < dockThickness) + return true; + break; + case SettingsData.Position.Bottom: + if (winY + winH > screenHeight - dockThickness) + return true; + break; + case SettingsData.Position.Left: + if (winX < dockThickness) + return true; + break; + case SettingsData.Position.Right: + if (winX + winW > screenWidth - dockThickness) + return true; + break; + } + } else if (!win.is_floating) { + return true; + } + } + + return false; + } + + // Hyprland implementation + const filtered = CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, screenName); + + if (filtered.length === 0) + return false; + + for (let i = 0; i < filtered.length; i++) { + const toplevel = filtered[i]; + + let hyprToplevel = null; + if (Hyprland.toplevels) { + const hyprToplevels = Array.from(Hyprland.toplevels.values); + for (let j = 0; j < hyprToplevels.length; j++) { + if (hyprToplevels[j].wayland === toplevel) { + hyprToplevel = hyprToplevels[j]; + break; + } + } + } + + if (!hyprToplevel?.lastIpcObject) + continue; + + const ipc = hyprToplevel.lastIpcObject; + const at = ipc.at; + const size = ipc.size; + if (!at || !size) + continue; + + const monX = hyprToplevel.monitor?.x ?? 0; + const monY = hyprToplevel.monitor?.y ?? 0; + + const winX = at[0] - monX; + const winY = at[1] - monY; + const winW = size[0]; + const winH = size[1]; + + switch (SettingsData.dockPosition) { + case SettingsData.Position.Top: + if (winY < dockThickness) + return true; + break; + case SettingsData.Position.Bottom: + if (winY + winH > screenHeight - dockThickness) + return true; + break; + case SettingsData.Position.Left: + if (winX < dockThickness) + return true; + break; + case SettingsData.Position.Right: + if (winX + winW > screenWidth - dockThickness) + return true; + break; + } + } + + return false; + } + Timer { id: revealHold interval: 250 @@ -122,6 +250,15 @@ Variants { if (CompositorService.isNiri && NiriService.inOverview && SettingsData.dockOpenOnOverview) { return true; } + + // Smart auto-hide: show dock when no windows overlap, hide when they do + if (SettingsData.dockSmartAutoHide) { + if (shouldHideForWindows) + return dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen || revealSticky; + return true; // No overlapping windows - show dock + } + + // Regular auto-hide: always hide unless hovering return !autoHide || dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen || revealSticky; } diff --git a/quickshell/Modules/Settings/DockTab.qml b/quickshell/Modules/Settings/DockTab.qml index 59789997..dcafdc9d 100644 --- a/quickshell/Modules/Settings/DockTab.qml +++ b/quickshell/Modules/Settings/DockTab.qml @@ -86,10 +86,30 @@ Item { settingKey: "dockAutoHide" tags: ["dock", "autohide", "hide", "hover"] text: I18n.tr("Auto-hide Dock") - description: I18n.tr("Hide the dock when not in use and reveal it when hovering near the dock area") + description: I18n.tr("Always hide the dock and reveal it when hovering near the dock area") checked: SettingsData.dockAutoHide visible: SettingsData.showDock - onToggled: checked => SettingsData.set("dockAutoHide", checked) + onToggled: checked => { + if (checked && SettingsData.dockSmartAutoHide) { + SettingsData.set("dockSmartAutoHide", false); + } + SettingsData.set("dockAutoHide", checked); + } + } + + SettingsToggleRow { + settingKey: "dockSmartAutoHide" + tags: ["dock", "smart", "autohide", "windows", "overlap", "intelligent"] + text: I18n.tr("Intelligent Auto-hide") + description: I18n.tr("Show dock when floating windows don't overlap its area") + checked: SettingsData.dockSmartAutoHide + visible: SettingsData.showDock && (CompositorService.isNiri || CompositorService.isHyprland) + onToggled: checked => { + if (checked && SettingsData.dockAutoHide) { + SettingsData.set("dockAutoHide", false); + } + SettingsData.set("dockSmartAutoHide", checked); + } } SettingsToggleRow { diff --git a/quickshell/translations/en.json b/quickshell/translations/en.json index d887538c..ab2faa7a 100644 --- a/quickshell/translations/en.json +++ b/quickshell/translations/en.json @@ -4265,6 +4265,12 @@ "reference": "Modules/Settings/DankBarTab.qml:1068", "comment": "" }, + { + "term": "Intelligent Auto-hide", + "context": "Intelligent Auto-hide", + "reference": "Modules/Settings/DockTab.qml:103", + "comment": "" + }, { "term": "Interface:", "context": "Interface:", @@ -7415,6 +7421,12 @@ "reference": "Modules/Settings/ThemeColorsTab.qml:1375", "comment": "" }, + { + "term": "Show dock when windows don't overlap its area, hide when they do", + "context": "Show dock when windows don't overlap its area, hide when they do", + "reference": "Modules/Settings/DockTab.qml:104", + "comment": "" + }, { "term": "Show launcher overlay when typing in Niri overview. Disable to use another launcher.", "context": "Show launcher overlay when typing in Niri overview. Disable to use another launcher.", diff --git a/quickshell/translations/template.json b/quickshell/translations/template.json index 112e0ba3..e4c7839c 100644 --- a/quickshell/translations/template.json +++ b/quickshell/translations/template.json @@ -4976,6 +4976,13 @@ "reference": "", "comment": "" }, + { + "term": "Intelligent Auto-hide", + "translation": "", + "context": "Intelligent Auto-hide", + "reference": "", + "comment": "" + }, { "term": "Interface:", "translation": "", @@ -8651,6 +8658,13 @@ "reference": "", "comment": "" }, + { + "term": "Show dock when windows don't overlap its area, hide when they do", + "translation": "", + "context": "", + "reference": "", + "comment": "" + }, { "term": "Show launcher overlay when typing in Niri overview. Disable to use another launcher.", "translation": "",