1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-15 02:02:08 -04:00

Refactor workspaceSwitcher (#246)

* Refactor WorkspaceSwitcher

Implement async data loading, dynamic UI, and fix QML connection
warnings.

* Enhance WorkspaceSwitcher

Adjust width dynamically based on icon count for better space
utilization

* fix(WorkspaceSwitcher): correct ternary logic for placeholder workspaces
This commit is contained in:
lonerorz
2025-09-25 02:36:38 +08:00
committed by GitHub
parent 7516d44de9
commit bed2259944

View File

@@ -271,6 +271,8 @@ Rectangle {
model: root.workspaceList model: root.workspaceList
Rectangle { Rectangle {
id: delegateRoot
property bool isActive: { property bool isActive: {
if (CompositorService.isHyprland) { if (CompositorService.isHyprland) {
return modelData && modelData.id === root.currentWorkspace return modelData && modelData.id === root.currentWorkspace
@@ -284,34 +286,72 @@ Rectangle {
return modelData === -1 return modelData === -1
} }
property bool isHovered: mouseArea.containsMouse property bool isHovered: mouseArea.containsMouse
property var workspaceData: {
property var loadedWorkspaceData: null
property var loadedIconData: null
property bool loadedHasIcon: false
property var loadedIcons: []
Timer {
id: dataUpdateTimer
interval: 50 // Defer data calculation by 50ms
onTriggered: {
if (isPlaceholder) { if (isPlaceholder) {
return null delegateRoot.loadedWorkspaceData = null
delegateRoot.loadedIconData = null
delegateRoot.loadedHasIcon = false
delegateRoot.loadedIcons = []
return
} }
var wsData = null;
if (CompositorService.isNiri) { if (CompositorService.isNiri) {
return NiriService.allWorkspaces.find(ws => ws.idx + 1 === modelData && ws.output === root.screenName) || null wsData = NiriService.allWorkspaces.find(ws => ws.idx + 1 === modelData && ws.output === root.screenName) || null;
} else if (CompositorService.isHyprland) {
wsData = modelData;
} }
return CompositorService.isHyprland ? modelData : null delegateRoot.loadedWorkspaceData = wsData;
var icData = null;
if (wsData?.name) {
icData = SettingsData.getWorkspaceNameIcon(wsData.name);
}
delegateRoot.loadedIconData = icData;
delegateRoot.loadedHasIcon = icData !== null;
if (SettingsData.showWorkspaceApps) {
delegateRoot.loadedIcons = root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData));
} else {
delegateRoot.loadedIcons = [];
}
}
}
function updateAllData() {
dataUpdateTimer.restart()
} }
property var iconData: workspaceData?.name ? SettingsData.getWorkspaceNameIcon(workspaceData.name) : null
property bool hasIcon: iconData !== null
property var icons: SettingsData.showWorkspaceApps ? root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData)) : []
width: { width: {
if (SettingsData.showWorkspaceApps) { if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
if (icons.length > 0) { const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
return isActive ? widgetHeight * 1.0 + Theme.spacingXS + contentRow.implicitWidth : widgetHeight * 0.8 + contentRow.implicitWidth const iconsWidth = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0);
} else { const baseWidth = isActive ? root.widgetHeight * 1.0 + Theme.spacingXS : root.widgetHeight * 0.8;
return isActive ? widgetHeight * 1.0 + Theme.spacingXS : widgetHeight * 0.8 return baseWidth + iconsWidth;
} }
} return isActive ? root.widgetHeight * 1.2 : root.widgetHeight * 0.8;
return isActive ? widgetHeight * 1.2 + Theme.spacingXS : widgetHeight * 0.8
} }
height: SettingsData.showWorkspaceApps ? widgetHeight * 0.8 : widgetHeight * 0.6 height: SettingsData.showWorkspaceApps ? widgetHeight * 0.8 : widgetHeight * 0.6
radius: height / 2 radius: height / 2
color: isActive ? Theme.primary : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha color: isActive ? Theme.primary : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha
Behavior on width {
enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
MouseArea { MouseArea {
id: mouseArea id: mouseArea
@@ -332,14 +372,20 @@ Rectangle {
} }
} }
// Loader for App Icons
Loader {
id: appIconsLoader
anchors.fill: parent
active: SettingsData.showWorkspaceApps
sourceComponent: Item {
Row { Row {
id: contentRow id: contentRow
anchors.centerIn: parent anchors.centerIn: parent
spacing: 4 spacing: 4
visible: SettingsData.showWorkspaceApps && icons.length > 0 visible: loadedIcons.length > 0
Repeater { Repeater {
model: icons.slice(0, SettingsData.maxWorkspaceIcons) model: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons)
delegate: Item { delegate: Item {
width: 18 width: 18
height: 18 height: 18
@@ -399,51 +445,80 @@ Rectangle {
} }
} }
} }
}
}
// Loader for Custom Name Icon
Loader {
id: customIconLoader
anchors.fill: parent
active: !isPlaceholder && loadedHasIcon && loadedIconData.type === "icon" && !SettingsData.showWorkspaceApps
sourceComponent: Item {
DankIcon { DankIcon {
visible: hasIcon && iconData.type === "icon" && (!SettingsData.showWorkspaceApps || icons.length === 0)
anchors.centerIn: parent anchors.centerIn: parent
name: (hasIcon && iconData.type === "icon") ? iconData.value : "" name: loadedIconData ? loadedIconData.value : "" // NULL CHECK
size: Theme.fontSizeSmall size: Theme.fontSizeSmall
color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : Theme.surfaceTextMedium color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : Theme.surfaceTextMedium
weight: isActive && !isPlaceholder ? 500 : 400 weight: isActive && !isPlaceholder ? 500 : 400
} }
}
}
// Loader for Custom Name Text
Loader {
id: customTextLoader
anchors.fill: parent
active: !isPlaceholder && loadedHasIcon && loadedIconData.type === "text" && !SettingsData.showWorkspaceApps
sourceComponent: Item {
StyledText { StyledText {
visible: hasIcon && iconData.type === "text" && (!SettingsData.showWorkspaceApps || icons.length === 0)
anchors.centerIn: parent anchors.centerIn: parent
text: (hasIcon && iconData.type === "text") ? iconData.value : "" text: loadedIconData ? loadedIconData.value : "" // NULL CHECK
color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : Theme.surfaceTextMedium color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : Theme.surfaceTextMedium
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
} }
}
}
// Loader for Workspace Index
Loader {
id: indexLoader
anchors.fill: parent
active: !isPlaceholder && SettingsData.showWorkspaceIndex && !loadedHasIcon && !SettingsData.showWorkspaceApps
sourceComponent: Item {
StyledText { StyledText {
visible: (SettingsData.showWorkspaceIndex && !hasIcon && (!SettingsData.showWorkspaceApps || icons.length === 0))
anchors.centerIn: parent anchors.centerIn: parent
text: { text: {
const isPlaceholder = CompositorService.isHyprland ? (modelData?.id === -1) : (modelData === -1) const isPlaceholder = CompositorService.isHyprland ? (modelData?.id === -1) : (modelData === -1)
if (isPlaceholder) { if (isPlaceholder) {
return index + 1 return index + 1
} }
return CompositorService.isHyprland ? (modelData?.id || "") : (modelData - 1);
return CompositorService.isHyprland ? (modelData?.id || "") : (modelData - 1)
} }
color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium color: isActive ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
} }
Behavior on width {
// When having more icons, animation becomes clunky
enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
} }
} }
// --- LOGIC / TRIGGERS ---
Component.onCompleted: updateAllData()
Connections {
target: CompositorService
function onSortedToplevelsChanged() { delegateRoot.updateAllData() }
}
Connections {
target: NiriService
enabled: CompositorService.isNiri
function onAllWorkspacesChanged() { delegateRoot.updateAllData() }
}
Connections {
target: SettingsData
function onShowWorkspaceAppsChanged() { delegateRoot.updateAllData() }
function onWorkspaceNameIconsChanged() { delegateRoot.updateAllData() }
}
} }
} }
} }