1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -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 runningAppsCompactMode: true
property bool runningAppsCurrentWorkspace: false
property bool runningAppsGroupByApp: false
property string clockDateFormat: ""
property string lockDateFormat: ""
property int mediaSize: 1
@@ -373,6 +374,7 @@ Singleton {
focusedWindowCompactMode = settings.focusedWindowCompactMode !== undefined ? settings.focusedWindowCompactMode : false
runningAppsCompactMode = settings.runningAppsCompactMode !== undefined ? settings.runningAppsCompactMode : true
runningAppsCurrentWorkspace = settings.runningAppsCurrentWorkspace !== undefined ? settings.runningAppsCurrentWorkspace : false
runningAppsGroupByApp = settings.runningAppsGroupByApp !== undefined ? settings.runningAppsGroupByApp : false
clockDateFormat = settings.clockDateFormat !== undefined ? settings.clockDateFormat : ""
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : ""
mediaSize = settings.mediaSize !== undefined ? settings.mediaSize : (settings.mediaCompactMode !== undefined ? (settings.mediaCompactMode ? 0 : 1) : 1)
@@ -576,6 +578,7 @@ Singleton {
"focusedWindowCompactMode": focusedWindowCompactMode,
"runningAppsCompactMode": runningAppsCompactMode,
"runningAppsCurrentWorkspace": runningAppsCurrentWorkspace,
"runningAppsGroupByApp": runningAppsGroupByApp,
"clockDateFormat": clockDateFormat,
"lockDateFormat": lockDateFormat,
"mediaSize": mediaSize,
@@ -701,7 +704,7 @@ Singleton {
"controlCenterWidgets", "showWorkspaceIndex", "workspaceScrolling", "showWorkspacePadding", "showWorkspaceApps",
"maxWorkspaceIcons", "workspacesPerMonitor", "workspaceNameIcons", "waveProgressEnabled",
"clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode",
"runningAppsCurrentWorkspace", "clockDateFormat", "lockDateFormat", "mediaSize",
"runningAppsCurrentWorkspace", "runningAppsGroupByApp", "clockDateFormat", "lockDateFormat", "mediaSize",
"dankBarLeftWidgets", "dankBarCenterWidgets", "dankBarRightWidgets",
"appLauncherViewMode", "spotlightModalViewMode", "sortAppsAlphabetically",
"networkPreference", "iconTheme", "launcherLogoMode", "launcherLogoCustomPath",
@@ -1232,6 +1235,11 @@ Singleton {
saveSettings()
}
function setRunningAppsGroupByApp(enabled) {
runningAppsGroupByApp = enabled
saveSettings()
}
function setClockDateFormat(format) {
clockDateFormat = format || ""
saveSettings()

View File

@@ -26,7 +26,28 @@ Rectangle {
}
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: {
if (windowCount === 0) {
return 0;
@@ -158,15 +179,19 @@ Rectangle {
Repeater {
id: windowRepeater
model: sortedToplevels
model: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels
delegate: Item {
id: delegateItem
property bool isFocused: modelData.activated
property string appId: modelData.appId || ""
property string windowTitle: modelData.title || "(Unnamed)"
property var toplevelObject: modelData
property bool isGrouped: SettingsData.runningAppsGroupByApp
property var groupData: isGrouped ? modelData : null
property var toplevelData: isGrouped ? (modelData.windows.length > 0 ? modelData.windows[0].toplevel : null) : 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: {
let appName = "Unknown";
if (appId) {
@@ -174,6 +199,9 @@ Rectangle {
appName = desktopEntry
&& desktopEntry.name ? desktopEntry.name : appId;
}
if (isGrouped && windowCount > 1) {
return appName + " (" + windowCount + " windows)";
}
return appName + (windowTitle ? " • " + windowTitle : "")
}
@@ -264,6 +292,27 @@ Rectangle {
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)
StyledText {
anchors.left: iconImg.right
@@ -289,7 +338,17 @@ Rectangle {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
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();
}
} else if (mouse.button === Qt.RightButton) {
@@ -352,15 +411,19 @@ Rectangle {
Repeater {
id: windowRepeater
model: sortedToplevels
model: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels
delegate: Item {
id: delegateItem
property bool isFocused: modelData.activated
property string appId: modelData.appId || ""
property string windowTitle: modelData.title || "(Unnamed)"
property var toplevelObject: modelData
property bool isGrouped: SettingsData.runningAppsGroupByApp
property var groupData: isGrouped ? modelData : null
property var toplevelData: isGrouped ? (modelData.windows.length > 0 ? modelData.windows[0].toplevel : null) : 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: {
let appName = "Unknown";
if (appId) {
@@ -368,6 +431,9 @@ Rectangle {
appName = desktopEntry
&& desktopEntry.name ? desktopEntry.name : appId;
}
if (isGrouped && windowCount > 1) {
return appName + " (" + windowCount + " windows)";
}
return appName + (windowTitle ? " • " + windowTitle : "")
}
@@ -456,6 +522,27 @@ Rectangle {
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 {
anchors.left: iconImg.right
anchors.leftMargin: Theme.spacingXS
@@ -480,7 +567,17 @@ Rectangle {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
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();
}
} 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 {
id: compactModeTooltip
width: tooltipText.contentWidth + Theme.spacingM * 2
@@ -930,4 +956,10 @@ Column {
active: false
sourceComponent: DankTooltip {}
}
Loader {
id: groupByAppTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
}