A chamfer with no neighbour behind it now carries the corner it removed, put back OUTSIDE the panel as a detached accent triangle. capGap is the perpendicular distance from the cut, hence the per-axis shift of capGap over root 2: the cap moves along the cut's normal, not along an axis. The trace joining two caps belongs to the RAIL, not the panel — the run it draws is the gap BETWEEN two panels, which no panel can see. HyprChromeBar filters the row down to panels (a slack Item has no `rightChamfer`, so spacers drop out, and dropping them is exactly what makes a trace span them), then joins each right cap to the next left cap: straight, one 45° step at the midpoint of the gap, straight. It meets the middle of each cap's outward face rather than its tip. TestPanel is a staging slot between two spacers. It counts seconds since load, which is the cheapest proof the panel is live, and standing alone it exercises a cap and a trace at both ends — something a rail of butted panels never does. Restructures the module in the same commit, since the moves and the edits above land in the same files: folders are PascalCase, the bar and its widgets moved under Widgets/Bar, and DebugWindow takes the HyprChrome Theme instead of the legacy one. The two singletons are identical today, so that is not a visual fix — it is a palette edit reaching the debug stage in future. Rename detection needs -M40% to follow HyprChromeBar, which grew past the default similarity threshold. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U19X5LGTxtq4pb4jdNVivv
97 lines
3.9 KiB
QML
97 lines
3.9 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import Quickshell
|
|
import Quickshell.Hyprland
|
|
import Quickshell.Wayland
|
|
import qs.HyprChrome.Widgets.Bar
|
|
import qs.HyprChrome.Widgets
|
|
|
|
// The hyprchrome shell: owns everything the rail's surfaces have to agree on,
|
|
// and instantiates them.
|
|
//
|
|
// State lives here rather than in any one surface because more than one of them
|
|
// reads it, and a second reader is what turns a local property into shared
|
|
// state. Three things qualify so far:
|
|
//
|
|
// * which monitor the shell lives on — every surface has to pick the same one
|
|
// * the density — the whole rail expands and collapses as one, so the toggle
|
|
// and the shortcut that drives it belong to the shell, not to the bar
|
|
// * the layer PAIR — the backdrop must sit exactly one layer below the bar in
|
|
// both densities. Two surfaces on the same layer stack by creation order,
|
|
// which is not something to rely on; one layer apart is a guarantee. Split
|
|
// across two files those two assignments drifted apart and the scrim ended
|
|
// up over the bar, so they are derived together here and passed down.
|
|
//
|
|
// A future widget joins by taking `targetScreen` and `expanded` the same way.
|
|
Scope {
|
|
id: shell
|
|
|
|
// Monitor the rail lives on. Falls back to the FIRST connected screen when
|
|
// the name matches nothing, so the bar still appears on a single-monitor
|
|
// session or after a cable swap (DebugWindow falls back to the last one
|
|
// instead — it wants the secondary).
|
|
property string screenName: "DP-2"
|
|
|
|
// Quickshell.screens is a QML list, not a JS array — no .find() on it.
|
|
readonly property var targetScreen: {
|
|
const screens = Quickshell.screens;
|
|
if (screens.length === 0)
|
|
return null;
|
|
for (let i = 0; i < screens.length; i++) {
|
|
if (screens[i].name === shell.screenName)
|
|
return screens[i];
|
|
}
|
|
return screens[0];
|
|
}
|
|
|
|
// Density is a property of the SHELL: every panel follows it, so the whole
|
|
// rail expands and collapses as one. Panels keep their own animation; only
|
|
// the decision is centralised here.
|
|
property bool expanded: false
|
|
|
|
function toggle() {
|
|
shell.expanded = !shell.expanded;
|
|
}
|
|
|
|
// Expanded the rail is over everything; collapsed it drops below ordinary
|
|
// windows. BOTTOM rather than BACKGROUND for the collapsed bar: it is the
|
|
// lowest level that still leaves a layer underneath for the backdrop, and
|
|
// it keeps the rail off the wallpaper's own level.
|
|
readonly property int barLayer: shell.expanded ? WlrLayer.Overlay : WlrLayer.Bottom
|
|
readonly property int backdropLayer: shell.expanded ? WlrLayer.Top : WlrLayer.Background
|
|
|
|
// SUPER A — see hosts/terra/home/hyprland.nix.
|
|
GlobalShortcut {
|
|
name: "chrome"
|
|
description: "Expand or collapse the hyprchrome bar"
|
|
onPressed: shell.toggle()
|
|
}
|
|
|
|
// Backdrop 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: shell.targetScreen
|
|
active: shell.expanded
|
|
wlrLayer: shell.backdropLayer
|
|
|
|
// Collapsed, the scrim only backs the rail, so it needs the band the
|
|
// rail occupies. contentHeight is the SETTLED height for the current
|
|
// state — it jumps once per toggle rather than tracking the panels
|
|
// frame by frame, so the backdrop animates the change itself instead of
|
|
// chasing a value that is already being animated.
|
|
barHeight: bar.contentHeight
|
|
}
|
|
|
|
HyprChromeBar {
|
|
id: bar
|
|
|
|
screen: shell.targetScreen
|
|
// Set here, not from the window's own `screen`: reading that inside
|
|
// `visible` is circular — a hidden window has no screen to report.
|
|
visible: shell.targetScreen !== null
|
|
expanded: shell.expanded
|
|
wlrLayer: shell.barLayer
|
|
}
|
|
}
|