dock: join pinned apps and open windows

This commit is contained in:
end-4
2025-06-09 17:43:58 +02:00
parent 4b4364d2a5
commit 5c2b12bf96
3 changed files with 99 additions and 73 deletions
-20
View File
@@ -120,26 +120,6 @@ Scope { // Scope
} }
} }
DockSeparator {} 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 } DockApps { id: dockApps }
DockSeparator {} DockSeparator {}
DockButton { DockButton {
@@ -14,8 +14,8 @@ import Quickshell.Wayland
import Quickshell.Hyprland import Quickshell.Hyprland
DockButton { DockButton {
id: appButton id: root
required property var appToplevel property var appToplevel
property var appListRoot property var appListRoot
property int lastFocused: -1 property int lastFocused: -1
property real iconSize: 35 property real iconSize: 35
@@ -23,56 +23,86 @@ DockButton {
property real countDotHeight: 4 property real countDotHeight: 4
property bool appIsActive: appToplevel.toplevels.find(t => (t.activated == true)) !== undefined property bool appIsActive: appToplevel.toplevels.find(t => (t.activated == true)) !== undefined
MouseArea { property bool isSeparator: appToplevel.appId === "SEPARATOR"
id: mouseArea enabled: !isSeparator
anchors.fill: parent implicitWidth: isSeparator ? 1 : implicitHeight - topInset - bottomInset
hoverEnabled: true
acceptedButtons: Qt.NoButton Loader {
onEntered: { active: isSeparator
appListRoot.lastHoveredButton = appButton anchors {
appListRoot.buttonHovered = true fill: parent
lastFocused = appToplevel.toplevels.length - 1 topMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal
bottomMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal
} }
onExited: { sourceComponent: DockSeparator {}
if (appListRoot.lastHoveredButton === appButton) { }
appListRoot.buttonHovered = false
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: { onClicked: {
if (appToplevel.toplevels.length === 0) {
DesktopEntries.byId(root.appToplevel.appId)?.execute();
return;
}
lastFocused = (lastFocused + 1) % appToplevel.toplevels.length lastFocused = (lastFocused + 1) % appToplevel.toplevels.length
appToplevel.toplevels[lastFocused].activate() appToplevel.toplevels[lastFocused].activate()
} }
contentItem: Item {
anchors.centerIn: parent
IconImage { contentItem: Loader {
id: iconImage active: !isSeparator
anchors { sourceComponent: Item {
left: parent.left anchors.centerIn: parent
right: parent.right
verticalCenter: parent.verticalCenter
}
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing")
implicitSize: appButton.iconSize
}
RowLayout { Loader {
spacing: 3 id: iconImageLoader
anchors { anchors {
top: iconImage.bottom left: parent.left
topMargin: 2 right: parent.right
horizontalCenter: parent.horizontalCenter 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) RowLayout {
delegate: Rectangle { spacing: 3
required property int index anchors {
radius: Appearance.rounding.full top: iconImageLoader.bottom
implicitWidth: (appToplevel.toplevels.length <= 3) ? topMargin: 2
appButton.countDotWidth : appButton.countDotHeight // Circles when too many horizontalCenter: parent.horizontalCenter
implicitHeight: appButton.countDotHeight }
color: appIsActive ? Appearance.m3colors.m3primary : ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4) 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)
}
} }
} }
} }
+28 -12
View File
@@ -23,8 +23,9 @@ Item {
property Item lastHoveredButton property Item lastHoveredButton
property bool buttonHovered: false property bool buttonHovered: false
property bool requestDockShow: previewPopup.show 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 implicitWidth: rowLayout.implicitWidth
implicitHeight: rowLayout.implicitHeight implicitHeight: rowLayout.implicitHeight
@@ -38,15 +39,33 @@ Item {
values: { values: {
var map = new Map(); 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) { for (const toplevel of ToplevelManager.toplevels.values) {
if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), []); if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), ({
map.get(toplevel.appId.toLowerCase()).push(toplevel); pinned: false,
toplevels: []
}));
map.get(toplevel.appId.toLowerCase()).toplevels.push(toplevel);
} }
var values = []; var values = [];
for (const [key, value] of map) { for (const [key, value] of map) {
values.push({ appId: key, toplevels: value }); values.push({ appId: key, toplevels: value.toplevels, pinned: value.pinned });
} }
return values; return values;
@@ -118,14 +137,9 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2 implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2
implicitHeight: root.maxWindowPreviewHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2 implicitHeight: root.maxWindowPreviewHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2
// anchors.horizontalCenter: parent.horizontalCenter
hoverEnabled: true hoverEnabled: true
// x: previewPopup.width / 2 + root.popupX
// Behavior on x {
// animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
// }
x: { 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 return itemCenter.x - width / 2
} }
StyledRectangularShadow { StyledRectangularShadow {
@@ -163,7 +177,9 @@ Item {
id: previewRowLayout id: previewRowLayout
anchors.centerIn: parent anchors.centerIn: parent
Repeater { Repeater {
model: previewPopup.appTopLevel?.toplevels ?? [] model: ScriptModel {
values: previewPopup.appTopLevel?.toplevels ?? []
}
RippleButton { RippleButton {
id: windowButton id: windowButton
required property var modelData required property var modelData