From 5c2b12bf96af9ca0cafacf46bbc58c1cb2a5d348 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:43:58 +0200 Subject: [PATCH] dock: join pinned apps and open windows --- .config/quickshell/modules/dock/Dock.qml | 20 ---- .../quickshell/modules/dock/DockAppButton.qml | 112 +++++++++++------- .config/quickshell/modules/dock/DockApps.qml | 40 +++++-- 3 files changed, 99 insertions(+), 73 deletions(-) diff --git a/.config/quickshell/modules/dock/Dock.qml b/.config/quickshell/modules/dock/Dock.qml index bd0ac8ec7..25846ac5f 100644 --- a/.config/quickshell/modules/dock/Dock.qml +++ b/.config/quickshell/modules/dock/Dock.qml @@ -120,26 +120,6 @@ Scope { // Scope } } DockSeparator {} - // Pinned apps - Repeater { - model: ConfigOptions?.dock.pinnedApps ?? [] - - DockButton { - id: pinnedAppButton - required property string modelData - property DesktopEntry entry: DesktopEntries.byId(modelData) - onClicked: { - pinnedAppButton?.entry.execute(); - } - contentItem: IconImage { - anchors.centerIn: parent - source: Quickshell.iconPath(AppSearch.guessIcon(modelData), "image-missing") - } - } - } - - DockSeparator { visible: (ConfigOptions?.dock.pinnedApps ?? []).length > 0 } - DockApps { id: dockApps } DockSeparator {} DockButton { diff --git a/.config/quickshell/modules/dock/DockAppButton.qml b/.config/quickshell/modules/dock/DockAppButton.qml index 9df44c853..2304fe158 100644 --- a/.config/quickshell/modules/dock/DockAppButton.qml +++ b/.config/quickshell/modules/dock/DockAppButton.qml @@ -14,8 +14,8 @@ import Quickshell.Wayland import Quickshell.Hyprland DockButton { - id: appButton - required property var appToplevel + id: root + property var appToplevel property var appListRoot property int lastFocused: -1 property real iconSize: 35 @@ -23,56 +23,86 @@ DockButton { property real countDotHeight: 4 property bool appIsActive: appToplevel.toplevels.find(t => (t.activated == true)) !== undefined - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton - onEntered: { - appListRoot.lastHoveredButton = appButton - appListRoot.buttonHovered = true - lastFocused = appToplevel.toplevels.length - 1 + property bool isSeparator: appToplevel.appId === "SEPARATOR" + enabled: !isSeparator + implicitWidth: isSeparator ? 1 : implicitHeight - topInset - bottomInset + + Loader { + active: isSeparator + anchors { + fill: parent + topMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal + bottomMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal } - onExited: { - if (appListRoot.lastHoveredButton === appButton) { - appListRoot.buttonHovered = false + sourceComponent: DockSeparator {} + } + + Loader { + anchors.fill: parent + active: appToplevel.toplevels.length > 0 + sourceComponent: MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.NoButton + onEntered: { + appListRoot.lastHoveredButton = root + appListRoot.buttonHovered = true + lastFocused = appToplevel.toplevels.length - 1 + } + onExited: { + if (appListRoot.lastHoveredButton === root) { + appListRoot.buttonHovered = false + } } } } + onClicked: { + if (appToplevel.toplevels.length === 0) { + DesktopEntries.byId(root.appToplevel.appId)?.execute(); + return; + } lastFocused = (lastFocused + 1) % appToplevel.toplevels.length appToplevel.toplevels[lastFocused].activate() } - contentItem: Item { - anchors.centerIn: parent - IconImage { - id: iconImage - anchors { - left: parent.left - right: parent.right - verticalCenter: parent.verticalCenter - } - source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing") - implicitSize: appButton.iconSize - } + contentItem: Loader { + active: !isSeparator + sourceComponent: Item { + anchors.centerIn: parent - RowLayout { - spacing: 3 - anchors { - top: iconImage.bottom - topMargin: 2 - horizontalCenter: parent.horizontalCenter + Loader { + id: iconImageLoader + anchors { + left: parent.left + right: parent.right + verticalCenter: parent.verticalCenter + } + active: !root.isSeparator + sourceComponent: IconImage { + source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing") + implicitSize: root.iconSize + } } - Repeater { - model: Math.min(appToplevel.toplevels.length, 3) - delegate: Rectangle { - required property int index - radius: Appearance.rounding.full - implicitWidth: (appToplevel.toplevels.length <= 3) ? - appButton.countDotWidth : appButton.countDotHeight // Circles when too many - implicitHeight: appButton.countDotHeight - color: appIsActive ? Appearance.m3colors.m3primary : ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4) + + RowLayout { + spacing: 3 + anchors { + top: iconImageLoader.bottom + topMargin: 2 + horizontalCenter: parent.horizontalCenter + } + Repeater { + model: Math.min(appToplevel.toplevels.length, 3) + delegate: Rectangle { + required property int index + radius: Appearance.rounding.full + implicitWidth: (appToplevel.toplevels.length <= 3) ? + root.countDotWidth : root.countDotHeight // Circles when too many + implicitHeight: root.countDotHeight + color: appIsActive ? Appearance.m3colors.m3primary : ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4) + } } } } diff --git a/.config/quickshell/modules/dock/DockApps.qml b/.config/quickshell/modules/dock/DockApps.qml index 9a8db93a9..900db0ca0 100644 --- a/.config/quickshell/modules/dock/DockApps.qml +++ b/.config/quickshell/modules/dock/DockApps.qml @@ -23,8 +23,9 @@ Item { property Item lastHoveredButton property bool buttonHovered: false property bool requestDockShow: previewPopup.show - property real popupX: parentWindow.mapFromItem(root.lastHoveredButton, root.lastHoveredButton.width / 2, root.lastHoveredButton.height / 2).x - implicitWidth / 2 - + property var parentWindow: root.QsWindow + property real popupX: parentWindow?.mapFromItem(root.lastHoveredButton, root.lastHoveredButton?.width / 2, root.lastHoveredButton?.height / 2).x - implicitWidth / 2 + ?? 0 implicitWidth: rowLayout.implicitWidth implicitHeight: rowLayout.implicitHeight @@ -38,15 +39,33 @@ Item { values: { var map = new Map(); + // Pinned apps + const pinnedApps = ConfigOptions?.dock.pinnedApps ?? []; + for (const appId of pinnedApps) { + if (!map.has(appId.toLowerCase())) map.set(appId.toLowerCase(), ({ + pinned: true, + toplevels: [] + })); + } + + // Separator + if (pinnedApps.length > 0) { + map.set("SEPARATOR", { pinned: false, toplevels: [] }); + } + + // Open windows for (const toplevel of ToplevelManager.toplevels.values) { - if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), []); - map.get(toplevel.appId.toLowerCase()).push(toplevel); + if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), ({ + pinned: false, + toplevels: [] + })); + map.get(toplevel.appId.toLowerCase()).toplevels.push(toplevel); } var values = []; for (const [key, value] of map) { - values.push({ appId: key, toplevels: value }); + values.push({ appId: key, toplevels: value.toplevels, pinned: value.pinned }); } return values; @@ -118,14 +137,9 @@ Item { anchors.bottom: parent.bottom implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2 implicitHeight: root.maxWindowPreviewHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2 - // anchors.horizontalCenter: parent.horizontalCenter hoverEnabled: true - // x: previewPopup.width / 2 + root.popupX - // Behavior on x { - // animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - // } x: { - const itemCenter = root.QsWindow.mapFromItem(root.lastHoveredButton, root.lastHoveredButton.width / 2, 0); + const itemCenter = root.QsWindow?.mapFromItem(root.lastHoveredButton, root.lastHoveredButton?.width / 2, 0); return itemCenter.x - width / 2 } StyledRectangularShadow { @@ -163,7 +177,9 @@ Item { id: previewRowLayout anchors.centerIn: parent Repeater { - model: previewPopup.appTopLevel?.toplevels ?? [] + model: ScriptModel { + values: previewPopup.appTopLevel?.toplevels ?? [] + } RippleButton { id: windowButton required property var modelData