pragma ComponentBehavior: Bound import Quickshell import Quickshell.Wayland import QtQuick import qs.hyprchrome.theme // Full-screen scrim behind the bar: dims the desktop and lays the dense bar's // drafting grid over it while the rail is expanded. // // Sits on the TOP layer while the bar itself is on OVERLAY. Two surfaces on the // same layer stack by creation order, which is not something to rely on; one // layer apart is a guarantee — above ordinary windows, below the bar. // // It reserves nothing and takes no input: the mask is an empty Region, so // clicks land on whatever is underneath rather than on the scrim. PanelWindow { id: backdrop property bool active: true property real dim: 0.55 property int gridSpacing: 120 // The dense bar drew this grid at 0.018 against its own near-black panel. // Over a 55% scrim on top of lit windows that is invisible, so it is a // knob rather than a constant. property real gridOpacity: 0.1 // The accent with its saturation pulled back: warm enough to read as part // of the palette, not so loud that a full-screen grid competes with the // bar. Derived rather than a literal so it tracks a palette change. // Registration crosses sit on every other intersection of the grid. property color crossColor: Theme.muted property real crossOpacity: 0.35 property int crossSize: 20 // Thickness in STEPS, not pixels: 1 -> 1px, 2 -> 3px, 3 -> 5px. Only odd // widths can straddle a 1px rule symmetrically, so an even pixel count // would push every mark half a pixel off the grid it registers against. property int crossThickness: 2 readonly property int crossWeight: Math.max(1, backdrop.crossThickness) * 2 - 1 property color gridColor: Qt.hsla(Theme.accent.hslHue, Theme.accent.hslSaturation * 0.45, Theme.accent.hslLightness, 1) visible: scrim.opacity > 0 WlrLayershell.namespace: "hyprchrome-scrim" WlrLayershell.layer: WlrLayer.Top WlrLayershell.keyboardFocus: WlrKeyboardFocus.None exclusionMode: ExclusionMode.Ignore color: "transparent" readonly property int crossColumns: Math.ceil(backdrop.width / (backdrop.gridSpacing * 2)) + 1 readonly property int crossRows: Math.ceil(backdrop.height / (backdrop.gridSpacing * 2)) + 1 anchors { top: true left: true right: true bottom: true } mask: Region {} Item { id: scrim anchors.fill: parent opacity: backdrop.active ? 1 : 0 // Matched to the bar's own collapse so the scrim and the panels resolve // together rather than one trailing the other. Behavior on opacity { NumberAnimation { duration: 200 easing.type: Easing.OutCubic } } Rectangle { anchors.fill: parent color: Theme.surface opacity: backdrop.dim } // Faint drafting grid; no gradient and deliberately subordinate to // whatever is showing through it. Repeater { model: Math.ceil(scrim.width / backdrop.gridSpacing) Rectangle { required property int index x: index * backdrop.gridSpacing width: 1 height: scrim.height color: backdrop.gridColor opacity: backdrop.gridOpacity } } Repeater { model: Math.ceil(scrim.height / backdrop.gridSpacing) Rectangle { required property int index y: index * backdrop.gridSpacing width: scrim.width height: 1 color: backdrop.gridColor opacity: backdrop.gridOpacity } } // Register marks on every other line, so they land on a 2x grid rather // than on every crossing — sparse enough to read as drafting registration // rather than as texture. Repeater { model: backdrop.crossColumns * backdrop.crossRows Item { required property int index readonly property int column: index % backdrop.crossColumns readonly property int row: Math.floor(index / backdrop.crossColumns) // Marks on every other rule in both directions, so they line up // in columns as well as rows. Both offsets are whole multiples of // gridSpacing, so every mark lands on a real intersection. // // Math.floor, not /2: the item offset and the bars inside it must // round the same way, or an even crossSize sits half a pixel off the // rule it marks. x: column * backdrop.gridSpacing * 2 - Math.floor(backdrop.crossSize / 2) y: row * backdrop.gridSpacing * 2 - Math.floor(backdrop.crossSize / 2) width: backdrop.crossSize height: backdrop.crossSize opacity: backdrop.crossOpacity Rectangle { // Placed with the same Math.floor the item's own offset uses. // anchors.verticalCenter halves the height unfloored, so an even // crossSize put the 1px bar half a pixel off the rule it marks. y: Math.floor(backdrop.crossSize / 2) - Math.floor(backdrop.crossWeight / 2) width: parent.width height: backdrop.crossWeight color: backdrop.crossColor } Rectangle { x: Math.floor(backdrop.crossSize / 2) - Math.floor(backdrop.crossWeight / 2) width: backdrop.crossWeight height: parent.height color: backdrop.crossColor } } } } }