mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-26 21:17:47 -04:00
245 lines
7.8 KiB
QML
245 lines
7.8 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import Quickshell
|
|
import Quickshell.Hyprland
|
|
import Quickshell.I3
|
|
import Quickshell.WindowManager
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
|
|
DankOSD {
|
|
id: root
|
|
|
|
property string workspaceLabel: ""
|
|
property var lastActiveWorkspaceId: null
|
|
|
|
readonly property real horizontalPadding: Theme.spacingL
|
|
readonly property real itemSpacing: Theme.spacingM
|
|
readonly property real textWidth: Math.ceil(textMetrics.width)
|
|
readonly property real digitReserve: Math.ceil(digitMetrics.width)
|
|
readonly property var activeWorkspace: root.getActiveWorkspace()
|
|
readonly property bool useExtWorkspace: {
|
|
if (Quickshell.env("DMS_FORCE_EXTWS") === "1")
|
|
return (WindowManager.windowsets?.length ?? 0) > 0;
|
|
if (!CompositorService.compositorDetected)
|
|
return false;
|
|
switch (CompositorService.compositor) {
|
|
case "niri":
|
|
case "hyprland":
|
|
case "mango":
|
|
case "sway":
|
|
case "scroll":
|
|
case "miracle":
|
|
return false;
|
|
default:
|
|
return (WindowManager.windowsets?.length ?? 0) > 0;
|
|
}
|
|
}
|
|
|
|
osdWidth: Math.min(
|
|
Math.max(140, horizontalPadding * 2 + Theme.iconSize + itemSpacing + textWidth + digitReserve),
|
|
screenWidth - Theme.spacingM * 2
|
|
)
|
|
osdHeight: 48
|
|
autoHideInterval: 1500
|
|
enableMouseInteraction: false
|
|
|
|
function stripSwayWorkspaceNumber(num, name) {
|
|
if (num === undefined || num === -1)
|
|
return name ?? "";
|
|
if (typeof name !== "string")
|
|
return "";
|
|
const prefix = num + ":";
|
|
if (!name.startsWith(prefix))
|
|
return name;
|
|
return name.slice(prefix.length);
|
|
}
|
|
|
|
function isSpecialHyprlandName(name) {
|
|
return name === "special" || name.startsWith("special:");
|
|
}
|
|
|
|
function inOverview() {
|
|
if (CompositorService.isNiri)
|
|
return NiriService.inOverview;
|
|
if (CompositorService.isMango)
|
|
return MangoService.isOutputInOverview(root.screen?.name);
|
|
return false;
|
|
}
|
|
|
|
function getActiveWorkspace() {
|
|
const screenName = root.screen?.name ?? "";
|
|
|
|
if (CompositorService.isNiri) {
|
|
const ws = NiriService.allWorkspaces.find(w => (!screenName || w.output === screenName) && w.is_active);
|
|
if (!ws)
|
|
return null;
|
|
return {
|
|
"id": ws.id,
|
|
"idx": ws.idx,
|
|
"name": ws.name ?? "",
|
|
"output": ws.output ?? ""
|
|
};
|
|
}
|
|
|
|
if (CompositorService.isHyprland) {
|
|
const monitor = Hyprland.monitors?.values.find(m => !screenName || m.name === screenName);
|
|
const ws = monitor?.activeWorkspace;
|
|
if (!ws)
|
|
return null;
|
|
const name = ws.name ?? "";
|
|
if (root.isSpecialHyprlandName(name))
|
|
return null;
|
|
const id = ws.id;
|
|
return {
|
|
"id": id,
|
|
"idx": id > 0 ? id : null,
|
|
"name": (name !== "" && name !== String(id)) ? name : "",
|
|
"output": monitor?.name ?? screenName
|
|
};
|
|
}
|
|
|
|
if (CompositorService.isMango) {
|
|
const tags = MangoService.getActiveTags(screenName);
|
|
if (tags.length === 0)
|
|
return null;
|
|
const tag = tags[0];
|
|
return {
|
|
"id": tag,
|
|
"idx": tag + 1,
|
|
"name": "",
|
|
"output": screenName
|
|
};
|
|
}
|
|
|
|
if (CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) {
|
|
const workspaces = I3.workspaces?.values || [];
|
|
const onScreen = workspaces.filter(w => !screenName || w.monitor?.name === screenName);
|
|
const ws = onScreen.find(w => w.focused) || onScreen.find(w => w.visible) || onScreen.find(w => w.active) || onScreen[0];
|
|
if (!ws)
|
|
return null;
|
|
const num = ws.number;
|
|
const name = root.stripSwayWorkspaceNumber(num, ws.name);
|
|
return {
|
|
"id": (num !== undefined && num !== -1) ? num : name,
|
|
"idx": (num !== undefined && num > 0) ? num : null,
|
|
"name": name,
|
|
"output": ws.monitor?.name ?? screenName
|
|
};
|
|
}
|
|
|
|
if (root.useExtWorkspace && root.screen) {
|
|
const projection = WindowManager.screenProjection(root.screen);
|
|
const ws = projection?.windowsets?.find(w => w.active);
|
|
if (!ws)
|
|
return null;
|
|
const name = (ws.name ?? "").trim();
|
|
const id = ws.id || name;
|
|
if (id === undefined || id === null || id === "")
|
|
return null;
|
|
const parsed = parseInt(name, 10);
|
|
return {
|
|
"id": id,
|
|
"idx": (name !== "" && String(parsed) === name) ? parsed : null,
|
|
"name": name,
|
|
"output": screenName
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function handleWorkspaceChange() {
|
|
if (!SettingsData.osdWorkspaceEnabled)
|
|
return;
|
|
|
|
const ws = root.activeWorkspace;
|
|
if (!ws || ws.id === undefined || ws.id === null)
|
|
return;
|
|
|
|
if (root.lastActiveWorkspaceId === null) {
|
|
root.lastActiveWorkspaceId = ws.id;
|
|
return;
|
|
}
|
|
if (root.lastActiveWorkspaceId === ws.id)
|
|
return;
|
|
if (root.inOverview())
|
|
return;
|
|
|
|
root.lastActiveWorkspaceId = ws.id;
|
|
root.updateWorkspaceInfo(ws);
|
|
root.show();
|
|
}
|
|
|
|
function updateWorkspaceInfo(ws) {
|
|
if (!ws)
|
|
return;
|
|
const num = (ws.idx !== undefined && ws.idx !== null && ws.idx > 0) ? ws.idx : (typeof ws.id === "number" && ws.id > 0 ? ws.id : null);
|
|
const name = (ws.name ?? "").trim();
|
|
|
|
if (num !== null) {
|
|
if (name !== "" && name !== String(num)) {
|
|
root.workspaceLabel = I18n.tr("Workspace %1: %2", "workspace switch OSD, %1 is workspace number, %2 is workspace name").arg(num).arg(name);
|
|
} else {
|
|
root.workspaceLabel = I18n.tr("Workspace %1", "workspace switch OSD, %1 is workspace number").arg(num);
|
|
}
|
|
} else if (name !== "") {
|
|
root.workspaceLabel = name;
|
|
} else {
|
|
root.workspaceLabel = I18n.tr("Workspace", "fallback label when workspace has no number or name");
|
|
}
|
|
}
|
|
|
|
onActiveWorkspaceChanged: root.handleWorkspaceChange()
|
|
|
|
Component.onCompleted: root.handleWorkspaceChange()
|
|
|
|
TextMetrics {
|
|
id: textMetrics
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
font.family: Theme.fontFamily
|
|
text: root.workspaceLabel
|
|
}
|
|
|
|
TextMetrics {
|
|
id: digitMetrics
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
font.family: Theme.fontFamily
|
|
text: "0"
|
|
}
|
|
|
|
content: Item {
|
|
anchors.fill: parent
|
|
|
|
RowLayout {
|
|
anchors {
|
|
fill: parent
|
|
leftMargin: root.horizontalPadding
|
|
rightMargin: root.horizontalPadding
|
|
}
|
|
spacing: root.itemSpacing
|
|
|
|
DankIcon {
|
|
Layout.alignment: Qt.AlignVCenter
|
|
name: "view_module"
|
|
size: Theme.iconSize
|
|
color: Theme.primary
|
|
}
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
text: root.workspaceLabel
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
wrapMode: Text.NoWrap
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
}
|
|
}
|