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 {}
// 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 {
@@ -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)
}
}
}
}
+28 -12
View File
@@ -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