P5Panel, P5Button, P5Slider and P5Tooltip become Panel, ActionButton, LevelSlider and Tooltip. ActionButton and LevelSlider avoid clashing with QtQuick.Controls, which the ask panel imports.
174 lines
6.1 KiB
QML
174 lines
6.1 KiB
QML
import QtQuick
|
|
import "root:/config"
|
|
import "root:/components"
|
|
|
|
// One slot on the right-hand rail: a glyph with an optional micro readout
|
|
// stacked beneath it, and an accent bar that wipes down the leading edge on
|
|
// hover. The rail is only 48px wide, so the readout sits under the glyph
|
|
// rather than beside it, and anything longer than a percentage belongs in the
|
|
// popout instead.
|
|
Item {
|
|
id: root
|
|
|
|
property string icon: ""
|
|
property string readout: ""
|
|
property string label: ""
|
|
property string statusText: ""
|
|
property string hint: ""
|
|
property bool suppressTooltip: false
|
|
property color tint: Theme.primary
|
|
|
|
// Three states, three meanings. `live` wants attention but nothing is wrong
|
|
// (unread notifications, an open power menu): accent. `alarm` is wrong or
|
|
// critical (a dying battery): alarm. `dim` is off by choice (muted, radio
|
|
// off, do-not-disturb): grey. These used to be one `alert` flag painted in
|
|
// the accent, which made a muted speaker look like the most active thing
|
|
// on the rail.
|
|
property bool live: false
|
|
property bool alarm: false
|
|
property bool dim: false
|
|
property bool pulsing: false
|
|
|
|
// Hold the readout line open even while there is nothing to put in it.
|
|
// Volume drops its percentage when muted and the bell only carries a count
|
|
// when something is unread, so without this the slot collapsed from 42px to
|
|
// 32px and every icon below it jumped a third of its own height.
|
|
property bool reserveReadout: false
|
|
|
|
readonly property bool hasReadout: root.readout !== "" || root.reserveReadout
|
|
readonly property bool hovered: hover.hovered
|
|
|
|
// Clearance between the wipe and the rail's left edge, chosen so the bar
|
|
// stays inside the slab once `scale` has expanded the icon about its centre.
|
|
readonly property real wipeInset: 5
|
|
|
|
signal clicked()
|
|
signal scrolled(real delta)
|
|
|
|
implicitWidth: Theme.railWidth
|
|
implicitHeight: root.hasReadout ? root.glyphBand + readoutLine.height + 1 : root.glyphBand
|
|
|
|
// The glyph always sits centred in a fixed band at the top of the slot and
|
|
// the readout hangs below it, rather than the pair being centred together.
|
|
// Centring the pair meant the icon slid up and down by a few px whenever its
|
|
// number appeared or vanished — the icons stopped lining up with each other
|
|
// exactly when something was changing and you were looking at them.
|
|
readonly property real glyphBand: 32
|
|
|
|
// Nerd Font's status glyphs are not all centred inside their own advance —
|
|
// the muted-speaker and muted-mic marks carry their cross out to the right,
|
|
// so centring the advance box left them visibly off-axis next to the wifi
|
|
// and bluetooth glyphs. Centre the ink instead, capped so a glyph with an
|
|
// odd bounding box cannot slide far off the rail's axis.
|
|
readonly property real opticalShift: {
|
|
const ink = glyphMetrics.tightBoundingRect;
|
|
if (ink.width <= 0) return 0;
|
|
const off = glyphMetrics.advanceWidth / 2 - (ink.x + ink.width / 2);
|
|
return Math.max(-4, Math.min(4, off));
|
|
}
|
|
|
|
TextMetrics {
|
|
id: glyphMetrics
|
|
text: root.icon
|
|
font.family: Theme.fontIcon
|
|
font.pixelSize: Theme.fsRailIcon
|
|
}
|
|
|
|
Item {
|
|
id: layout
|
|
anchors.fill: parent
|
|
|
|
Icon {
|
|
id: glyph
|
|
x: (root.width - width) / 2 + root.opticalShift
|
|
y: (root.glyphBand - height) / 2
|
|
text: root.icon
|
|
pulsing: root.pulsing
|
|
font.pixelSize: Theme.fsRailIcon
|
|
color: {
|
|
if (root.alarm) return Theme.alarm;
|
|
if (root.live) return Theme.accent;
|
|
if (root.dim) return Theme.muted;
|
|
if (root.hovered) return Theme.glow;
|
|
return root.tint;
|
|
}
|
|
|
|
Behavior on color {
|
|
ColorAnimation { duration: Theme.durBase }
|
|
}
|
|
}
|
|
|
|
Text {
|
|
id: readoutLine
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.top: parent.top
|
|
anchors.topMargin: root.glyphBand + 1
|
|
visible: root.readout !== ""
|
|
text: root.readout
|
|
color: {
|
|
if (root.alarm) return Theme.alarm;
|
|
if (root.dim) return Theme.muted;
|
|
return Theme.subtext;
|
|
}
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: Theme.fsMicro
|
|
font.weight: Font.DemiBold
|
|
renderType: Text.NativeRendering
|
|
|
|
Behavior on color {
|
|
ColorAnimation { duration: Theme.durBase }
|
|
}
|
|
}
|
|
}
|
|
|
|
// Leading-edge wipe. On the rail this runs vertically down the left side,
|
|
// mirroring the accent bar Panel puts on horizontal modules.
|
|
//
|
|
// Held off the edge by `wipeInset`: the icon spans the rail's full width, so
|
|
// at x = 0 this landed exactly on the slab's own border.
|
|
//
|
|
// It is the accent rather than `tint` because it only ever appears on
|
|
// hover, and hover is the accent everywhere else in the shell (Theme.glow).
|
|
// Painting it white put a white bar on the rail's edge for no stated reason.
|
|
Rectangle {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: root.wipeInset
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: 2
|
|
radius: 1
|
|
height: root.hovered ? Theme.marker * 2 : 0
|
|
color: Theme.glow
|
|
|
|
Behavior on height {
|
|
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutExpo }
|
|
}
|
|
}
|
|
|
|
|
|
onAlarmChanged: if (alarm) glyph.bump()
|
|
onLiveChanged: if (live) glyph.bump()
|
|
|
|
HoverHandler {
|
|
id: hover
|
|
cursorShape: Qt.PointingHandCursor
|
|
}
|
|
|
|
TapHandler {
|
|
acceptedButtons: Qt.LeftButton
|
|
onTapped: root.clicked()
|
|
}
|
|
WheelHandler {
|
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
|
onWheel: event => root.scrolled(event.angleDelta.y)
|
|
}
|
|
|
|
Tooltip {
|
|
anchorItem: root
|
|
triggerHovered: root.hovered
|
|
title: root.label
|
|
status: root.statusText
|
|
hint: root.hint
|
|
suppressed: root.suppressTooltip
|
|
}
|
|
}
|