import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Wayland import qs.hyprchrome.widgets.host import qs.hyprchrome.widgets.vitals import qs.hyprchrome.widgets.workspaces import qs.hyprchrome.widgets.tray // The dense status rail itself: one layer surface holding the panel row. // // It owns nothing shared — the screen, the density and its layer all arrive // from HyprChromeShell, which is also what keeps this surface and the backdrop // one layer apart. What it does own is its own measurement: `contentHeight` is // the settled height of the current density, which the shell hands to the // backdrop and which sizes the exclusive zone. PanelWindow { id: window // Density for every panel on the rail; driven by the shell. property bool expanded: false // Which layer to sit on. An int rather than a private decision: it is half // of a pair with the backdrop's, so the shell derives both. See // HyprChromeShell. property int wlrLayer: WlrLayer.Overlay // Own namespace so a layerrule can exempt the rail from Hyprland's layer // animation without also catching the launchers, which share the default // "quickshell" namespace and do want their fade. WlrLayershell.namespace: "hyprchrome-bar" WlrLayershell.layer: window.wlrLayer property int margin: 12 // The surface never resizes: it is always tall enough for the expanded // rail, and only the exclusive zone tracks the current state. Resizing a // layer surface makes Hyprland animate the change, which showed up as the // panels twitching a pixel or two the moment the collapse finished. // // The zone still follows the target height, so tiled windows reflow once // per toggle, at the start, and slide while the panels animate. readonly property real expandedContent: Math.max(hostPanel.expandedHeight, workspacesPanel.expandedHeight, vitalsPanel.expandedHeight, trayPanel.expandedHeight) + window.margin * 2 readonly property real contentHeight: Math.max(hostPanel.targetHeight, workspacesPanel.targetHeight, vitalsPanel.targetHeight, trayPanel.targetHeight) + window.margin * 2 implicitHeight: Math.round(window.expandedContent) exclusionMode: ExclusionMode.Normal exclusiveZone: Math.round(window.contentHeight) // Only the panels take input. Without this the surface would keep eating // clicks across its full height while the rail is collapsed. mask: Region { item: panelRow } anchors { top: true; left: true; right: true; } color: "transparent" RowLayout { id: panelRow x: window.margin y: window.margin width: window.width - window.margin * 2 spacing: 0 HostPanel { id: hostPanel expanded: window.expanded // Workspaces butt against its right edge; the left end of the row is free. rightChamfer: false toggleOnClick: false Layout.preferredWidth: 400 Layout.fillHeight: true } WorkspacesPanel { id: workspacesPanel expanded: window.expanded // Mid-rail: a panel on either side, so neither corner is cut. leftChamfer: false rightChamfer: false toggleOnClick: false // No preferred width: the panel sizes itself to however many outputs // the session has, the same way the tray sizes itself to its items. Layout.fillHeight: true } VitalsPanel { id: vitalsPanel expanded: window.expanded // Workspaces on the left, the spacer on the right — and a spacer is not // a panel, so that edge keeps its cut. leftChamfer: false toggleOnClick: false Layout.preferredWidth: 500 Layout.fillHeight: true } // Slack lives between the left group and the tray, so the tray sits // flush right whatever the other panels measure. Item { Layout.fillWidth: true } TrayPanel { id: trayPanel expanded: window.expanded toggleOnClick: false Layout.fillHeight: true } } }