Something went wrong. Try again.
dope ass quickshell bar
Something went wrong. Try again.
quickshell Bar.qml
14 kB · 404 lines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405import QtQuick 2.15import Quickshellimport Quickshell.Ioimport Quickshell.Services.SystemTrayimport Quickshell.Services.Pipewireimport Quickshell.Services.UPower
PanelWindow { id: root
property var niriWindowsList: [] property var niriWorkspacesList: []
function px(val) { return val / screen.devicePixelRatio }
visible: true implicitHeight: px(60) exclusiveZone: implicitHeight exclusionMode: ExclusionMode.Auto implicitWidth: screen.width color: Config.color("background")
anchors { top: true }
property bool idleInhibitActive: false
Process { running: root.idleInhibitActive command: ["systemd-inhibit", "--what=idle:sleep", "--who=quickshell-bar", "--why=Idle inhibitor toggled on", "--mode=block", "sleep", "infinity"] }
property var activeTrayMenu: null property int trayMenuAnchorX: 0 property var activeSubmenuEntry: null
TrayMenuPopup { id: trayMenuPopup bar: root }
TraySubmenuPopup { bar: root mainPopup: trayMenuPopup }
AudioPopup { id: audioPopup bar: root }
// single clock tick shared by any clock widget instance property string clockText: Qt.formatDateTime(new Date(), "h:mm AP")
Timer { interval: 1000 running: true repeat: true onTriggered: root.clockText = Qt.formatDateTime(new Date(), "h:mm AP") }
// name -> widget component, used by the section Repeaters below function widgetFor(name) { switch (name) { case "niriWindows": return niriWindowsComp case "idleInhibit": return idleInhibitComp case "audio": return audioComp case "battery": return batteryComp case "wattage": return wattageComp case "tray": return trayComp case "clock": return clockComp default: return null } }
// --- widgets --------------------------------------------------------------- // Each widget defines its own implicit size and centers its content, so the // hosting Loader in a section Row sizes to it automatically.
Component { id: niriWindowsComp NiriWindowList { niriWindowsList: root.niriWindowsList niriWorkspacesList: root.niriWorkspacesList outputName: root.screen.name barColor: root.color } }
Component { id: idleInhibitComp Item { implicitWidth: 28 implicitHeight: 26
Rectangle { anchors.centerIn: parent width: 26 height: 26 radius: 6 color: root.idleInhibitActive ? Config.color("accentBackground") : (idleInhibitArea.containsMouse ? Config.color("surface") : Config.color("transparent")) border.color: root.idleInhibitActive ? Config.color("accent") : (idleInhibitArea.containsMouse ? Config.color("focus") : Config.color("transparent")) border.width: 1
Text { anchors.centerIn: parent text: "" font.family: "Maple Mono" font.pixelSize: 16 color: root.idleInhibitActive ? Config.color("accent") : Config.color("muted") } }
MouseArea { id: idleInhibitArea anchors.fill: parent hoverEnabled: true onClicked: root.idleInhibitActive = !root.idleInhibitActive } } }
Component { id: audioComp Item { id: audio implicitWidth: audioContent.width + 8 implicitHeight: 26
property bool muted: Pipewire.defaultAudioSink?.audio.muted ?? false
Row { id: audioContent anchors.centerIn: parent spacing: 4
Text { anchors.verticalCenter: parent.verticalCenter text: audio.muted ? String.fromCodePoint(0xF04C4) : "" font.family: "Maple Mono" font.pixelSize: 16 color: audio.muted ? Config.color("muted") : Config.color("foreground") }
Text { anchors.verticalCenter: parent.verticalCenter text: Math.round((Pipewire.defaultAudioSink?.audio.volume ?? 0) * 100) + "%" font.family: "Inter" font.pixelSize: 13 font.weight: 700 color: audio.muted ? Config.color("muted") : Config.color("foreground") } }
MouseArea { anchors.fill: parent onWheel: (wheel) => { if (!Pipewire.defaultAudioSink) return var delta = wheel.angleDelta.y > 0 ? 0.01 : -0.01 Pipewire.defaultAudioSink.audio.volume = Math.max(0, Math.min(1, Pipewire.defaultAudioSink.audio.volume + delta)) } onClicked: audioPopup.visible = !audioPopup.visible } } }
Component { id: trayComp Row { spacing: root.px(4)
Repeater { model: SystemTray.items
Item { id: trayItem required property SystemTrayItem modelData implicitWidth: 24 implicitHeight: 26
Image { anchors.centerIn: parent width: 16 height: 16 source: trayItem.modelData.icon sourceSize: Qt.size(16, 16) smooth: true }
MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: function(event) { if (event.button === Qt.LeftButton) { if (trayItem.modelData.hasMenu) { if (trayMenuPopup.visible && root.activeTrayMenu === trayItem.modelData.menu) { trayMenuPopup.visible = false } else { root.trayMenuAnchorX = trayItem.mapToItem(null, 0, 0).x root.activeTrayMenu = trayItem.modelData.menu trayMenuPopup.visible = true } } else { trayItem.modelData.activate() } } } } } } } }
Component { id: clockComp Item { // Size to the text so the clock sits flush against the right margin // (a fixed width + left-aligned text left dead space to the right edge). implicitWidth: clockLabel.width implicitHeight: 26
Text { id: clockLabel anchors.centerIn: parent color: Config.color("foreground") font.family: "Inter" font.pixelSize: 16 font.weight: 700 text: root.clockText } } }
Component { id: batteryComp Item { id: bat property var dev: UPower.displayDevice property bool present: dev && dev.isPresent // Quickshell reports percentage as a 0.0-1.0 fraction; scale to 0-100. property real pct: dev ? dev.percentage * 100 : 0 property int st: dev ? dev.state : 0 property bool charging: st === UPowerDeviceState.Charging || st === UPowerDeviceState.PendingCharge property bool full: st === UPowerDeviceState.FullyCharged property bool low: pct <= 15 && !charging && !full
// Material Design (Nerd Font) battery ramp, reactive on pct/state. readonly property string glyph: { var p = Math.max(0, Math.min(100, pct)) if (full) return String.fromCodePoint(0xF0079) if (charging) { var c = [0xF089C, 0xF0086, 0xF0087, 0xF0088, 0xF089D, 0xF0089, 0xF089E, 0xF008A, 0xF008B, 0xF0085] return String.fromCodePoint(c[Math.min(9, Math.floor(p / 10))]) } if (p >= 95) return String.fromCodePoint(0xF0079) var d = [0xF007A, 0xF007B, 0xF007C, 0xF007D, 0xF007E, 0xF007F, 0xF0080, 0xF0081, 0xF0082] return String.fromCodePoint(d[Math.max(0, Math.min(8, Math.floor(p / 10) - 1))]) }
visible: present implicitWidth: present ? batContent.width + 8 : 0 implicitHeight: 26
Row { id: batContent anchors.centerIn: parent spacing: 4
Text { anchors.verticalCenter: parent.verticalCenter text: bat.glyph font.family: "Maple Mono" font.pixelSize: 16 color: bat.low ? Config.color("negative") : (bat.charging || bat.full ? Config.color("positive") : Config.color("foreground")) }
Text { anchors.verticalCenter: parent.verticalCenter text: Math.round(bat.pct) + "%" font.family: "Inter" font.pixelSize: 13 font.weight: 700 color: bat.low ? Config.color("negative") : Config.color("foreground") } } } }
Component { id: wattageComp Item { id: watt property var dev: UPower.displayDevice property bool present: dev && dev.isPresent property real rate: dev ? dev.changeRate : 0 property int st: dev ? dev.state : 0 property bool charging: st === UPowerDeviceState.Charging || st === UPowerDeviceState.PendingCharge property bool discharging: st === UPowerDeviceState.Discharging || st === UPowerDeviceState.PendingDischarge // Show whenever the battery is actively charging or discharging, regardless of // the instantaneous rate. The rate briefly reads 0 (e.g. just after (un)plugging, // or when the EC returns an empty current_now), which would otherwise flicker the // widget out during discharge. property bool active: present && (charging || discharging)
visible: active implicitWidth: active ? wattContent.width + 8 : 0 implicitHeight: 26
Row { id: wattContent anchors.centerIn: parent spacing: 4
Text { anchors.verticalCenter: parent.verticalCenter text: String.fromCodePoint(0xF140B) // lightning bolt font.family: "Maple Mono" font.pixelSize: 16 color: watt.charging ? Config.color("positive") : Config.color("foreground") }
Text { anchors.verticalCenter: parent.verticalCenter text: (watt.charging ? "+" : "-") + watt.rate.toFixed(1) + " W" font.family: "Inter" font.pixelSize: 13 font.weight: 700 color: Config.color("foreground") } } } }
// --- sections -------------------------------------------------------------- // Order and placement come from Config; reordering is a config edit.
component WidgetRow: Row { id: widgetRow property var widgets: [] height: parent.height spacing: px(4)
Repeater { model: widgetRow.widgets
Loader { required property var modelData anchors.verticalCenter: widgetRow.verticalCenter sourceComponent: root.widgetFor(modelData) } } }
// Left section is clipped to the space before the right cluster so a long window // list truncates instead of overlapping the right-hand widgets. Item { id: leftContainer clip: true height: parent.height anchors { left: parent.left leftMargin: px(10) right: rightRow.left rightMargin: px(8) verticalCenter: parent.verticalCenter }
WidgetRow { widgets: Config.leftWidgets anchors { left: parent.left verticalCenter: parent.verticalCenter } } }
WidgetRow { widgets: Config.centerWidgets anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } }
WidgetRow { id: rightRow widgets: Config.rightWidgets anchors { right: parent.right rightMargin: px(10) verticalCenter: parent.verticalCenter } }}