Feature: Add pinned tray for Bitwarden and Sunshine
Replaces the disabled generic system tray with a curated PinnedTray showing just Bitwarden and Sunshine. Each shows its live tray icon (and forwards clicks to it) while running; when not running it shows a dimmed static icon and launches the app on click instead. Right-clicking Sunshine specifically opens its web UI in the browser rather than forwarding to secondaryActivate(), since that's how it's actually configured/used. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -46,7 +46,7 @@ PanelWindow {
|
||||
rightMargin: Appearance.spacing
|
||||
}
|
||||
spacing: Appearance.spacing
|
||||
//Tray {}
|
||||
PinnedTray {}
|
||||
TailscaleIndicator { barWindow: bar }
|
||||
VpnIndicator { barWindow: bar }
|
||||
NetworkIndicator { barWindow: bar }
|
||||
|
||||
101
dotfiles/.config/quickshell/modules/bar/PinnedTray.qml
Normal file
101
dotfiles/.config/quickshell/modules/bar/PinnedTray.qml
Normal file
@@ -0,0 +1,101 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.SystemTray
|
||||
import Quickshell.Widgets
|
||||
import "../../config"
|
||||
|
||||
// A curated stand-in for the full system tray (see Tray.qml) — just the
|
||||
// couple of apps worth one click away, rather than every SNI item any
|
||||
// background app happens to register. Each entry shows that app's own live
|
||||
// tray icon (and forwards clicks to it, same as Tray.qml) while it's
|
||||
// running; when it isn't, it falls back to the app's static icon, dimmed,
|
||||
// and a click launches it instead of doing nothing.
|
||||
RowLayout {
|
||||
id: root
|
||||
spacing: Appearance.spacing * 0.6
|
||||
|
||||
// name: matched case-insensitively against a tray item's id/title to
|
||||
// find this app's live icon, once it's running.
|
||||
// icon: themed icon name shown in place of a live tray icon.
|
||||
// launch: command run on left-click while not running.
|
||||
// secondary: optional command run on right-click, regardless of running
|
||||
// state, in place of forwarding to the tray item's own
|
||||
// secondaryActivate() (which for most apps' tray icons does
|
||||
// nothing useful anyway).
|
||||
readonly property var pinned: [
|
||||
{
|
||||
name: "Bitwarden",
|
||||
icon: "bitwarden",
|
||||
launch: ["bitwarden-desktop", "--ozone-platform-hint=auto", "--silent"],
|
||||
},
|
||||
{
|
||||
name: "Sunshine",
|
||||
icon: "dev.lizardbyte.app.Sunshine",
|
||||
launch: ["systemctl", "--user", "start", "app-dev.lizardbyte.app.Sunshine.service"],
|
||||
// Sunshine is configured entirely through its web UI — there's
|
||||
// no separate desktop app to "open" here.
|
||||
secondary: ["xdg-open", "https://localhost:47990"],
|
||||
},
|
||||
]
|
||||
|
||||
function findTrayItem(name) {
|
||||
const needle = name.toLowerCase()
|
||||
for (const item of SystemTray.items.values) {
|
||||
if (item.id.toLowerCase().includes(needle) || item.title.toLowerCase().includes(needle))
|
||||
return item
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.pinned
|
||||
|
||||
delegate: Item {
|
||||
id: slot
|
||||
required property var modelData
|
||||
readonly property var trayItem: root.findTrayItem(modelData.name)
|
||||
readonly property bool running: trayItem !== null
|
||||
|
||||
implicitWidth: Appearance.iconSize
|
||||
implicitHeight: Appearance.iconSize
|
||||
|
||||
IconImage {
|
||||
anchors.fill: parent
|
||||
source: slot.running ? slot.trayItem.icon : Quickshell.iconPath(slot.modelData.icon)
|
||||
// Not-running is the only cue we give beyond the click
|
||||
// behavior itself — dimmed rather than hidden or badged, so
|
||||
// the icon stays put and clickable either way.
|
||||
opacity: slot.running ? 1.0 : 0.5
|
||||
}
|
||||
|
||||
Process { id: launcher }
|
||||
Process { id: secondaryProc }
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -4
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: (mouse) => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
if (slot.modelData.secondary) {
|
||||
if (!secondaryProc.running) {
|
||||
secondaryProc.command = slot.modelData.secondary
|
||||
secondaryProc.running = true
|
||||
}
|
||||
} else if (slot.running) {
|
||||
slot.trayItem.secondaryActivate()
|
||||
}
|
||||
} else if (slot.running) {
|
||||
slot.trayItem.activate()
|
||||
} else if (!launcher.running) {
|
||||
launcher.command = slot.modelData.launch
|
||||
launcher.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user