mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-29 16:02:51 -05:00
Add grouped apps option to the dock
This commit is contained in:
@@ -116,6 +116,7 @@ Singleton {
|
|||||||
property bool qtThemingEnabled: false
|
property bool qtThemingEnabled: false
|
||||||
property bool showDock: false
|
property bool showDock: false
|
||||||
property bool dockAutoHide: false
|
property bool dockAutoHide: false
|
||||||
|
property bool dockGroupByApp: false
|
||||||
property real cornerRadius: 12
|
property real cornerRadius: 12
|
||||||
property bool notificationOverlayEnabled: false
|
property bool notificationOverlayEnabled: false
|
||||||
property bool topBarAutoHide: false
|
property bool topBarAutoHide: false
|
||||||
@@ -304,6 +305,7 @@ Singleton {
|
|||||||
qtThemingEnabled = settings.qtThemingEnabled !== undefined ? settings.qtThemingEnabled : false
|
qtThemingEnabled = settings.qtThemingEnabled !== undefined ? settings.qtThemingEnabled : false
|
||||||
showDock = settings.showDock !== undefined ? settings.showDock : false
|
showDock = settings.showDock !== undefined ? settings.showDock : false
|
||||||
dockAutoHide = settings.dockAutoHide !== undefined ? settings.dockAutoHide : false
|
dockAutoHide = settings.dockAutoHide !== undefined ? settings.dockAutoHide : false
|
||||||
|
dockGroupByApp = settings.dockGroupByApp !== undefined ? settings.dockGroupByApp : false
|
||||||
cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12
|
cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12
|
||||||
notificationOverlayEnabled = settings.notificationOverlayEnabled !== undefined ? settings.notificationOverlayEnabled : false
|
notificationOverlayEnabled = settings.notificationOverlayEnabled !== undefined ? settings.notificationOverlayEnabled : false
|
||||||
topBarAutoHide = settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false
|
topBarAutoHide = settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false
|
||||||
@@ -415,6 +417,7 @@ Singleton {
|
|||||||
"qtThemingEnabled": qtThemingEnabled,
|
"qtThemingEnabled": qtThemingEnabled,
|
||||||
"showDock": showDock,
|
"showDock": showDock,
|
||||||
"dockAutoHide": dockAutoHide,
|
"dockAutoHide": dockAutoHide,
|
||||||
|
"dockGroupByApp": dockGroupByApp,
|
||||||
"cornerRadius": cornerRadius,
|
"cornerRadius": cornerRadius,
|
||||||
"notificationOverlayEnabled": notificationOverlayEnabled,
|
"notificationOverlayEnabled": notificationOverlayEnabled,
|
||||||
"topBarAutoHide": topBarAutoHide,
|
"topBarAutoHide": topBarAutoHide,
|
||||||
@@ -941,6 +944,11 @@ Singleton {
|
|||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setDockGroupByApp(enabled) {
|
||||||
|
dockGroupByApp = enabled
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
function setCornerRadius(radius) {
|
function setCornerRadius(radius) {
|
||||||
cornerRadius = radius
|
cornerRadius = radius
|
||||||
saveSettings()
|
saveSettings()
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ PanelWindow {
|
|||||||
property var contextMenu
|
property var contextMenu
|
||||||
property bool autoHide: SettingsData.dockAutoHide
|
property bool autoHide: SettingsData.dockAutoHide
|
||||||
property real backgroundTransparency: SettingsData.dockTransparency
|
property real backgroundTransparency: SettingsData.dockTransparency
|
||||||
|
property bool groupByApp: SettingsData.dockGroupByApp
|
||||||
|
|
||||||
property bool contextMenuOpen: (contextMenu && contextMenu.visible && contextMenu.screen === modelData)
|
property bool contextMenuOpen: (contextMenu && contextMenu.visible && contextMenu.screen === modelData)
|
||||||
property bool windowIsFullscreen: {
|
property bool windowIsFullscreen: {
|
||||||
@@ -135,6 +136,7 @@ PanelWindow {
|
|||||||
anchors.bottomMargin: 4
|
anchors.bottomMargin: 4
|
||||||
|
|
||||||
contextMenu: dock.contextMenu
|
contextMenu: dock.contextMenu
|
||||||
|
groupByApp: dock.groupByApp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -25,24 +26,39 @@ Item {
|
|||||||
property bool isHovered: mouseArea.containsMouse && !dragging
|
property bool isHovered: mouseArea.containsMouse && !dragging
|
||||||
property bool showTooltip: mouseArea.containsMouse && !dragging
|
property bool showTooltip: mouseArea.containsMouse && !dragging
|
||||||
property bool isWindowFocused: {
|
property bool isWindowFocused: {
|
||||||
if (!appData || appData.type !== "window") {
|
if (!appData) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const toplevel = getToplevelObject()
|
|
||||||
if (!toplevel) {
|
if (appData.type === "window") {
|
||||||
return false
|
const toplevel = getToplevelObject()
|
||||||
|
if (!toplevel) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return toplevel.activated
|
||||||
|
} else if (appData.type === "grouped") {
|
||||||
|
// For grouped apps, check if any window is focused
|
||||||
|
const allToplevels = ToplevelManager.toplevels.values
|
||||||
|
for (let i = 0; i < allToplevels.length; i++) {
|
||||||
|
const toplevel = allToplevels[i]
|
||||||
|
if (toplevel.appId === appData.appId && toplevel.activated) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return toplevel.activated
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
property string tooltipText: {
|
property string tooltipText: {
|
||||||
if (!appData) {
|
if (!appData) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appData.type === "window" && showWindowTitle) {
|
if ((appData.type === "window" && showWindowTitle) || (appData.type === "grouped" && appData.windowTitle)) {
|
||||||
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
|
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
|
||||||
const appName = desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId
|
const appName = desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId
|
||||||
return appName + (windowTitle ? " • " + windowTitle : "")
|
const title = appData.type === "window" ? windowTitle : appData.windowTitle
|
||||||
|
return appName + (title ? " • " + title : "")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!appData.appId) {
|
if (!appData.appId) {
|
||||||
@@ -57,7 +73,7 @@ Item {
|
|||||||
height: 40
|
height: 40
|
||||||
|
|
||||||
function getToplevelObject() {
|
function getToplevelObject() {
|
||||||
if (!appData || appData.type !== "window") {
|
if (!appData) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,23 +82,47 @@ Item {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appData.uniqueId) {
|
if (appData.type === "window") {
|
||||||
for (var i = 0; i < sortedToplevels.length; i++) {
|
if (appData.uniqueId) {
|
||||||
const toplevel = sortedToplevels[i]
|
for (var i = 0; i < sortedToplevels.length; i++) {
|
||||||
const checkId = toplevel.title + "|" + (toplevel.appId || "") + "|" + i
|
const toplevel = sortedToplevels[i]
|
||||||
if (checkId === appData.uniqueId) {
|
const checkId = toplevel.title + "|" + (toplevel.appId || "") + "|" + i
|
||||||
return toplevel
|
if (checkId === appData.uniqueId) {
|
||||||
|
return toplevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appData.windowId !== undefined && appData.windowId !== null && appData.windowId >= 0) {
|
||||||
|
if (appData.windowId < sortedToplevels.length) {
|
||||||
|
return sortedToplevels[appData.windowId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (appData.type === "grouped") {
|
||||||
|
if (appData.windowId !== undefined && appData.windowId !== null && appData.windowId >= 0) {
|
||||||
|
if (appData.windowId < sortedToplevels.length) {
|
||||||
|
return sortedToplevels[appData.windowId]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appData.windowId !== undefined && appData.windowId !== null && appData.windowId >= 0) {
|
return null
|
||||||
if (appData.windowId < sortedToplevels.length) {
|
}
|
||||||
return sortedToplevels[appData.windowId]
|
|
||||||
}
|
function getGroupedToplevels() {
|
||||||
|
if (!appData || appData.type !== "grouped") {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
const toplevels = []
|
||||||
|
const allToplevels = ToplevelManager.toplevels.values
|
||||||
|
for (let i = 0; i < allToplevels.length; i++) {
|
||||||
|
const toplevel = allToplevels[i]
|
||||||
|
if (toplevel.appId === appData.appId) {
|
||||||
|
toplevels.push(toplevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toplevels
|
||||||
}
|
}
|
||||||
onIsHoveredChanged: {
|
onIsHoveredChanged: {
|
||||||
if (isHovered) {
|
if (isHovered) {
|
||||||
@@ -232,6 +272,36 @@ Item {
|
|||||||
if (toplevel) {
|
if (toplevel) {
|
||||||
toplevel.activate()
|
toplevel.activate()
|
||||||
}
|
}
|
||||||
|
} else if (appData.type === "grouped") {
|
||||||
|
if (appData.windowCount === 0) {
|
||||||
|
if (appData && appData.appId) {
|
||||||
|
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
|
||||||
|
if (desktopEntry) {
|
||||||
|
AppUsageHistoryData.addAppUsage({
|
||||||
|
"id": appData.appId,
|
||||||
|
"name": desktopEntry.name || appData.appId,
|
||||||
|
"icon": desktopEntry.icon || "",
|
||||||
|
"exec": desktopEntry.exec || "",
|
||||||
|
"comment": desktopEntry.comment || ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
SessionService.launchDesktopEntry(desktopEntry)
|
||||||
|
}
|
||||||
|
} else if (appData.windowCount === 1) {
|
||||||
|
// For single window, activate directly
|
||||||
|
const toplevel = getToplevelObject()
|
||||||
|
if (toplevel) {
|
||||||
|
console.log("Activating grouped app window:", appData.windowTitle)
|
||||||
|
toplevel.activate()
|
||||||
|
} else {
|
||||||
|
console.warn("No toplevel found for grouped app")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For multiple windows, show context menu (hide pin option for left-click)
|
||||||
|
if (contextMenu) {
|
||||||
|
contextMenu.showForButton(root, appData, 65, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (mouse.button === Qt.MiddleButton) {
|
} else if (mouse.button === Qt.MiddleButton) {
|
||||||
if (appData && appData.appId) {
|
if (appData && appData.appId) {
|
||||||
@@ -248,8 +318,11 @@ Item {
|
|||||||
SessionService.launchDesktopEntry(desktopEntry)
|
SessionService.launchDesktopEntry(desktopEntry)
|
||||||
}
|
}
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
if (contextMenu) {
|
if (contextMenu && appData) {
|
||||||
contextMenu.showForButton(root, appData, 40)
|
console.log("Right-clicked on app:", appData.appId, "type:", appData.type, "windowCount:", appData.windowCount || 0)
|
||||||
|
contextMenu.showForButton(root, appData, 40, false)
|
||||||
|
} else {
|
||||||
|
console.warn("No context menu or appData available")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,31 +395,54 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Indicator for running/focused state
|
// Indicator for running/focused state
|
||||||
Rectangle {
|
Row {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: -2
|
anchors.bottomMargin: -2
|
||||||
width: 8
|
spacing: 2
|
||||||
height: 2
|
visible: appData && (appData.isRunning || appData.type === "window" || (appData.type === "grouped" && appData.windowCount > 0))
|
||||||
radius: 1
|
|
||||||
visible: appData && (appData.isRunning || appData.type === "window")
|
Repeater {
|
||||||
color: {
|
model: {
|
||||||
if (!appData) {
|
if (!appData) return 0
|
||||||
return "transparent"
|
if (appData.type === "grouped") {
|
||||||
|
return Math.min(appData.windowCount, 4)
|
||||||
|
} else if (appData.type === "window" || appData.isRunning) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWindowFocused) {
|
Rectangle {
|
||||||
return Theme.primary
|
width: appData && appData.type === "grouped" && appData.windowCount > 1 ? 4 : 8
|
||||||
}
|
height: 2
|
||||||
|
radius: 1
|
||||||
|
color: {
|
||||||
|
if (!appData) {
|
||||||
|
return "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
if (appData.isRunning || appData.type === "window") {
|
if (appData.type !== "grouped" || appData.windowCount === 1) {
|
||||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
if (isWindowFocused) {
|
||||||
}
|
return Theme.primary
|
||||||
|
}
|
||||||
|
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||||
|
}
|
||||||
|
|
||||||
return "transparent"
|
if (appData.type === "grouped" && appData.windowCount > 1) {
|
||||||
|
const groupToplevels = getGroupedToplevels()
|
||||||
|
if (index < groupToplevels.length && groupToplevels[index].activated) {
|
||||||
|
return Theme.primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
transform: Translate {
|
transform: Translate {
|
||||||
id: translateY
|
id: translateY
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Item {
|
|||||||
property var contextMenu: null
|
property var contextMenu: null
|
||||||
property bool requestDockShow: false
|
property bool requestDockShow: false
|
||||||
property int pinnedAppCount: 0
|
property int pinnedAppCount: 0
|
||||||
|
property bool groupByApp: false
|
||||||
|
|
||||||
implicitWidth: row.width
|
implicitWidth: row.width
|
||||||
implicitHeight: row.height
|
implicitHeight: row.height
|
||||||
@@ -50,53 +51,134 @@ Item {
|
|||||||
|
|
||||||
const items = []
|
const items = []
|
||||||
const pinnedApps = [...(SessionData.pinnedApps || [])]
|
const pinnedApps = [...(SessionData.pinnedApps || [])]
|
||||||
|
|
||||||
pinnedApps.forEach(appId => {
|
|
||||||
items.push({
|
|
||||||
"type": "pinned",
|
|
||||||
"appId": appId,
|
|
||||||
"windowId": -1,
|
|
||||||
"windowTitle": "",
|
|
||||||
"workspaceId": -1,
|
|
||||||
"isPinned": true,
|
|
||||||
"isRunning": false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
root.pinnedAppCount = pinnedApps.length
|
|
||||||
|
|
||||||
const sortedToplevels = CompositorService.sortedToplevels
|
const sortedToplevels = CompositorService.sortedToplevels
|
||||||
|
|
||||||
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
if (root.groupByApp) {
|
||||||
items.push({
|
// Group windows by appId
|
||||||
"type": "separator",
|
const appGroups = new Map()
|
||||||
"appId": "__SEPARATOR__",
|
|
||||||
"windowId": -1,
|
// Add pinned apps first (even if they have no windows)
|
||||||
"windowTitle": "",
|
pinnedApps.forEach(appId => {
|
||||||
"workspaceId": -1,
|
appGroups.set(appId, {
|
||||||
"isPinned": false,
|
appId: appId,
|
||||||
"isRunning": false,
|
isPinned: true,
|
||||||
"isFocused": false
|
windows: []
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Group all running windows by appId
|
||||||
|
sortedToplevels.forEach((toplevel, index) => {
|
||||||
|
const appId = toplevel.appId || "unknown"
|
||||||
|
if (!appGroups.has(appId)) {
|
||||||
|
appGroups.set(appId, {
|
||||||
|
appId: appId,
|
||||||
|
isPinned: false,
|
||||||
|
windows: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const title = toplevel.title || "(Unnamed)"
|
||||||
|
const truncatedTitle = title.length > 50 ? title.substring(0, 47) + "..." : title
|
||||||
|
const uniqueId = toplevel.title + "|" + (toplevel.appId || "") + "|" + index
|
||||||
|
|
||||||
|
appGroups.get(appId).windows.push({
|
||||||
|
windowId: index,
|
||||||
|
windowTitle: truncatedTitle,
|
||||||
|
uniqueId: uniqueId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sort groups: pinned first, then unpinned
|
||||||
|
const pinnedGroups = []
|
||||||
|
const unpinnedGroups = []
|
||||||
|
|
||||||
|
Array.from(appGroups.entries()).forEach(([appId, group]) => {
|
||||||
|
// For grouped apps, just show the first window info but track all windows
|
||||||
|
const firstWindow = group.windows.length > 0 ? group.windows[0] : null
|
||||||
|
|
||||||
|
const item = {
|
||||||
|
"type": "grouped",
|
||||||
|
"appId": appId,
|
||||||
|
"windowId": firstWindow ? firstWindow.windowId : -1,
|
||||||
|
"windowTitle": firstWindow ? firstWindow.windowTitle : "",
|
||||||
|
"workspaceId": -1,
|
||||||
|
"isPinned": group.isPinned,
|
||||||
|
"isRunning": group.windows.length > 0,
|
||||||
|
"windowCount": group.windows.length,
|
||||||
|
"uniqueId": firstWindow ? firstWindow.uniqueId : "",
|
||||||
|
"allWindows": group.windows
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.isPinned) {
|
||||||
|
pinnedGroups.push(item)
|
||||||
|
} else {
|
||||||
|
unpinnedGroups.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add items in order
|
||||||
|
pinnedGroups.forEach(item => items.push(item))
|
||||||
|
|
||||||
|
// Add separator if needed
|
||||||
|
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
||||||
|
items.push({
|
||||||
|
"type": "separator",
|
||||||
|
"appId": "__SEPARATOR__",
|
||||||
|
"windowId": -1,
|
||||||
|
"windowTitle": "",
|
||||||
|
"workspaceId": -1,
|
||||||
|
"isPinned": false,
|
||||||
|
"isRunning": false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
unpinnedGroups.forEach(item => items.push(item))
|
||||||
|
root.pinnedAppCount = pinnedGroups.length
|
||||||
|
} else {
|
||||||
|
pinnedApps.forEach(appId => {
|
||||||
|
items.push({
|
||||||
|
"type": "pinned",
|
||||||
|
"appId": appId,
|
||||||
|
"windowId": -1,
|
||||||
|
"windowTitle": "",
|
||||||
|
"workspaceId": -1,
|
||||||
|
"isPinned": true,
|
||||||
|
"isRunning": false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
root.pinnedAppCount = pinnedApps.length
|
||||||
|
|
||||||
|
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
||||||
|
items.push({
|
||||||
|
"type": "separator",
|
||||||
|
"appId": "__SEPARATOR__",
|
||||||
|
"windowId": -1,
|
||||||
|
"windowTitle": "",
|
||||||
|
"workspaceId": -1,
|
||||||
|
"isPinned": false,
|
||||||
|
"isRunning": false,
|
||||||
|
"isFocused": false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedToplevels.forEach((toplevel, index) => {
|
||||||
|
const title = toplevel.title || "(Unnamed)"
|
||||||
|
const truncatedTitle = title.length > 50 ? title.substring(0, 47) + "..." : title
|
||||||
|
const uniqueId = toplevel.title + "|" + (toplevel.appId || "") + "|" + index
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
"type": "window",
|
||||||
|
"appId": toplevel.appId,
|
||||||
|
"windowId": index,
|
||||||
|
"windowTitle": truncatedTitle,
|
||||||
|
"workspaceId": -1,
|
||||||
|
"isPinned": false,
|
||||||
|
"isRunning": true,
|
||||||
|
"uniqueId": uniqueId
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sortedToplevels.forEach((toplevel, index) => {
|
|
||||||
const title = toplevel.title || "(Unnamed)"
|
|
||||||
const truncatedTitle = title.length > 50 ? title.substring(0, 47) + "..." : title
|
|
||||||
const uniqueId = toplevel.title + "|" + (toplevel.appId || "") + "|" + index
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
"type": "window",
|
|
||||||
"appId": toplevel.appId,
|
|
||||||
"windowId": index,
|
|
||||||
"windowTitle": truncatedTitle,
|
|
||||||
"workspaceId": -1,
|
|
||||||
"isPinned": false,
|
|
||||||
"isRunning": true,
|
|
||||||
"uniqueId": uniqueId
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
items.forEach(item => append(item))
|
items.forEach(item => append(item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +213,7 @@ Item {
|
|||||||
index: model.index
|
index: model.index
|
||||||
|
|
||||||
// Override tooltip for windows to show window title
|
// Override tooltip for windows to show window title
|
||||||
showWindowTitle: model.type === "window"
|
showWindowTitle: model.type === "window" || model.type === "grouped"
|
||||||
windowTitle: model.windowTitle || ""
|
windowTitle: model.windowTitle || ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,4 +233,8 @@ Item {
|
|||||||
dockModel.updateModel()
|
dockModel.updateModel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onGroupByAppChanged: {
|
||||||
|
dockModel.updateModel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,13 @@ PanelWindow {
|
|||||||
property var anchorItem: null
|
property var anchorItem: null
|
||||||
property real dockVisibleHeight: 40
|
property real dockVisibleHeight: 40
|
||||||
property int margin: 10
|
property int margin: 10
|
||||||
|
property bool hidePin: false
|
||||||
|
|
||||||
function showForButton(button, data, dockHeight) {
|
function showForButton(button, data, dockHeight, hidePinOption) {
|
||||||
anchorItem = button
|
anchorItem = button
|
||||||
appData = data
|
appData = data
|
||||||
dockVisibleHeight = dockHeight || 40
|
dockVisibleHeight = dockHeight || 40
|
||||||
|
hidePin = hidePinOption || false
|
||||||
|
|
||||||
const dockWindow = button.Window.window
|
const dockWindow = button.Window.window
|
||||||
if (dockWindow) {
|
if (dockWindow) {
|
||||||
@@ -144,7 +146,102 @@ PanelWindow {
|
|||||||
anchors.topMargin: Theme.spacingS
|
anchors.topMargin: Theme.spacingS
|
||||||
spacing: 1
|
spacing: 1
|
||||||
|
|
||||||
|
// Window list for grouped apps
|
||||||
|
Repeater {
|
||||||
|
model: {
|
||||||
|
if (!root.appData || root.appData.type !== "grouped") return []
|
||||||
|
|
||||||
|
const toplevels = []
|
||||||
|
const allToplevels = ToplevelManager.toplevels.values
|
||||||
|
for (let i = 0; i < allToplevels.length; i++) {
|
||||||
|
const toplevel = allToplevels[i]
|
||||||
|
if (toplevel.appId === root.appData.appId) {
|
||||||
|
toplevels.push(toplevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toplevels
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 28
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: windowArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.right: closeButton.left
|
||||||
|
anchors.rightMargin: Theme.spacingXS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: (modelData && modelData.title) ? modelData.title : "(Unnamed)"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: closeButton
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingXS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 20
|
||||||
|
height: 20
|
||||||
|
radius: 10
|
||||||
|
color: closeMouseArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.2) : "transparent"
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "close"
|
||||||
|
size: 12
|
||||||
|
color: closeMouseArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (modelData && modelData.close) {
|
||||||
|
modelData.close()
|
||||||
|
}
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: windowArea
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: 24
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (modelData && modelData.activate) {
|
||||||
|
modelData.activate()
|
||||||
|
}
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
visible: {
|
||||||
|
if (!root.appData) return false
|
||||||
|
if (root.appData.type !== "grouped") return false
|
||||||
|
return root.appData.windowCount > 0
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: !root.hidePin
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -198,7 +295,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: root.appData && root.appData.type === "window"
|
visible: root.appData && (root.appData.type === "window" || (root.appData.type === "grouped" && root.appData.windowCount > 0))
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -210,7 +307,12 @@ PanelWindow {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: "Close Window"
|
text: {
|
||||||
|
if (root.appData && root.appData.type === "grouped") {
|
||||||
|
return "Close All Windows"
|
||||||
|
}
|
||||||
|
return "Close Window"
|
||||||
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
|
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
@@ -224,8 +326,26 @@ PanelWindow {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (root.appData && root.appData.toplevelObject) {
|
const sortedToplevels = CompositorService.sortedToplevels
|
||||||
root.appData.toplevelObject.close()
|
if (root.appData && root.appData.type === "window") {
|
||||||
|
// Find and close the specific window
|
||||||
|
for (var i = 0; i < sortedToplevels.length; i++) {
|
||||||
|
const toplevel = sortedToplevels[i]
|
||||||
|
const checkId = toplevel.title + "|" + (toplevel.appId || "") + "|" + i
|
||||||
|
if (checkId === root.appData.uniqueId) {
|
||||||
|
toplevel.close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (root.appData && root.appData.type === "grouped") {
|
||||||
|
// Close all windows for this app
|
||||||
|
const allToplevels = ToplevelManager.toplevels.values
|
||||||
|
for (let i = 0; i < allToplevels.length; i++) {
|
||||||
|
const toplevel = allToplevels[i]
|
||||||
|
if (toplevel.appId === root.appData.appId) {
|
||||||
|
toplevel.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,6 +156,79 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group by App
|
||||||
|
StyledRect {
|
||||||
|
width: parent.width
|
||||||
|
height: groupByAppSection.implicitHeight + Theme.spacingL * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g,
|
||||||
|
Theme.surfaceVariant.b, 0.3)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||||
|
Theme.outline.b, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
visible: SettingsData.showDock
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: groupByAppSection
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "apps"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - Theme.iconSize - Theme.spacingM
|
||||||
|
- groupByAppToggle.width - Theme.spacingM
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Group by App"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Group multiple windows of the same app together with a window count indicator"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
id: groupByAppToggle
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
checked: SettingsData.dockGroupByApp
|
||||||
|
onToggled: checked => {
|
||||||
|
SettingsData.setDockGroupByApp(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dock Transparency Section
|
// Dock Transparency Section
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
Reference in New Issue
Block a user