133 lines
4.3 KiB
QML
133 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
|
|
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.OutBack }
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|