425 lines
12 KiB
QML
425 lines
12 KiB
QML
import qs.config
|
||
import qs.custom
|
||
import qs.services
|
||
import qs.util
|
||
import Quickshell
|
||
import Quickshell.Widgets
|
||
import Quickshell.Hyprland
|
||
import Quickshell.Wayland
|
||
import QtQuick
|
||
import QtQuick.Layouts
|
||
|
||
Item {
|
||
id: root
|
||
|
||
required property PersistentProperties uiState
|
||
required property Item wrapper
|
||
required property HyprlandToplevel window
|
||
|
||
property HyprlandToplevel toplevel: Hypr.activeToplevel
|
||
property var screen: QsWindow.window.screen
|
||
property bool pinned: false
|
||
|
||
implicitWidth: childrenRect.width
|
||
implicitHeight: childrenRect.height
|
||
|
||
Component.onCompleted: {
|
||
if (window) {
|
||
state = "detail";
|
||
pinned = true;
|
||
toplevel = window;
|
||
}
|
||
wrapper.window = null;
|
||
}
|
||
|
||
// States
|
||
|
||
states: [
|
||
State {
|
||
name: "detail"
|
||
StateChangeScript {
|
||
script: root.wrapper.persistent = true
|
||
}
|
||
ParentChange {
|
||
target: header
|
||
parent: infobox
|
||
}
|
||
PropertyChanges {
|
||
infobox { visible: true }
|
||
preview { visible: true }
|
||
pin { visible: true }
|
||
visit { visible: true }
|
||
del { visible: true }
|
||
expand { rotation: -90 }
|
||
title { maximumLineCount: 1 }
|
||
}
|
||
}
|
||
]
|
||
|
||
transitions: Transition {
|
||
Anim {
|
||
targets: [infobox, preview]
|
||
property: "scale"
|
||
from: 0; to: 1
|
||
}
|
||
Anim {
|
||
target: buttons
|
||
property: "opacity"
|
||
from: 0; to: 1
|
||
duration: Config.anim.durations.large * 2
|
||
}
|
||
Anim {
|
||
targets: header
|
||
property: "scale"
|
||
to: 1
|
||
}
|
||
}
|
||
|
||
// Reveal on window title change
|
||
// (or close if window is invalid)
|
||
Anim on opacity {
|
||
id: reveal
|
||
from: 0
|
||
to: 1
|
||
}
|
||
|
||
onToplevelChanged: {
|
||
root.opacity = 0;
|
||
if (!toplevel) {
|
||
root.wrapper.hasCurrent = false;
|
||
} else if (!root.pinned) {
|
||
reveal.restart();
|
||
} else {
|
||
root.opacity = 1;
|
||
}
|
||
}
|
||
|
||
RowLayout {
|
||
id: layout
|
||
|
||
spacing: 15
|
||
|
||
RowLayout {
|
||
id: header
|
||
|
||
spacing: 12
|
||
|
||
Binding {
|
||
when: infobox.visible
|
||
header {
|
||
anchors.left: infobox.left
|
||
anchors.right: infobox.right
|
||
anchors.top: infobox.top
|
||
anchors.margins: 12
|
||
}
|
||
}
|
||
|
||
IconImage {
|
||
id: icon
|
||
|
||
Layout.alignment: Qt.AlignVCenter
|
||
implicitSize: 36
|
||
source: Icons.getAppIcon(toplevel?.lastIpcObject.class ?? "", "")
|
||
}
|
||
|
||
ColumnLayout {
|
||
id: names
|
||
|
||
spacing: 0
|
||
Layout.fillWidth: true
|
||
Layout.maximumWidth: 400
|
||
|
||
CustomText {
|
||
id: title
|
||
|
||
Layout.fillWidth: true
|
||
text: toplevel?.title ?? ""
|
||
color: Config.colors.secondary
|
||
elide: Text.ElideRight
|
||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||
|
||
Behavior on text {
|
||
Anim {
|
||
target: names
|
||
property: "opacity"
|
||
from: 0
|
||
to: 1
|
||
}
|
||
}
|
||
}
|
||
|
||
CustomText {
|
||
Layout.fillWidth: true
|
||
text: toplevel?.lastIpcObject.class ?? ""
|
||
font.pointSize: Config.font.size.small
|
||
color: Config.colors.tertiary
|
||
elide: Text.ElideRight
|
||
|
||
Behavior on text {
|
||
Anim {
|
||
target: names
|
||
property: "opacity"
|
||
from: 0
|
||
to: 1
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
CustomRect {
|
||
id: infobox
|
||
visible: false
|
||
transformOrigin: Item.TopRight
|
||
|
||
implicitWidth: 300
|
||
implicitHeight: 240
|
||
|
||
color: Config.colors.container
|
||
radius: 17
|
||
|
||
ColumnLayout {
|
||
id: infolayout
|
||
anchors.top: parent.top
|
||
anchors.left: parent.left
|
||
anchors.right: parent.right
|
||
anchors.topMargin: 48
|
||
anchors.margins: 12
|
||
|
||
spacing: 3
|
||
|
||
|
||
CustomRect {
|
||
color: Config.colors.inactive
|
||
|
||
Layout.fillWidth: true
|
||
Layout.preferredHeight: 1
|
||
Layout.topMargin: 8
|
||
Layout.bottomMargin: 6
|
||
Layout.leftMargin: 5
|
||
Layout.rightMargin: 5
|
||
}
|
||
|
||
Detail {
|
||
icon: "workspaces"
|
||
text: {
|
||
for (let i = 0; i < root.uiState.workspaces.count; i++) {
|
||
if (root.uiState.workspaces.get(i).workspace === root.toplevel?.workspace)
|
||
return qsTr("Workspace: %1").arg(i + 1)
|
||
}
|
||
return qsTr("Workspace unknown")
|
||
}
|
||
}
|
||
|
||
Detail {
|
||
icon: "desktop_windows"
|
||
text: {
|
||
const mon = root.toplevel?.monitor;
|
||
mon ? qsTr("Monitor: %1 (%2)").arg(mon.name).arg(mon.id) : qsTr("Monitor: unknown")
|
||
}
|
||
}
|
||
Detail {
|
||
icon: "location_on"
|
||
text: qsTr("Address: %1").arg(`0x${root.toplevel?.address}` ?? "unknown")
|
||
}
|
||
|
||
Detail {
|
||
icon: "location_searching"
|
||
text: qsTr("Position: %1, %2").arg(root.toplevel?.lastIpcObject.at[0] ?? -1).arg(root.toplevel?.lastIpcObject.at[1] ?? -1)
|
||
}
|
||
|
||
Detail {
|
||
icon: "resize"
|
||
text: qsTr("Size: %1 ⨯ %2").arg(root.toplevel?.lastIpcObject.size[0] ?? -1).arg(root.toplevel?.lastIpcObject.size[1] ?? -1)
|
||
color: Config.colors.tertiary
|
||
}
|
||
|
||
Detail {
|
||
icon: "account_tree"
|
||
text: qsTr("Process id: %1").arg(Number(root.toplevel?.lastIpcObject.pid ?? -1).toLocaleString(undefined, "f"))
|
||
color: Config.colors.primary
|
||
}
|
||
|
||
Detail {
|
||
icon: "gradient"
|
||
text: qsTr("Xwayland: %1").arg(root.toplevel?.lastIpcObject.xwayland ? "yes" : "no")
|
||
}
|
||
|
||
Detail {
|
||
icon: "picture_in_picture_center"
|
||
text: qsTr("Floating: %1").arg(root.toplevel?.lastIpcObject.floating ? "yes" : "no")
|
||
color: Config.colors.secondary
|
||
}
|
||
}
|
||
}
|
||
|
||
ClippingWrapperRectangle {
|
||
id: preview
|
||
visible: false
|
||
|
||
Layout.alignment: Qt.AlignVCenter
|
||
|
||
transformOrigin: Item.TopLeft
|
||
color: "transparent"
|
||
radius: 12
|
||
|
||
ScreencopyView {
|
||
captureSource: root.toplevel?.wayland ?? null
|
||
live: true
|
||
|
||
constraintSize.width: 375
|
||
constraintSize.height: 240
|
||
}
|
||
}
|
||
|
||
Item {
|
||
id: buttons
|
||
|
||
Layout.fillHeight: true
|
||
width: buttonTopLayout.width
|
||
|
||
property real buttonSize: 37
|
||
|
||
ColumnLayout {
|
||
id: buttonTopLayout
|
||
spacing: 2
|
||
anchors.top: parent.top
|
||
|
||
StateLayer {
|
||
id: expand
|
||
|
||
implicitWidth: buttons.buttonSize
|
||
implicitHeight: buttons.buttonSize
|
||
|
||
function onClicked(): void {
|
||
if (root.state === "")
|
||
root.state = "detail";
|
||
else
|
||
root.wrapper.hasCurrent = false;
|
||
}
|
||
|
||
MaterialIcon {
|
||
anchors.centerIn: parent
|
||
anchors.horizontalCenterOffset: font.pointSize * 0.05
|
||
|
||
text: "chevron_right"
|
||
font.pointSize: Config.font.size.large
|
||
}
|
||
}
|
||
|
||
StateLayer {
|
||
id: pin
|
||
visible: false
|
||
|
||
implicitWidth: buttons.buttonSize
|
||
implicitHeight: buttons.buttonSize
|
||
|
||
function onClicked(): void {
|
||
if (root.pinned) {
|
||
root.pinned = false;
|
||
root.toplevel = Qt.binding(() => Hypr.activeToplevel);
|
||
} else {
|
||
root.pinned = true;
|
||
root.toplevel = root.toplevel;
|
||
}
|
||
}
|
||
|
||
MaterialIcon {
|
||
anchors.centerIn: parent
|
||
|
||
text: "keep"
|
||
fill: root.pinned
|
||
font.pointSize: Config.font.size.large - 3
|
||
}
|
||
}
|
||
}
|
||
|
||
ColumnLayout {
|
||
id: buttonBottomLayout
|
||
anchors.bottom: parent.bottom
|
||
spacing: 2
|
||
|
||
StateLayer {
|
||
id: visit
|
||
visible: false
|
||
|
||
implicitWidth: buttons.buttonSize
|
||
implicitHeight: buttons.buttonSize
|
||
|
||
disabled: Hypr.activeToplevel === root.toplevel
|
||
function onClicked(): void {
|
||
if (root.toplevel)
|
||
Hypr.dispatch(`focuswindow address:0x${root.toplevel.address}`);
|
||
}
|
||
|
||
MaterialIcon {
|
||
anchors.centerIn: parent
|
||
|
||
text: "flip_to_front"
|
||
color: parent.disabled ? Config.colors.inactive : Config.colors.primary
|
||
font.pointSize: Config.font.size.large - 3
|
||
|
||
Behavior on color {
|
||
CAnim {}
|
||
}
|
||
}
|
||
}
|
||
|
||
StateLayer {
|
||
id: del
|
||
visible: false
|
||
|
||
Layout.bottomMargin: 8
|
||
color: Config.colors.error
|
||
|
||
implicitWidth: buttons.buttonSize
|
||
implicitHeight: buttons.buttonSize
|
||
|
||
function onClicked(): void {
|
||
if (root.toplevel)
|
||
Hypr.dispatch(`killwindow address:0x${root.toplevel.address}`);
|
||
root.wrapper.hasCurrent = false;
|
||
}
|
||
|
||
MaterialIcon {
|
||
anchors.centerIn: parent
|
||
|
||
text: "delete"
|
||
color: Config.colors.error
|
||
font.pointSize: Config.font.size.large - 3
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
component Detail: RowLayout {
|
||
id: detail
|
||
|
||
required property string icon
|
||
required property string text
|
||
property alias color: icon.color
|
||
|
||
Layout.fillWidth: true
|
||
|
||
spacing: 7
|
||
|
||
MaterialIcon {
|
||
id: icon
|
||
|
||
Layout.alignment: Qt.AlignVCenter
|
||
font.pointSize: Config.font.size.smaller
|
||
text: detail.icon
|
||
}
|
||
|
||
CustomText {
|
||
Layout.fillWidth: true
|
||
Layout.alignment: Qt.AlignVCenter
|
||
|
||
text: detail.text
|
||
elide: Text.ElideRight
|
||
font.family: Config.font.family.mono
|
||
font.pointSize: Config.font.size.smaller
|
||
}
|
||
}
|
||
}
|