- Idle is handled in the shell instead of hypridle: the screen dims slowly at 4 minutes, locks at 5, powers down at 6, and suspends at 15 on battery. Idle inhibitors hold every stage off; Super+Shift+I does the same by hand and puts a cup on the rail. - The shell draws the wallpaper (backdrop/), so hyprpaper is gone. The picture is drawn a little larger than the screen and drifts with the workspace a beat after the windows, and dims a shade while windows are open. The path lives in quickshell/wallpaper. - The lock screen and power menu say "Take your time." instead of the NieR slogan. - hyprpaper is no longer launched twice, the media module keeps the player that played last instead of an empty browser tab, and grouped windows show thin untitled tabs with their place in the group (2/3) in the bar title. - qt6ct uses Darkly; the package list gains adw-gtk-theme and darkly-bin.
234 lines
6.9 KiB
QML
234 lines
6.9 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import "root:/config"
|
|
import "root:/components"
|
|
import "root:/services"
|
|
import "root:/popouts"
|
|
|
|
// The right-hand status rail — one slab, not a stack of cards.
|
|
//
|
|
// Connectivity, audio, power, tray and notifications all sit inside a single
|
|
// continuous column, divided by hairline rules the same way the bar divides its
|
|
// modules. Same lean, same chamfer, same keyline as the bar and the dock, so
|
|
// the three surfaces read as one system.
|
|
//
|
|
// On a 1080p panel this is the cheap edge: 48px of width is 2.5% of the screen,
|
|
// where the same 48px of height would be 4.4% on top of what the bar takes.
|
|
PanelWindow {
|
|
id: root
|
|
|
|
required property var modelData
|
|
screen: modelData
|
|
|
|
WlrLayershell.layer: WlrLayer.Top
|
|
WlrLayershell.namespace: "takemi-rail"
|
|
|
|
anchors {
|
|
top: true
|
|
bottom: true
|
|
right: true
|
|
}
|
|
|
|
// Hangs from the underside of the bar, flush with the screen edge. Like
|
|
// the bar it draws no surface of its own; see Frame.
|
|
margins.top: Theme.barHeight
|
|
exclusionMode: ExclusionMode.Ignore
|
|
implicitWidth: Theme.railWidth
|
|
color: "transparent"
|
|
|
|
readonly property real edgeInset: Theme.padM
|
|
|
|
// ------------------------------------------------------------------- top
|
|
Column {
|
|
id: upper
|
|
anchors.top: parent.top
|
|
anchors.topMargin: root.edgeInset
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: 0
|
|
|
|
RailIcon {
|
|
id: netIcon
|
|
icon: Net.icon
|
|
label: "Network"
|
|
statusText: Net.label
|
|
suppressTooltip: netPopout.detailActive
|
|
dim: !Sys.netUp
|
|
onClicked: netPopout.togglePinned()
|
|
}
|
|
|
|
RailIcon {
|
|
id: btIcon
|
|
icon: Bt.icon
|
|
label: "Bluetooth"
|
|
statusText: Bt.label
|
|
suppressTooltip: btPopout.detailActive
|
|
dim: !Bt.enabled
|
|
onClicked: btPopout.togglePinned()
|
|
}
|
|
|
|
Divider {}
|
|
|
|
RailIcon {
|
|
id: volIcon
|
|
icon: Audio.icon
|
|
readout: Audio.muted ? "" : Audio.volumePercent + "%"
|
|
reserveReadout: true
|
|
label: "Volume"
|
|
statusText: Audio.muted ? "Muted" : Audio.volumePercent + "% · " + Audio.sinkName
|
|
hint: "Scroll to adjust"
|
|
suppressTooltip: audioPopout.detailActive
|
|
dim: Audio.muted
|
|
onClicked: audioPopout.togglePinned()
|
|
onScrolled: delta => Audio.changeVolume(delta > 0 ? 0.05 : -0.05)
|
|
}
|
|
|
|
RailIcon {
|
|
id: micIcon
|
|
icon: Audio.micIcon
|
|
label: "Microphone"
|
|
statusText: Audio.micMuted ? "Muted" : Audio.micPercent + "% · " + Audio.sourceName
|
|
hint: "Scroll to adjust"
|
|
suppressTooltip: audioPopout.detailActive
|
|
dim: Audio.micMuted
|
|
onClicked: audioPopout.togglePinned()
|
|
onScrolled: delta => Audio.setMicVolume(Audio.micVolume + (delta > 0 ? 0.05 : -0.05))
|
|
}
|
|
|
|
RailIcon {
|
|
id: brightIcon
|
|
visible: Brightness.available
|
|
icon: Brightness.icon
|
|
readout: Math.round(Brightness.percent) + "%"
|
|
label: "Brightness"
|
|
statusText: Math.round(Brightness.percent) + "%"
|
|
hint: "Scroll to adjust"
|
|
onScrolled: delta => Brightness.change(delta > 0 ? 5 : -5)
|
|
}
|
|
|
|
Divider { visible: Battery.available }
|
|
|
|
RailIcon {
|
|
id: battIcon
|
|
visible: Battery.available
|
|
icon: Battery.icon
|
|
readout: Math.round(Battery.percent) + "%"
|
|
label: "Battery"
|
|
statusText: Math.round(Battery.percent) + "% · " + Battery.timeLabel
|
|
suppressTooltip: battPopout.detailActive
|
|
alarm: Battery.critical
|
|
pulsing: Battery.critical
|
|
onClicked: battPopout.togglePinned()
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------- bottom
|
|
Column {
|
|
id: lower
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: root.edgeInset
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: 0
|
|
|
|
Tray {
|
|
id: tray
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
}
|
|
|
|
Divider { visible: tray.visible }
|
|
|
|
// Only there while the idle chain is held off by hand, so it is
|
|
// obvious why the screen never locked.
|
|
RailIcon {
|
|
id: awakeIcon
|
|
visible: Idle.stayAwake
|
|
icon: ""
|
|
label: "Stay awake"
|
|
statusText: "Screen will not dim or lock"
|
|
hint: "Click to allow sleep"
|
|
live: true
|
|
onClicked: Idle.toggleStayAwake()
|
|
}
|
|
|
|
RailIcon {
|
|
id: bellIcon
|
|
icon: Notifs.icon
|
|
label: "Notifications"
|
|
statusText: Notifs.dnd
|
|
? "Do not disturb"
|
|
: (Notifs.unread > 0 ? Notifs.unread + " unread" : "All caught up")
|
|
readout: Notifs.unread > 0
|
|
? (Notifs.unread > 9 ? "9+" : String(Notifs.unread))
|
|
: ""
|
|
reserveReadout: true
|
|
dim: Notifs.dnd
|
|
live: Notifs.hasUnread && !Notifs.dnd
|
|
suppressTooltip: notifPopout.detailActive
|
|
onClicked: {
|
|
notifPopout.togglePinned();
|
|
Notifs.markRead();
|
|
}
|
|
}
|
|
|
|
RailIcon {
|
|
id: powerIcon
|
|
icon: ""
|
|
label: "Power"
|
|
statusText: "Session actions"
|
|
live: Actions.powerMenuOpen
|
|
onClicked: Actions.togglePowerMenu()
|
|
}
|
|
}
|
|
|
|
// The rail's counterpart to the bar's divider: a horizontal hairline.
|
|
component Divider: Item {
|
|
implicitWidth: Theme.railWidth
|
|
implicitHeight: Theme.padM + 4
|
|
|
|
Rectangle {
|
|
anchors.centerIn: parent
|
|
width: 16
|
|
height: 1
|
|
color: Theme.alpha(Theme.outline, 0.6)
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------- popouts
|
|
// All of these hang off the left edge of the rail, opening into the screen.
|
|
NetPopout {
|
|
id: netPopout
|
|
anchorItem: netIcon
|
|
triggerHovered: netIcon.hovered
|
|
fromEdge: Edges.Left
|
|
}
|
|
|
|
BtPopout {
|
|
id: btPopout
|
|
anchorItem: btIcon
|
|
triggerHovered: btIcon.hovered
|
|
fromEdge: Edges.Left
|
|
}
|
|
|
|
AudioPopout {
|
|
id: audioPopout
|
|
anchorItem: volIcon
|
|
triggerHovered: volIcon.hovered || micIcon.hovered
|
|
fromEdge: Edges.Left
|
|
}
|
|
|
|
BatteryPopout {
|
|
id: battPopout
|
|
anchorItem: battIcon
|
|
triggerHovered: battIcon.hovered
|
|
fromEdge: Edges.Left
|
|
visible: Battery.available && shown
|
|
}
|
|
|
|
NotifPopout {
|
|
id: notifPopout
|
|
anchorItem: bellIcon
|
|
triggerHovered: bellIcon.hovered
|
|
fromEdge: Edges.Left
|
|
}
|
|
}
|