import "root:/services/" import "root:/modules/common" import "root:/modules/common/widgets" import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Widgets import Quickshell.Wayland import Quickshell.Hyprland import "./icons.js" as Icons Item { id: root required property var panelWindow readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen) readonly property var toplevels: ToplevelManager.toplevels readonly property int workspacesShown: ConfigOptions.overview.numOfRows * ConfigOptions.overview.numOfCols readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown) property var windows: HyprlandData.windowList property var windowByAddress: HyprlandData.windowByAddress property var windowAddresses: HyprlandData.addresses property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor.id) property real scale: ConfigOptions.overview.scale property real workspaceImplicitWidth: (monitor.width - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale property real workspaceImplicitHeight: (monitor.height - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale property real workspaceNumberMargin: 80 property real workspaceNumberSize: 80 property int workspaceZ: 0 property int windowZ: 1 property int windowDraggingZ: 99999 property real workspaceSpacing: 5 property int draggingFromWorkspace: -1 property int draggingTargetWorkspace: -1 onDraggingFromWorkspaceChanged: { console.log("draggingTargetWorkspace", draggingFromWorkspace) } implicitWidth: overviewBackground.implicitWidth + Appearance.sizes.elevationMargin * 2 implicitHeight: overviewBackground.implicitHeight + Appearance.sizes.elevationMargin * 2 property Component windowComponent: OverviewWindow {} property list windowWidgets: [] Process { id: closeOverview command: ["bash", "-c", "qs ipc call overview close &"] // Somehow has to be async to work? } Rectangle { id: overviewBackground anchors.fill: parent implicitWidth: workspaceColumnLayout.implicitWidth + 5 * 2 implicitHeight: workspaceColumnLayout.implicitHeight + 5 * 2 color: Appearance.colors.colLayer0 radius: Appearance.rounding.screenRounding * root.scale + 5 * 2 ColumnLayout { id: workspaceColumnLayout z: root.workspaceZ anchors.centerIn: parent spacing: workspaceSpacing Repeater { model: ConfigOptions.overview.numOfRows delegate: RowLayout { id: row property int rowIndex: index spacing: workspaceSpacing Repeater { // Workspace repeater model: ConfigOptions.overview.numOfCols Rectangle { // Workspace id: workspace property int colIndex: index property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * ConfigOptions.overview.numOfCols + colIndex + 1 property color defaultColor: Appearance.colors.colLayer1 // TODO: reconsider this color for a cleaner look implicitWidth: root.workspaceImplicitWidth implicitHeight: root.workspaceImplicitHeight color: defaultColor radius: Appearance.rounding.screenRounding * root.scale border.width: 2 border.color: "transparent" MouseArea { id: workspaceArea anchors.fill: parent acceptedButtons: Qt.LeftButton onClicked: { if (root.draggingTargetWorkspace === -1) { closeOverview.running = true Hyprland.dispatch(`workspace ${workspaceValue}`) } } } DropArea { anchors.fill: parent onEntered: { root.draggingTargetWorkspace = workspaceValue if (root.draggingFromWorkspace == root.draggingTargetWorkspace) return; border.color = Appearance.colors.colLayer2Hover workspace.color = Appearance.mix(defaultColor, Appearance.colors.colLayer1Hover, 0.1) } onExited: { border.color = "transparent" workspace.color = defaultColor if (root.draggingTargetWorkspace == workspaceValue) root.draggingTargetWorkspace = -1 } } } } } } } Item { id: windowSpace anchors.centerIn: parent implicitWidth: workspaceColumnLayout.implicitWidth implicitHeight: workspaceColumnLayout.implicitHeight Repeater { // Window repeater model: windowAddresses.filter((address) => { var win = windowByAddress[address] return (root.workspaceGroup * root.workspacesShown < win.workspace.id && win.workspace.id <= (root.workspaceGroup + 1) * root.workspacesShown) }) delegate: OverviewWindow { id: window windowData: windowByAddress[modelData] monitorData: root.monitorData scale: root.scale availableWorkspaceWidth: root.workspaceImplicitWidth availableWorkspaceHeight: root.workspaceImplicitHeight property bool atInitPosition: (initX == x && initY == y) restrictToWorkspace: Drag.active || atInitPosition property int workspaceColIndex: (windowData?.workspace.id - 1) % ConfigOptions.overview.numOfCols property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / ConfigOptions.overview.numOfCols) xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex Timer { id: updateWindowPosition interval: ConfigOptions.hacks.arbitraryRaceConditionDelay repeat: false running: false onTriggered: { window.x = Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset window.y = Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset } } z: atInitPosition ? root.windowZ : root.windowDraggingZ Drag.hotSpot.x: targetWindowWidth / 2 Drag.hotSpot.y: targetWindowHeight / 2 MouseArea { id: dragArea anchors.fill: parent hoverEnabled: true onEntered: hovered = true onExited: hovered = false acceptedButtons: Qt.LeftButton | Qt.MiddleButton drag.target: parent onPressed: { root.draggingFromWorkspace = windowData?.workspace.id window.pressed = true window.Drag.active = true window.Drag.source = window } onReleased: { const targetWorkspace = root.draggingTargetWorkspace window.pressed = false window.Drag.active = false root.draggingFromWorkspace = -1 if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) { Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`) updateWindowPosition.restart() } else { window.x = window.initX window.y = window.initY } } onClicked: (event) => { if (!windowData) return; if (event.button === Qt.LeftButton) { closeOverview.running = true Hyprland.dispatch(`workspace ${windowData.workspace.id}`) event.accepted = true } else if (event.button === Qt.MiddleButton) { Hyprland.dispatch(`closewindow address:${windowData.address}`) event.accepted = true } } } } } } } DropShadow { z: -9999 anchors.fill: overviewBackground horizontalOffset: 0 verticalOffset: 2 radius: Appearance.sizes.elevationMargin samples: radius * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs color: Appearance.colors.colShadow source: overviewBackground } }