feat(quickshell): add dense telemetry bar #4
@@ -0,0 +1,162 @@
|
||||
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.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import QtQuick.Shapes
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
import qs.hyprchrome.widgets
|
||||
import qs.hyprchrome.widgets.host
|
||||
import qs.hyprchrome.widgets.vitals
|
||||
@@ -45,12 +46,24 @@ Scope {
|
||||
onPressed: root.toggle()
|
||||
}
|
||||
|
||||
// Scrim first: it is a layer below the bar, so stacking does not depend on
|
||||
// creation order, but keeping the declaration order the same as the visual
|
||||
// order costs nothing.
|
||||
ChromeBackdrop {
|
||||
screen: root.targetScreen
|
||||
active: root.expanded
|
||||
}
|
||||
|
||||
PanelWindow {
|
||||
id: window
|
||||
|
||||
screen: root.targetScreen
|
||||
visible: root.targetScreen !== null
|
||||
|
||||
// One layer above the scrim, so the stacking is guaranteed rather than
|
||||
// dependent on surface creation order. See ChromeBackdrop.
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
|
||||
property int margin: 12
|
||||
|
||||
// Where the panels will SETTLE, not where they are mid-transition. Binding
|
||||
|
||||
Reference in New Issue
Block a user