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
73 lines
2.2 KiB
QML
73 lines
2.2 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import Quickshell
|
|
import Quickshell.Services.SystemTray
|
|
import QtQuick
|
|
import QtQuick.Shapes
|
|
import qs.HyprChrome.Theme
|
|
|
|
// One tray item as a chamfered cell. Declares `modelData` required so it can be
|
|
// a Repeater delegate directly, without an Item wrapper in between.
|
|
//
|
|
// Left click activates, right click opens the item's own menu — anchored under
|
|
// the cell rather than at the window edge, since this rail sits along the top.
|
|
MouseArea {
|
|
id: cell
|
|
|
|
required property SystemTrayItem modelData
|
|
|
|
property int iconSize: 18
|
|
property int chamfer: 4
|
|
|
|
implicitWidth: cell.iconSize + 8
|
|
implicitHeight: cell.iconSize + 8
|
|
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
|
|
onClicked: event => {
|
|
if (event.button === Qt.LeftButton) {
|
|
cell.modelData.activate();
|
|
} else if (cell.modelData.hasMenu) {
|
|
const window = cell.QsWindow?.window;
|
|
if (window) {
|
|
const anchor = cell.mapToItem(null, 0, cell.height);
|
|
cell.modelData.display(window, anchor.x, anchor.y);
|
|
}
|
|
}
|
|
event.accepted = true;
|
|
}
|
|
|
|
Shape {
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
|
|
ShapePath {
|
|
strokeWidth: 1
|
|
strokeColor: cell.containsMouse ? Theme.accent : Theme.textAlpha(0.18)
|
|
fillColor: cell.containsMouse ? Theme.selection : Theme.textAlpha(0.06)
|
|
|
|
startX: cell.chamfer
|
|
startY: 0
|
|
PathLine { x: cell.width; y: 0 }
|
|
PathLine { x: cell.width; y: cell.height - cell.chamfer }
|
|
PathLine { x: cell.width - cell.chamfer; y: cell.height }
|
|
PathLine { x: 0; y: cell.height }
|
|
PathLine { x: 0; y: cell.chamfer }
|
|
PathLine { x: cell.chamfer; y: 0 }
|
|
|
|
Behavior on strokeColor { ColorAnimation { duration: 150 } }
|
|
}
|
|
}
|
|
|
|
Image {
|
|
anchors.centerIn: parent
|
|
source: cell.modelData.icon
|
|
width: cell.iconSize
|
|
height: cell.iconSize
|
|
fillMode: Image.PreserveAspectFit
|
|
smooth: true
|
|
}
|
|
}
|