internal: use hyprlandtoplevel

This commit is contained in:
2 * r + 2 * t 2025-06-27 21:37:15 +10:00
parent 8308060a75
commit b264ae9888
9 changed files with 53 additions and 130 deletions

View file

@ -36,7 +36,7 @@ MouseArea {
property real sw: Math.abs(sx - ex)
property real sh: Math.abs(sy - ey)
property list<var> clients: Hyprland.clients.filter(c => c.workspace.id === Hyprland.activeWsId)
property list<var> clients: Hyprland.toplevels.values.filter(c => c.workspace.id === Hyprland.activeWsId)
function checkClientRects(x: real, y: real): void {
for (const c of clients) {

View file

@ -101,7 +101,7 @@ Item {
anchors.rightMargin: -Config.border.thickness
onWheel: event => {
const activeWs = Hyprland.activeClient?.workspace?.name;
const activeWs = Hyprland.activeToplevel?.workspace?.name;
if (activeWs?.startsWith("special:"))
Hyprland.dispatch(`togglespecialworkspace ${activeWs.slice(8)}`);
else if (event.angleDelta.y < 0 || Hyprland.activeWsId > 1)

View file

@ -60,7 +60,7 @@ Item {
id: icon
animate: true
text: Icons.getAppCategoryIcon(Hyprland.activeClient?.wmClass, "desktop_windows")
text: Icons.getAppCategoryIcon(Hyprland.activeToplevel?.lastIpcObject.class, "desktop_windows")
color: root.colour
anchors.horizontalCenter: parent.horizontalCenter
@ -77,7 +77,7 @@ Item {
TextMetrics {
id: metrics
text: Hyprland.activeClient?.title ?? qsTr("Desktop")
text: Hyprland.activeToplevel?.title ?? qsTr("Desktop")
font.pointSize: Appearance.font.size.smaller
font.family: Appearance.font.family.mono
elide: Qt.ElideRight

View file

@ -76,14 +76,14 @@ Item {
Repeater {
model: ScriptModel {
values: Hyprland.clients.filter(c => c.workspace?.id === root.ws)
values: Hyprland.toplevels.values.filter(c => c.workspace?.id === root.ws)
}
MaterialIcon {
required property Hyprland.Client modelData
required property var modelData
grade: 0
text: Icons.getAppCategoryIcon(modelData.wmClass, "terminal")
text: Icons.getAppCategoryIcon(modelData.lastIpcObject.class, "terminal")
color: Colours.palette.m3onSurfaceVariant
}
}

View file

@ -12,7 +12,7 @@ Item {
required property Item wrapper
implicitWidth: Hyprland.activeClient ? child.implicitWidth : -Appearance.padding.large * 2
implicitWidth: Hyprland.activeToplevel ? child.implicitWidth : -Appearance.padding.large * 2
implicitHeight: child.implicitHeight
Column {
@ -33,7 +33,7 @@ Item {
Layout.alignment: Qt.AlignVCenter
implicitSize: details.implicitHeight
source: Icons.getAppIcon(Hyprland.activeClient?.wmClass ?? "", "image-missing")
source: Icons.getAppIcon(Hyprland.activeToplevel?.lastIpcObject.class ?? "", "image-missing")
}
ColumnLayout {
@ -44,14 +44,14 @@ Item {
StyledText {
Layout.fillWidth: true
text: Hyprland.activeClient?.title ?? ""
text: Hyprland.activeToplevel?.title ?? ""
font.pointSize: Appearance.font.size.normal
elide: Text.ElideRight
}
StyledText {
Layout.fillWidth: true
text: Hyprland.activeClient?.wmClass ?? ""
text: Hyprland.activeToplevel?.lastIpcObject.class ?? ""
color: Colours.palette.m3onSurfaceVariant
elide: Text.ElideRight
}
@ -91,7 +91,7 @@ Item {
ScreencopyView {
id: preview
captureSource: Hyprland.activeClient ? ToplevelManager.activeToplevel : null
captureSource: Hyprland.activeToplevel?.wayland ?? null
live: visible
constraintSize.width: Config.bar.sizes.windowPreviewSize

View file

@ -78,7 +78,7 @@ ColumnLayout {
Button {
required property int index
readonly property int wsId: Math.floor((Hyprland.activeWsId - 1) / 10) * 10 + index + 1
readonly property bool isCurrent: Hyprland.activeClient.workspace.id === wsId
readonly property bool isCurrent: Hyprland.activeToplevel?.workspace.id === wsId
color: isCurrent ? Colours.palette.m3surfaceContainerHighest : Colours.palette.m3tertiaryContainer
onColor: isCurrent ? Colours.palette.m3onSurface : Colours.palette.m3onTertiaryContainer
@ -86,7 +86,7 @@ ColumnLayout {
disabled: isCurrent
function onClicked(): void {
Hyprland.dispatch(`movetoworkspace ${wsId},address:${Hyprland.activeClient?.address}`);
Hyprland.dispatch(`movetoworkspace ${wsId},address:0x${Hyprland.activeToplevel?.address}`);
}
}
}
@ -107,20 +107,20 @@ ColumnLayout {
Layout.rightMargin: Appearance.padding.large
Layout.bottomMargin: Appearance.padding.large
spacing: Hyprland.activeClient?.floating ? Appearance.spacing.normal : Appearance.spacing.small
spacing: Hyprland.activeToplevel?.lastIpcObject.floating ? Appearance.spacing.normal : Appearance.spacing.small
Button {
color: Colours.palette.m3secondaryContainer
onColor: Colours.palette.m3onSecondaryContainer
text: Hyprland.activeClient?.floating ? qsTr("Tile") : qsTr("Float")
text: Hyprland.activeToplevel?.lastIpcObject.floating ? qsTr("Tile") : qsTr("Float")
function onClicked(): void {
Hyprland.dispatch(`togglefloating address:${Hyprland.activeClient?.address}`);
Hyprland.dispatch(`togglefloating address:0x${Hyprland.activeToplevel?.address}`);
}
}
Loader {
active: Hyprland.activeClient?.floating
active: Hyprland.activeToplevel?.lastIpcObject.floating
asynchronous: true
Layout.fillWidth: active
Layout.leftMargin: active ? 0 : -parent.spacing
@ -129,10 +129,10 @@ ColumnLayout {
sourceComponent: Button {
color: Colours.palette.m3secondaryContainer
onColor: Colours.palette.m3onSecondaryContainer
text: Hyprland.activeClient?.lastIpcObject.pinned ? qsTr("Unpin") : qsTr("Pin")
text: Hyprland.activeToplevel?.lastIpcObject.pinned ? qsTr("Unpin") : qsTr("Pin")
function onClicked(): void {
Hyprland.dispatch(`pin address:${Hyprland.activeClient?.address}`);
Hyprland.dispatch(`pin address:0x${Hyprland.activeToplevel?.address}`);
}
}
}
@ -143,7 +143,7 @@ ColumnLayout {
text: qsTr("Kill")
function onClicked(): void {
Hyprland.dispatch(`movetoworkspace ${wsId},address:${Hyprland.activeClient?.address}`);
Hyprland.dispatch(`movetoworkspace ${wsId},address:0x${Hyprland.activeToplevel?.address}`);
}
}
}

View file

@ -14,7 +14,7 @@ ColumnLayout {
Label {
Layout.topMargin: Appearance.padding.large * 2
text: Hyprland.activeClient?.title ?? qsTr("No active client")
text: Hyprland.activeToplevel?.title ?? qsTr("No active client")
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
font.pointSize: Appearance.font.size.large
@ -22,7 +22,7 @@ ColumnLayout {
}
Label {
text: Hyprland.activeClient?.wmClass ?? qsTr("No active client")
text: Hyprland.activeToplevel?.lastIpcObject.class ?? qsTr("No active client")
color: Colours.palette.m3tertiary
font.pointSize: Appearance.font.size.larger
@ -41,31 +41,31 @@ ColumnLayout {
Detail {
icon: "location_on"
text: qsTr("Address: %1").arg(Hyprland.activeClient?.address ?? "unknown")
text: qsTr("Address: %1").arg(`0x${Hyprland.activeToplevel?.address}` ?? "unknown")
color: Colours.palette.m3primary
}
Detail {
icon: "location_searching"
text: qsTr("Position: %1, %2").arg(Hyprland.activeClient?.x ?? -1).arg(Hyprland.activeClient?.y ?? -1)
text: qsTr("Position: %1, %2").arg(Hyprland.activeToplevel?.lastIpcObject.at[0] ?? -1).arg(Hyprland.activeToplevel?.lastIpcObject.at[1] ?? -1)
}
Detail {
icon: "resize"
text: qsTr("Size: %1 x %2").arg(Hyprland.activeClient?.width ?? -1).arg(Hyprland.activeClient?.height ?? -1)
text: qsTr("Size: %1 x %2").arg(Hyprland.activeToplevel?.lastIpcObject.size[0] ?? -1).arg(Hyprland.activeToplevel?.lastIpcObject.size[1] ?? -1)
color: Colours.palette.m3tertiary
}
Detail {
icon: "workspaces"
text: qsTr("Workspace: %1 (%2)").arg(Hyprland.activeClient?.workspace.name ?? -1).arg(Hyprland.activeClient?.workspace.id ?? -1)
text: qsTr("Workspace: %1 (%2)").arg(Hyprland.activeToplevel?.workspace.name ?? -1).arg(Hyprland.activeToplevel?.workspace.id ?? -1)
color: Colours.palette.m3secondary
}
Detail {
icon: "desktop_windows"
text: {
const mon = Hyprland.activeClient?.monitor;
const mon = Hyprland.activeToplevel?.monitor;
if (mon)
return qsTr("Monitor: %1 (%2) at %3, %4").arg(mon.name).arg(mon.id).arg(mon.x).arg(mon.y);
return qsTr("Monitor: unknown");
@ -74,42 +74,42 @@ ColumnLayout {
Detail {
icon: "page_header"
text: qsTr("Initial title: %1").arg(Hyprland.activeClient?.initialTitle ?? "unknown")
text: qsTr("Initial title: %1").arg(Hyprland.activeToplevel?.lastIpcObject.initialTitle ?? "unknown")
color: Colours.palette.m3tertiary
}
Detail {
icon: "category"
text: qsTr("Initial class: %1").arg(Hyprland.activeClient?.initialClass ?? "unknown")
text: qsTr("Initial class: %1").arg(Hyprland.activeToplevel?.lastIpcObject.initialClass ?? "unknown")
}
Detail {
icon: "account_tree"
text: qsTr("Process id: %1").arg(Hyprland.activeClient?.pid ?? -1)
text: qsTr("Process id: %1").arg(Hyprland.activeToplevel?.lastIpcObject.pid ?? -1)
color: Colours.palette.m3primary
}
Detail {
icon: "picture_in_picture_center"
text: qsTr("Floating: %1").arg(Hyprland.activeClient?.floating ? "yes" : "no")
text: qsTr("Floating: %1").arg(Hyprland.activeToplevel?.lastIpcObject.floating ? "yes" : "no")
color: Colours.palette.m3secondary
}
Detail {
icon: "gradient"
text: qsTr("Xwayland: %1").arg(Hyprland.activeClient?.lastIpcObject.xwayland ? "yes" : "no")
text: qsTr("Xwayland: %1").arg(Hyprland.activeToplevel?.lastIpcObject.xwayland ? "yes" : "no")
}
Detail {
icon: "keep"
text: qsTr("Pinned: %1").arg(Hyprland.activeClient?.lastIpcObject.pinned ? "yes" : "no")
text: qsTr("Pinned: %1").arg(Hyprland.activeToplevel?.lastIpcObject.pinned ? "yes" : "no")
color: Colours.palette.m3secondary
}
Detail {
icon: "fullscreen"
text: {
const fs = Hyprland.activeClient?.fullscreen;
const fs = Hyprland.activeToplevel?.lastIpcObject.fullscreen;
if (fs)
return qsTr("Fullscreen state: %1").arg(fs == 0 ? "off" : fs == 1 ? "maximised" : "on");
return qsTr("Fullscreen state: unknown");

View file

@ -34,7 +34,7 @@ Item {
Loader {
anchors.centerIn: parent
active: !Hyprland.activeClient
active: !Hyprland.activeToplevel
asynchronous: true
sourceComponent: ColumnLayout {
@ -69,10 +69,10 @@ Item {
anchors.centerIn: parent
captureSource: Hyprland.activeClient ? ToplevelManager.activeToplevel : null
captureSource: Hyprland.activeToplevel?.wayland ?? null
live: true
constraintSize.width: parent.height * Math.min(screen.width / screen.height, Hyprland.activeClient.width / Hyprland.activeClient.height)
constraintSize.width: parent.height * Math.min(root.screen.width / root.screen.height, Hyprland.activeToplevel?.lastIpcObject.size[0] / Hyprland.activeToplevel?.lastIpcObject.size[1])
constraintSize.height: parent.height
}
}
@ -86,11 +86,11 @@ Item {
animate: true
text: {
const client = Hyprland.activeClient;
const client = Hyprland.activeToplevel;
if (!client)
return qsTr("No active client");
const mon = Hyprland.monitors.values[Hyprland.activeClient.lastIpcObject.monitor];
const mon = client.monitor;
return qsTr("%1 on monitor %2 at %3, %4").arg(client.title).arg(mon.name).arg(client.x).arg(client.y);
}
}

View file

@ -1,114 +1,37 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import QtQuick
Singleton {
id: root
readonly property list<Client> clients: []
readonly property var toplevels: Hyprland.toplevels
readonly property var workspaces: Hyprland.workspaces
readonly property var monitors: Hyprland.monitors
property Client activeClient: null
readonly property HyprlandWorkspace activeWorkspace: focusedMonitor?.activeWorkspace ?? null
readonly property HyprlandToplevel activeToplevel: Hyprland.activeToplevel
readonly property HyprlandWorkspace focusedWorkspace: Hyprland.focusedWorkspace
readonly property HyprlandMonitor focusedMonitor: Hyprland.focusedMonitor
readonly property int activeWsId: activeWorkspace?.id ?? 1
property point cursorPos
function reload() {
Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors();
getClients.running = true;
getActiveClient.running = true;
}
readonly property int activeWsId: focusedWorkspace?.id ?? 1
function dispatch(request: string): void {
Hyprland.dispatch(request);
}
Component.onCompleted: reload()
Connections {
target: Hyprland
function onRawEvent(event: HyprlandEvent): void {
if (!event.name.endsWith("v2"))
root.reload();
if (event.name.endsWith("v2"))
return;
if (event.name.includes("mon"))
Hyprland.refreshMonitors();
else if (event.name.includes("workspace"))
Hyprland.refreshWorkspaces();
else
Hyprland.refreshToplevels();
}
}
Process {
id: getClients
command: ["hyprctl", "-j", "clients"]
stdout: StdioCollector {
onStreamFinished: {
const clients = JSON.parse(text);
const rClients = root.clients;
const destroyed = rClients.filter(rc => !clients.find(c => c.address === rc.address));
for (const client of destroyed)
rClients.splice(rClients.indexOf(client), 1).forEach(c => c.destroy());
for (const client of clients) {
const match = rClients.find(c => c.address === client.address);
if (match) {
match.lastIpcObject = client;
} else {
rClients.push(clientComp.createObject(root, {
lastIpcObject: client
}));
}
}
}
}
}
Process {
id: getActiveClient
command: ["hyprctl", "-j", "activewindow"]
stdout: StdioCollector {
onStreamFinished: {
const client = JSON.parse(text);
const rClient = root.activeClient;
if (client.address) {
if (rClient)
rClient.lastIpcObject = client;
else
root.activeClient = clientComp.createObject(root, {
lastIpcObject: client
});
} else if (rClient) {
rClient.destroy();
root.activeClient = null;
}
}
}
}
component Client: QtObject {
required property var lastIpcObject
readonly property string address: lastIpcObject.address
readonly property string wmClass: lastIpcObject.class
readonly property string title: lastIpcObject.title
readonly property string initialClass: lastIpcObject.initialClass
readonly property string initialTitle: lastIpcObject.initialTitle
readonly property int x: lastIpcObject.at[0]
readonly property int y: lastIpcObject.at[1]
readonly property int width: lastIpcObject.size[0]
readonly property int height: lastIpcObject.size[1]
readonly property HyprlandWorkspace workspace: Hyprland.workspaces.values.find(w => w.id === lastIpcObject.workspace.id) ?? null
readonly property HyprlandMonitor monitor: Hyprland.monitors.values.find(m => m.id === lastIpcObject.monitor) ?? null
readonly property bool floating: lastIpcObject.floating
readonly property bool fullscreen: lastIpcObject.fullscreen
readonly property int pid: lastIpcObject.pid
readonly property int focusHistoryId: lastIpcObject.focusHistoryID
}
Component {
id: clientComp
Client {}
}
}