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 QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
import Quickshell.Wayland
|
||||||
import qs.hyprchrome.widgets
|
import qs.hyprchrome.widgets
|
||||||
import qs.hyprchrome.widgets.host
|
import qs.hyprchrome.widgets.host
|
||||||
import qs.hyprchrome.widgets.vitals
|
import qs.hyprchrome.widgets.vitals
|
||||||
@@ -45,12 +46,24 @@ Scope {
|
|||||||
onPressed: root.toggle()
|
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 {
|
PanelWindow {
|
||||||
id: window
|
id: window
|
||||||
|
|
||||||
screen: root.targetScreen
|
screen: root.targetScreen
|
||||||
visible: root.targetScreen !== null
|
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
|
property int margin: 12
|
||||||
|
|
||||||
// Where the panels will SETTLE, not where they are mid-transition. Binding
|
// Where the panels will SETTLE, not where they are mid-transition. Binding
|
||||||
|
|||||||
Reference in New Issue
Block a user