1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-10 07:28:30 -04:00

dock/apps: support minimize

This commit is contained in:
bbedward
2026-08-09 17:08:43 -04:00
parent 80bad610ce
commit b0b9b61593
8 changed files with 242 additions and 50 deletions
@@ -350,7 +350,7 @@ Item {
}
const nextWindow = windows[nextIndex];
if (nextWindow) {
nextWindow.activate();
CompositorService.activateToplevel(nextWindow);
}
}
@@ -569,6 +569,14 @@ BasePill {
return modelData.toplevel ? modelData.toplevel.activated : false;
}
readonly property bool isMinimized: {
if (!CompositorService.supportsMinimize)
return false;
if (modelData.type === "grouped")
return modelData.allWindows.length > 0 && modelData.allWindows.every(w => w.toplevel && w.toplevel.minimized);
return modelData.toplevel?.minimized === true;
}
property var appId: modelData.appId
property int windowCount: modelData.windowCount || (modelData.isRunning ? 1 : 0)
property string windowTitle: {
@@ -698,6 +706,7 @@ BasePill {
mipmap: true
asynchronous: true
visible: status === Image.Ready && !coreIcon.visible
opacity: appItem.isMinimized ? 0.4 : 1
layer.enabled: appItem.appId === "org.quickshell" || appItem.appId === "com.danklinux.dms"
layer.smooth: true
layer.mipmap: true
@@ -730,6 +739,7 @@ BasePill {
name: "sports_esports"
color: Theme.widgetTextColor
visible: !iconImg.visible && !coreIcon.visible && Paths.isSteamApp(appItem.appId)
opacity: appItem.isMinimized ? 0.4 : 1
transformOrigin: Item.Center
scale: appItem.enlargeScale
@@ -754,6 +764,7 @@ BasePill {
}
font.pixelSize: 10
color: Theme.widgetTextColor
opacity: appItem.isMinimized ? 0.4 : 1
transformOrigin: Item.Center
scale: appItem.enlargeScale
@@ -884,8 +895,7 @@ BasePill {
SessionService.launchDesktopEntry(desktopEntry);
}
} else if (modelData.windowCount === 1) {
if (modelData.allWindows[0].toplevel)
modelData.allWindows[0].toplevel.activate();
CompositorService.toggleToplevel(modelData.allWindows[0].toplevel);
} else {
let currentIndex = -1;
for (var i = 0; i < modelData.allWindows.length; i++) {
@@ -895,7 +905,7 @@ BasePill {
}
}
const nextIndex = (currentIndex + 1) % modelData.allWindows.length;
modelData.allWindows[nextIndex].toplevel.activate();
CompositorService.activateToplevel(modelData.allWindows[nextIndex].toplevel);
}
}
}
@@ -171,7 +171,7 @@ PanelWindow {
}
Rectangle {
implicitWidth: Theme.spacingS + windowTitle.implicitWidth + Theme.spacingXS + closeButton.width + Theme.spacingXS
implicitWidth: Theme.spacingS + windowTitle.implicitWidth + Theme.spacingXS + (minimizeButton.visible ? minimizeButton.width + 2 : 0) + closeButton.width + Theme.spacingXS
width: parent.width
height: 28
radius: Theme.cornerRadius
@@ -181,7 +181,7 @@ PanelWindow {
id: windowTitle
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: closeButton.left
anchors.right: minimizeButton.visible ? minimizeButton.left : closeButton.left
anchors.rightMargin: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
text: (modelData && modelData.title) ? modelData.title : I18n.tr("(Unnamed)")
@@ -192,6 +192,40 @@ PanelWindow {
wrapMode: Text.NoWrap
}
Rectangle {
id: minimizeButton
visible: CompositorService.canMinimize(modelData)
anchors.right: closeButton.left
anchors.rightMargin: 2
anchors.verticalCenter: parent.verticalCenter
width: 20
height: 20
radius: 10
color: minimizeMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
DankIcon {
anchors.centerIn: parent
name: modelData.minimized ? "expand_content" : "minimize"
size: 12
color: Theme.surfaceText
}
MouseArea {
id: minimizeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (modelData.minimized) {
CompositorService.activateToplevel(modelData);
} else {
modelData.minimized = true;
}
root.close();
}
}
}
Rectangle {
id: closeButton
anchors.right: parent.right
@@ -232,14 +266,12 @@ PanelWindow {
MouseArea {
id: windowArea
anchors.fill: parent
anchors.rightMargin: 24
anchors.rightMargin: minimizeButton.visible ? 46 : 24
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => windowRipple.trigger(mouse.x, mouse.y)
onClicked: {
if (modelData && modelData.activate) {
modelData.activate();
}
CompositorService.activateToplevel(modelData);
root.close();
}
}
@@ -187,7 +187,7 @@ BasePill {
const nextWindow = windows[nextIndex];
if (nextWindow)
nextWindow.activate();
CompositorService.activateToplevel(nextWindow);
} else {
scrollAccumulator += deltaY;
@@ -209,7 +209,7 @@ BasePill {
const nextWindow = windows[nextIndex];
if (nextWindow)
nextWindow.activate();
CompositorService.activateToplevel(nextWindow);
scrollAccumulator = 0;
}
@@ -261,6 +261,13 @@ BasePill {
property string windowTitle: toplevelData ? (toplevelData.title || "(Unnamed)") : "(Unnamed)"
property var toplevelObject: toplevelData
property int windowCount: isGrouped ? modelData.windows.length : 1
readonly property bool isMinimized: {
if (!CompositorService.supportsMinimize)
return false;
if (isGrouped)
return groupData.windows.length > 0 && groupData.windows.every(w => w.toplevel.minimized);
return toplevelObject?.minimized === true;
}
property string tooltipText: {
root._desktopEntriesUpdateTrigger;
const desktopEntry = effectiveAppId ? DesktopEntries.heuristicLookup(effectiveAppId) : null;
@@ -309,6 +316,7 @@ BasePill {
mipmap: true
asynchronous: true
visible: status === Image.Ready
opacity: delegateItem.isMinimized ? 0.4 : 1
layer.enabled: appId === "org.quickshell" || appId === "com.danklinux.dms"
layer.smooth: true
layer.mipmap: true
@@ -327,6 +335,7 @@ BasePill {
name: "sports_esports"
color: Theme.widgetTextColor
visible: !iconImg.visible && Paths.isSteamApp(effectiveAppId)
opacity: delegateItem.isMinimized ? 0.4 : 1
}
StyledText {
@@ -343,6 +352,7 @@ BasePill {
}
font.pixelSize: 10
color: Theme.widgetTextColor
opacity: delegateItem.isMinimized ? 0.4 : 1
}
Rectangle {
@@ -406,9 +416,9 @@ BasePill {
}
}
const nextIndex = (currentIndex + 1) % groupData.windows.length;
groupData.windows[nextIndex].toplevel.activate();
CompositorService.activateToplevel(groupData.windows[nextIndex].toplevel);
} else if (toplevelObject) {
toplevelObject.activate();
CompositorService.toggleToplevel(toplevelObject);
}
} else if (mouse.button === Qt.RightButton) {
if (tooltipLoader.item) {
@@ -433,7 +443,7 @@ BasePill {
const localPos = delegateItem.mapToItem(null, delegateItem.width / 2, 0);
const screenHeight = root.parentScreen ? root.parentScreen.height : Screen.height;
const isBottom = root.axis?.edge === "bottom";
const yPos = isBottom ? (screenHeight - root.barThickness - root.barSpacing - 32 - Theme.spacingXS) : (root.barThickness + root.barSpacing + Theme.spacingXS);
const yPos = isBottom ? (screenHeight - root.barThickness - root.barSpacing - windowContextMenuLoader.item.menuHeight - Theme.spacingXS) : (root.barThickness + root.barSpacing + Theme.spacingXS);
windowContextMenuLoader.item.showAt(localPos.x, yPos, false, root.axis?.edge);
}
}
@@ -506,6 +516,13 @@ BasePill {
property string windowTitle: toplevelData ? (toplevelData.title || "(Unnamed)") : "(Unnamed)"
property var toplevelObject: toplevelData
property int windowCount: isGrouped ? modelData.windows.length : 1
readonly property bool isMinimized: {
if (!CompositorService.supportsMinimize)
return false;
if (isGrouped)
return groupData.windows.length > 0 && groupData.windows.every(w => w.toplevel.minimized);
return toplevelObject?.minimized === true;
}
property string tooltipText: {
root._desktopEntriesUpdateTrigger;
const desktopEntry = effectiveAppId ? DesktopEntries.heuristicLookup(effectiveAppId) : null;
@@ -553,6 +570,7 @@ BasePill {
mipmap: true
asynchronous: true
visible: status === Image.Ready
opacity: delegateItem.isMinimized ? 0.4 : 1
layer.enabled: appId === "org.quickshell" || appId === "com.danklinux.dms"
layer.smooth: true
layer.mipmap: true
@@ -571,6 +589,7 @@ BasePill {
name: "sports_esports"
color: Theme.widgetTextColor
visible: !iconImg.visible && Paths.isSteamApp(effectiveAppId)
opacity: delegateItem.isMinimized ? 0.4 : 1
}
StyledText {
@@ -587,6 +606,7 @@ BasePill {
}
font.pixelSize: 10
color: Theme.widgetTextColor
opacity: delegateItem.isMinimized ? 0.4 : 1
}
Rectangle {
@@ -650,9 +670,9 @@ BasePill {
}
}
const nextIndex = (currentIndex + 1) % groupData.windows.length;
groupData.windows[nextIndex].toplevel.activate();
CompositorService.activateToplevel(groupData.windows[nextIndex].toplevel);
} else if (toplevelObject) {
toplevelObject.activate();
CompositorService.toggleToplevel(toplevelObject);
}
} else if (mouse.button === Qt.RightButton) {
if (tooltipLoader.item) {
@@ -677,7 +697,7 @@ BasePill {
const localPos = delegateItem.mapToItem(null, delegateItem.width / 2, 0);
const screenHeight = root.parentScreen ? root.parentScreen.height : Screen.height;
const isBottom = root.axis?.edge === "bottom";
const yPos = isBottom ? (screenHeight - root.barThickness - root.barSpacing - 32 - Theme.spacingXS) : (root.barThickness + root.barSpacing + Theme.spacingXS);
const yPos = isBottom ? (screenHeight - root.barThickness - root.barSpacing - windowContextMenuLoader.item.menuHeight - Theme.spacingXS) : (root.barThickness + root.barSpacing + Theme.spacingXS);
windowContextMenuLoader.item.showAt(localPos.x, yPos, false, root.axis?.edge);
}
}
@@ -742,6 +762,7 @@ BasePill {
}
property var currentWindow: null
readonly property real menuHeight: contextMenuRect.height
property bool isVisible: false
property point anchorPos: Qt.point(0, 0)
property bool isVertical: false
@@ -858,19 +879,60 @@ BasePill {
return contextMenuWindow.anchorPos.y;
}
}
width: 100
height: 32
width: 120
height: menuColumn.height + Theme.spacingXS * 2
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
radius: Theme.cornerRadius
border.width: BlurService.borderWidth
border.color: BlurService.borderColor
Column {
id: menuColumn
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Theme.spacingXS
width: parent.width - Theme.spacingXS * 2
spacing: 1
Rectangle {
anchors.fill: parent
radius: parent.radius
color: closeMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(BlurService.hoverColor(Theme.widgetBaseHoverColor), 0)
visible: CompositorService.canMinimize(contextMenuWindow.currentWindow)
width: parent.width
height: 28
radius: Theme.cornerRadius
color: minimizeMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
StyledText {
anchors.centerIn: parent
text: contextMenuWindow.currentWindow?.minimized ? I18n.tr("Restore") : I18n.tr("Minimize")
font.pixelSize: Theme.fontSizeSmall
color: Theme.widgetTextColor
}
MouseArea {
id: minimizeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
const targetWindow = contextMenuWindow.currentWindow;
if (targetWindow) {
if (targetWindow.minimized) {
CompositorService.activateToplevel(targetWindow);
} else {
targetWindow.minimized = true;
}
}
contextMenuWindow.close();
}
}
}
Rectangle {
width: parent.width
height: 28
radius: Theme.cornerRadius
color: closeMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
StyledText {
anchors.centerIn: parent
text: I18n.tr("Close")
@@ -894,3 +956,5 @@ BasePill {
}
}
}
}
}
+23 -3
View File
@@ -107,6 +107,23 @@ Item {
return false;
}
readonly property bool isMinimized: {
if (!CompositorService.supportsMinimize || !appData) {
return false;
}
switch (appData.type) {
case "window":
return getToplevelObject()?.minimized === true;
case "grouped":
{
const toplevels = getGroupedToplevels();
return toplevels.length > 0 && toplevels.every(t => t.minimized);
}
default:
return false;
}
}
property string tooltipText: {
if (!appData || !appData.appId) {
return "";
@@ -185,7 +202,7 @@ Item {
const nextToplevel = toplevels[(currentIndex + 1) % toplevels.length];
if (restoreSpecialWorkspaceWindow(nextToplevel))
return;
nextToplevel.activate();
CompositorService.activateToplevel(nextToplevel);
}
onIsHoveredChanged: {
if (mouseArea.pressed || dragging)
@@ -333,7 +350,7 @@ Item {
if (windowToplevel) {
if (restoreSpecialWorkspaceWindow(windowToplevel))
return;
windowToplevel.activate();
CompositorService.toggleToplevel(windowToplevel);
}
break;
case "grouped":
@@ -360,7 +377,7 @@ Item {
if (groupedToplevel) {
if (restoreSpecialWorkspaceWindow(groupedToplevel))
return;
groupedToplevel.activate();
CompositorService.toggleToplevel(groupedToplevel);
}
} else {
cycleGroupedToplevels();
@@ -503,6 +520,7 @@ Item {
smooth: true
asynchronous: true
visible: status === Image.Ready && !coreIcon.visible
opacity: root.isMinimized ? 0.4 : 1
layer.enabled: appData && (appData.appId === "org.quickshell" || appData.appId === "com.danklinux.dms")
layer.smooth: true
layer.mipmap: true
@@ -518,6 +536,7 @@ Item {
height: actualIconSize
anchors.centerIn: parent
visible: !coreIcon.visible && iconImg.status !== Image.Ready && appData && appData.appId && !Paths.isSteamApp(appData.appId)
opacity: root.isMinimized ? 0.4 : 1
color: Theme.surfaceLight
radius: Theme.cornerRadius
border.width: 1
@@ -550,6 +569,7 @@ Item {
name: "sports_esports"
color: Theme.surfaceText
visible: !coreIcon.visible && iconImg.status !== Image.Ready && appData && appData.appId && Paths.isSteamApp(appData.appId)
opacity: root.isMinimized ? 0.4 : 1
}
Loader {
+38 -6
View File
@@ -31,7 +31,7 @@ DockContextMenuBase {
}
Rectangle {
implicitWidth: Theme.spacingS + windowTitle.implicitWidth + Theme.spacingXS + closeButton.width + Theme.spacingXS
implicitWidth: Theme.spacingS + windowTitle.implicitWidth + Theme.spacingXS + (minimizeButton.visible ? minimizeButton.width + 2 : 0) + closeButton.width + Theme.spacingXS
width: parent.width
height: 28
radius: Theme.cornerRadius
@@ -41,7 +41,7 @@ DockContextMenuBase {
id: windowTitle
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: closeButton.left
anchors.right: minimizeButton.visible ? minimizeButton.left : closeButton.left
anchors.rightMargin: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
text: (modelData && modelData.title) ? modelData.title : I18n.tr("(Unnamed)")
@@ -52,6 +52,40 @@ DockContextMenuBase {
wrapMode: Text.NoWrap
}
Rectangle {
id: minimizeButton
visible: CompositorService.canMinimize(modelData)
anchors.right: closeButton.left
anchors.rightMargin: 2
anchors.verticalCenter: parent.verticalCenter
width: 20
height: 20
radius: 10
color: minimizeMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
DankIcon {
anchors.centerIn: parent
name: modelData.minimized ? "expand_content" : "minimize"
size: 12
color: Theme.surfaceText
}
MouseArea {
id: minimizeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (modelData.minimized) {
CompositorService.activateToplevel(modelData);
} else {
modelData.minimized = true;
}
root.close();
}
}
}
Rectangle {
id: closeButton
anchors.right: parent.right
@@ -92,14 +126,12 @@ DockContextMenuBase {
MouseArea {
id: windowArea
anchors.fill: parent
anchors.rightMargin: 24
anchors.rightMargin: minimizeButton.visible ? 46 : 24
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => windowRipple.trigger(mouse.x, mouse.y)
onClicked: {
if (modelData && modelData.activate) {
modelData.activate();
}
CompositorService.activateToplevel(modelData);
root.close();
}
}
+34
View File
@@ -7,6 +7,7 @@ import Quickshell.I3
import Quickshell.Wayland
import Quickshell.Hyprland
import qs.Common
import qs.DankCommon.Common as DankCommon
import qs.Services
Singleton {
@@ -44,6 +45,39 @@ Singleton {
signal toplevelsChanged
readonly property bool supportsMinimize: DankCommon.Compositor.supportsMinimize
function canMinimize(toplevel) {
return supportsMinimize && toplevel && toplevel.minimized !== undefined;
}
function activateToplevel(toplevel) {
if (!toplevel)
return;
if (canMinimize(toplevel) && toplevel.minimized)
toplevel.minimized = false;
toplevel.activate();
}
function toggleToplevel(toplevel) {
if (!toplevel)
return;
if (!canMinimize(toplevel)) {
toplevel.activate();
return;
}
if (toplevel.minimized) {
toplevel.minimized = false;
toplevel.activate();
return;
}
if (toplevel.activated) {
toplevel.minimized = true;
return;
}
toplevel.activate();
}
function fetchRandrData() {
Proc.runCommand("randr", [Proc.dmsBin, "randr", "--json"], (output, exitCode) => {
if (exitCode === 0 && output) {