Files
DankMaterialShell/quickshell/Modules/OSD/WorkspaceOSD.qml
T

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
}
}
}
}