The square markers, block meters and hard-cornered fills read as boxy next to the porthole. The desktop is now soft throughout: the porthole is the shape everything is built around and every smaller surface echoes it. - Theme gains radiusS / radiusM / radiusL. radiusL is frameRadius - padS, so anything inset padS inside one of the frame's curves is concentric with it. - P5Panel and Skew draw rounded rectangles (Geom.roundedRect) whenever they neither lean nor are chopped, which is always now; the polygon path is kept for shapes brought back on purpose. - Markers are dots; workspace pips, toggles, transport and media chips, notification actions and today's date are pills or discs; the slider handle is round. Meters are one continuous rounded line that glides to its level instead of a row of segments. - Album art is rounded with ClippingRectangle in the bar, the media popout and the dashboard. - Free-standing panels (launcher, ask) drop the corner brackets and take the frame radius. - Fix: a hovered notification's wash was a full-bleed square, so in the drawer's bottom corner it ran past the drawer's curve. It is inset by padS/2 and rounded by radiusL, which keeps it inside the curve. Hyprland: the discord special workspace and its window rule are gone, SUPER+S stays the only scratchpad, and SUPER+D now opens the dashboard (it was on SUPER+I). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
134 lines
4.3 KiB
QML
134 lines
4.3 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Widgets
|
|
import "root:/config"
|
|
import "root:/components"
|
|
|
|
// One line in the result list. Rest is bare — no fill, no border — so a list of
|
|
// forty apps reads as a column of text rather than forty boxes. Only the
|
|
// selected row gets a slab, and it takes the full inverted treatment: white
|
|
// fill, black text. That is the strongest contrast the palette has, which is
|
|
// what the row you are about to launch should be wearing.
|
|
Item {
|
|
id: root
|
|
|
|
required property var entry
|
|
required property bool selected
|
|
property bool hovered: false
|
|
property string badge: ""
|
|
|
|
signal activated
|
|
signal pinRequested
|
|
|
|
implicitHeight: 46
|
|
|
|
// Selection slab. Leans like everything else; drawn as a Skew so the
|
|
// diagonals get analytic antialiasing instead of a pixel staircase.
|
|
Skew {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: -Theme.padS
|
|
anchors.rightMargin: -Theme.padS
|
|
radius: Theme.radiusM
|
|
color: root.selected ? Theme.treatmentSelectedFill : "transparent"
|
|
opacity: root.selected ? 1 : 0
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: Theme.durFast; easing.type: Easing.OutQuad }
|
|
}
|
|
}
|
|
|
|
// Hover is an accent tick in the gutter rather than a second slab, so
|
|
// pointing at a row cannot be mistaken for selecting it.
|
|
Skew {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
x: -Theme.padS
|
|
width: 3
|
|
height: root.hovered && !root.selected ? parent.height - 10 : 0
|
|
color: Theme.accent
|
|
|
|
Behavior on height {
|
|
NumberAnimation { duration: Theme.durFast; easing.type: Easing.OutCubic }
|
|
}
|
|
}
|
|
|
|
Row {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: Theme.padM
|
|
anchors.rightMargin: Theme.padM
|
|
spacing: Theme.padM
|
|
|
|
IconImage {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
implicitSize: 26
|
|
asynchronous: true
|
|
source: root.entry.icon ? Quickshell.iconPath(root.entry.icon, true) : ""
|
|
visible: source !== ""
|
|
}
|
|
|
|
// Entries with no icon still need to occupy the same gutter, or their
|
|
// labels wander left and the column stops being a column.
|
|
Icon {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: 26
|
|
text: ""
|
|
color: root.selected ? Theme.treatmentSelectedText : Theme.muted
|
|
visible: !root.entry.icon && root.entry.kind !== "dmenu"
|
|
}
|
|
|
|
Column {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: parent.width - 26 - Theme.padM * 2 - trailing.width
|
|
spacing: -1
|
|
|
|
Text {
|
|
width: parent.width
|
|
text: root.entry.name
|
|
elide: Text.ElideRight
|
|
color: root.selected ? Theme.treatmentSelectedText : Theme.text
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: Theme.fsBody
|
|
font.weight: root.selected ? Font.Bold : Theme.weightBody
|
|
renderType: Text.NativeRendering
|
|
}
|
|
|
|
Text {
|
|
width: parent.width
|
|
text: root.entry.sub
|
|
visible: text !== ""
|
|
elide: Text.ElideRight
|
|
color: root.selected ? Theme.alpha(Theme.ink, 0.6) : Theme.muted
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: Theme.fsMicro
|
|
renderType: Text.NativeRendering
|
|
}
|
|
}
|
|
}
|
|
|
|
// Trailing hint: the key that fires this row. Only the selected row shows
|
|
// one, because a column of ⏎ glyphs tells you nothing.
|
|
Text {
|
|
id: trailing
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: Theme.padM
|
|
text: root.selected ? root.badge : ""
|
|
color: Theme.alpha(Theme.ink, 0.55)
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: Theme.fsMicro
|
|
font.letterSpacing: 2
|
|
renderType: Text.NativeRendering
|
|
}
|
|
|
|
HoverHandler {
|
|
onHoveredChanged: root.hovered = hovered
|
|
}
|
|
|
|
TapHandler {
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
onTapped: (point, button) => {
|
|
if (button === Qt.RightButton) root.pinRequested();
|
|
else root.activated();
|
|
}
|
|
}
|
|
}
|