1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

feat: Group by App on Running Apps

This commit is contained in:
purian23
2025-10-17 22:24:47 -04:00
parent f4cd27d316
commit 7c1e247ef8
3 changed files with 151 additions and 14 deletions

View File

@@ -109,6 +109,7 @@ Singleton {
property bool focusedWindowCompactMode: false property bool focusedWindowCompactMode: false
property bool runningAppsCompactMode: true property bool runningAppsCompactMode: true
property bool runningAppsCurrentWorkspace: false property bool runningAppsCurrentWorkspace: false
property bool runningAppsGroupByApp: false
property string clockDateFormat: "" property string clockDateFormat: ""
property string lockDateFormat: "" property string lockDateFormat: ""
property int mediaSize: 1 property int mediaSize: 1
@@ -373,6 +374,7 @@ Singleton {
focusedWindowCompactMode = settings.focusedWindowCompactMode !== undefined ? settings.focusedWindowCompactMode : false focusedWindowCompactMode = settings.focusedWindowCompactMode !== undefined ? settings.focusedWindowCompactMode : false
runningAppsCompactMode = settings.runningAppsCompactMode !== undefined ? settings.runningAppsCompactMode : true runningAppsCompactMode = settings.runningAppsCompactMode !== undefined ? settings.runningAppsCompactMode : true
runningAppsCurrentWorkspace = settings.runningAppsCurrentWorkspace !== undefined ? settings.runningAppsCurrentWorkspace : false runningAppsCurrentWorkspace = settings.runningAppsCurrentWorkspace !== undefined ? settings.runningAppsCurrentWorkspace : false
runningAppsGroupByApp = settings.runningAppsGroupByApp !== undefined ? settings.runningAppsGroupByApp : false
clockDateFormat = settings.clockDateFormat !== undefined ? settings.clockDateFormat : "" clockDateFormat = settings.clockDateFormat !== undefined ? settings.clockDateFormat : ""
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "" lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : ""
mediaSize = settings.mediaSize !== undefined ? settings.mediaSize : (settings.mediaCompactMode !== undefined ? (settings.mediaCompactMode ? 0 : 1) : 1) mediaSize = settings.mediaSize !== undefined ? settings.mediaSize : (settings.mediaCompactMode !== undefined ? (settings.mediaCompactMode ? 0 : 1) : 1)
@@ -576,6 +578,7 @@ Singleton {
"focusedWindowCompactMode": focusedWindowCompactMode, "focusedWindowCompactMode": focusedWindowCompactMode,
"runningAppsCompactMode": runningAppsCompactMode, "runningAppsCompactMode": runningAppsCompactMode,
"runningAppsCurrentWorkspace": runningAppsCurrentWorkspace, "runningAppsCurrentWorkspace": runningAppsCurrentWorkspace,
"runningAppsGroupByApp": runningAppsGroupByApp,
"clockDateFormat": clockDateFormat, "clockDateFormat": clockDateFormat,
"lockDateFormat": lockDateFormat, "lockDateFormat": lockDateFormat,
"mediaSize": mediaSize, "mediaSize": mediaSize,
@@ -701,7 +704,7 @@ Singleton {
"controlCenterWidgets", "showWorkspaceIndex", "workspaceScrolling", "showWorkspacePadding", "showWorkspaceApps", "controlCenterWidgets", "showWorkspaceIndex", "workspaceScrolling", "showWorkspacePadding", "showWorkspaceApps",
"maxWorkspaceIcons", "workspacesPerMonitor", "workspaceNameIcons", "waveProgressEnabled", "maxWorkspaceIcons", "workspacesPerMonitor", "workspaceNameIcons", "waveProgressEnabled",
"clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode",
"runningAppsCurrentWorkspace", "clockDateFormat", "lockDateFormat", "mediaSize", "runningAppsCurrentWorkspace", "runningAppsGroupByApp", "clockDateFormat", "lockDateFormat", "mediaSize",
"dankBarLeftWidgets", "dankBarCenterWidgets", "dankBarRightWidgets", "dankBarLeftWidgets", "dankBarCenterWidgets", "dankBarRightWidgets",
"appLauncherViewMode", "spotlightModalViewMode", "sortAppsAlphabetically", "appLauncherViewMode", "spotlightModalViewMode", "sortAppsAlphabetically",
"networkPreference", "iconTheme", "launcherLogoMode", "launcherLogoCustomPath", "networkPreference", "iconTheme", "launcherLogoMode", "launcherLogoCustomPath",
@@ -1232,6 +1235,11 @@ Singleton {
saveSettings() saveSettings()
} }
function setRunningAppsGroupByApp(enabled) {
runningAppsGroupByApp = enabled
saveSettings()
}
function setClockDateFormat(format) { function setClockDateFormat(format) {
clockDateFormat = format || "" clockDateFormat = format || ""
saveSettings() saveSettings()

View File

@@ -26,7 +26,28 @@ Rectangle {
} }
return CompositorService.sortedToplevels; return CompositorService.sortedToplevels;
} }
readonly property int windowCount: sortedToplevels.length readonly property var groupedWindows: {
if (!SettingsData.runningAppsGroupByApp) {
return [];
}
const appGroups = new Map();
sortedToplevels.forEach((toplevel, index) => {
const appId = toplevel.appId || "unknown";
if (!appGroups.has(appId)) {
appGroups.set(appId, {
appId: appId,
windows: []
});
}
appGroups.get(appId).windows.push({
toplevel: toplevel,
windowId: index,
windowTitle: toplevel.title || "(Unnamed)"
});
});
return Array.from(appGroups.values());
}
readonly property int windowCount: SettingsData.runningAppsGroupByApp ? groupedWindows.length : sortedToplevels.length
readonly property int calculatedSize: { readonly property int calculatedSize: {
if (windowCount === 0) { if (windowCount === 0) {
return 0; return 0;
@@ -158,15 +179,19 @@ Rectangle {
Repeater { Repeater {
id: windowRepeater id: windowRepeater
model: sortedToplevels model: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels
delegate: Item { delegate: Item {
id: delegateItem id: delegateItem
property bool isFocused: modelData.activated property bool isGrouped: SettingsData.runningAppsGroupByApp
property string appId: modelData.appId || "" property var groupData: isGrouped ? modelData : null
property string windowTitle: modelData.title || "(Unnamed)" property var toplevelData: isGrouped ? (modelData.windows.length > 0 ? modelData.windows[0].toplevel : null) : modelData
property var toplevelObject: modelData property bool isFocused: toplevelData ? toplevelData.activated : false
property string appId: isGrouped ? modelData.appId : (modelData.appId || "")
property string windowTitle: toplevelData ? (toplevelData.title || "(Unnamed)") : "(Unnamed)"
property var toplevelObject: toplevelData
property int windowCount: isGrouped ? modelData.windows.length : 1
property string tooltipText: { property string tooltipText: {
let appName = "Unknown"; let appName = "Unknown";
if (appId) { if (appId) {
@@ -174,6 +199,9 @@ Rectangle {
appName = desktopEntry appName = desktopEntry
&& desktopEntry.name ? desktopEntry.name : appId; && desktopEntry.name ? desktopEntry.name : appId;
} }
if (isGrouped && windowCount > 1) {
return appName + " (" + windowCount + " windows)";
}
return appName + (windowTitle ? " • " + windowTitle : "") return appName + (windowTitle ? " • " + windowTitle : "")
} }
@@ -264,6 +292,27 @@ Rectangle {
font.weight: Font.Medium font.weight: Font.Medium
} }
Rectangle {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: SettingsData.runningAppsCompactMode ? -2 : 2
anchors.bottomMargin: -2
width: 14
height: 14
radius: 7
color: Theme.primary
visible: isGrouped && windowCount > 1
z: 10
StyledText {
anchors.centerIn: parent
text: windowCount > 9 ? "9+" : windowCount
font.pixelSize: 9
color: Theme.surface
font.weight: Font.Bold
}
}
// Window title text (only visible in expanded mode) // Window title text (only visible in expanded mode)
StyledText { StyledText {
anchors.left: iconImg.right anchors.left: iconImg.right
@@ -289,7 +338,17 @@ Rectangle {
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => { onClicked: (mouse) => {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
if (toplevelObject) { if (isGrouped && windowCount > 1) {
let currentIndex = -1;
for (let i = 0; i < groupData.windows.length; i++) {
if (groupData.windows[i].toplevel.activated) {
currentIndex = i;
break;
}
}
const nextIndex = (currentIndex + 1) % groupData.windows.length;
groupData.windows[nextIndex].toplevel.activate();
} else if (toplevelObject) {
toplevelObject.activate(); toplevelObject.activate();
} }
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
@@ -352,15 +411,19 @@ Rectangle {
Repeater { Repeater {
id: windowRepeater id: windowRepeater
model: sortedToplevels model: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels
delegate: Item { delegate: Item {
id: delegateItem id: delegateItem
property bool isFocused: modelData.activated property bool isGrouped: SettingsData.runningAppsGroupByApp
property string appId: modelData.appId || "" property var groupData: isGrouped ? modelData : null
property string windowTitle: modelData.title || "(Unnamed)" property var toplevelData: isGrouped ? (modelData.windows.length > 0 ? modelData.windows[0].toplevel : null) : modelData
property var toplevelObject: modelData property bool isFocused: toplevelData ? toplevelData.activated : false
property string appId: isGrouped ? modelData.appId : (modelData.appId || "")
property string windowTitle: toplevelData ? (toplevelData.title || "(Unnamed)") : "(Unnamed)"
property var toplevelObject: toplevelData
property int windowCount: isGrouped ? modelData.windows.length : 1
property string tooltipText: { property string tooltipText: {
let appName = "Unknown"; let appName = "Unknown";
if (appId) { if (appId) {
@@ -368,6 +431,9 @@ Rectangle {
appName = desktopEntry appName = desktopEntry
&& desktopEntry.name ? desktopEntry.name : appId; && desktopEntry.name ? desktopEntry.name : appId;
} }
if (isGrouped && windowCount > 1) {
return appName + " (" + windowCount + " windows)";
}
return appName + (windowTitle ? " • " + windowTitle : "") return appName + (windowTitle ? " • " + windowTitle : "")
} }
@@ -456,6 +522,27 @@ Rectangle {
font.weight: Font.Medium font.weight: Font.Medium
} }
Rectangle {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: SettingsData.runningAppsCompactMode ? -2 : 2
anchors.bottomMargin: -2
width: 14
height: 14
radius: 7
color: Theme.primary
visible: isGrouped && windowCount > 1
z: 10
StyledText {
anchors.centerIn: parent
text: windowCount > 9 ? "9+" : windowCount
font.pixelSize: 9
color: Theme.surface
font.weight: Font.Bold
}
}
StyledText { StyledText {
anchors.left: iconImg.right anchors.left: iconImg.right
anchors.leftMargin: Theme.spacingXS anchors.leftMargin: Theme.spacingXS
@@ -480,7 +567,17 @@ Rectangle {
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => { onClicked: (mouse) => {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
if (toplevelObject) { if (isGrouped && windowCount > 1) {
let currentIndex = -1;
for (let i = 0; i < groupData.windows.length; i++) {
if (groupData.windows[i].toplevel.activated) {
currentIndex = i;
break;
}
}
const nextIndex = (currentIndex + 1) % groupData.windows.length;
groupData.windows[nextIndex].toplevel.activate();
} else if (toplevelObject) {
toplevelObject.activate(); toplevelObject.activate();
} }
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {

View File

@@ -463,6 +463,32 @@ Column {
} }
} }
DankActionButton {
id: groupByAppButton
buttonSize: 28
visible: modelData.id === "runningApps"
iconName: "apps"
iconSize: 16
iconColor: SettingsData.runningAppsGroupByApp ? Theme.primary : Theme.outline
onClicked: {
SettingsData.setRunningAppsGroupByApp(!SettingsData.runningAppsGroupByApp)
}
onEntered: {
groupByAppTooltipLoader.active = true
if (groupByAppTooltipLoader.item) {
const tooltipText = SettingsData.runningAppsGroupByApp ? "Ungroup" : "Group by App"
const p = groupByAppButton.mapToItem(null, groupByAppButton.width / 2, 0)
groupByAppTooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
}
}
onExited: {
if (groupByAppTooltipLoader.item) {
groupByAppTooltipLoader.item.hide()
}
groupByAppTooltipLoader.active = false
}
}
Rectangle { Rectangle {
id: compactModeTooltip id: compactModeTooltip
width: tooltipText.contentWidth + Theme.spacingM * 2 width: tooltipText.contentWidth + Theme.spacingM * 2
@@ -930,4 +956,10 @@ Column {
active: false active: false
sourceComponent: DankTooltip {} sourceComponent: DankTooltip {}
} }
Loader {
id: groupByAppTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
} }