import Quickshell import Quickshell.Hyprland import Quickshell.Widgets import Quickshell.Services.SystemTray import Quickshell.Io import QtQuick PanelWindow { id: bar required property var walColors required property string motd required property int notificationCount required property var latestNotification property var notifServer: null required property SystemStats stats required property string fontFamily // --- Notification toast state --- property int _toastCounter: 0 onLatestNotificationChanged: { if (latestNotification) { toastModel.append({ toastId: _toastCounter++, appName: latestNotification.appName || "", summary: latestNotification.summary || "", body: latestNotification.body || "" }) } } ListModel { id: toastModel } anchors { top: true left: true right: true } color: "transparent" exclusionMode: ExclusionMode.Normal exclusiveZone: 34 implicitHeight: 34 // --- Theme colors --- property color bgColor: (walColors.special && walColors.special.background) ? walColors.special.background : "#1d160d" property color fgColor: (walColors.special && walColors.special.foreground) ? walColors.special.foreground : "#d8d8d3" property color accentColor: (walColors.colors && walColors.colors.color1) ? walColors.colors.color1 : "#946F50" property color accent2Color: (walColors.colors && walColors.colors.color2) ? walColors.colors.color2 : "#BA9351" property color accent3Color: (walColors.colors && walColors.colors.color3) ? walColors.colors.color3 : "#BA9351" property color accent4Color: (walColors.colors && walColors.colors.color4) ? walColors.colors.color4 : "#BA9351" property color accent5Color: (walColors.colors && walColors.colors.color5) ? walColors.colors.color5 : "#BA9351" property color accent6Color: (walColors.colors && walColors.colors.color6) ? walColors.colors.color6 : "#BA9351" property color accent7Color: (walColors.colors && walColors.colors.color7) ? walColors.colors.color7 : "#BA9351" // --- Hyprland state --- property var hyprMonitor: Hyprland.monitorFor(screen) property var monitorWorkspaces: { let vals = Hyprland.workspaces.values let result = [] for (let i = 0; i < vals.length; i++) { if (vals[i].monitor === hyprMonitor && vals[i].id > 0) result.push(vals[i]) } return result.sort((a, b) => a.id - b.id) } property bool specialWorkspaceVisible: false Connections { target: Hyprland function onRawEvent(event) { if (event.name === "activespecial") { let parts = event.data.split(",") let monName = parts[parts.length - 1] if (monName === hyprMonitor?.name) { bar.specialWorkspaceVisible = parts[0] !== "" } } } } property string windowTitle: { const active = Hyprland.activeToplevel const currentWs = Hyprland.focusedWorkspace?.id let title = active?.title ?? "" if (!title || title === "~" || title === "kitty" || title === "fish" || active.workspace?.id !== currentWs) return motd return title } // --- Clock --- SystemClock { id: clock precision: SystemClock.Minutes } // --- Bar layout --- Item { anchors.fill: parent anchors.margins: 2 // Left module group Rectangle { id: leftGroup z: 1 anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter height: 30 width: leftContent.width + 8 clip: true color: bar.bgColor border.color: bar.accentColor border.width: 2 radius: 100 Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Row { id: leftContent anchors.left: parent.left anchors.leftMargin: 4 anchors.verticalCenter: parent.verticalCenter spacing: 0 Workspaces { workspaces: bar.monitorWorkspaces accentColor: bar.accentColor fgColor: bar.fgColor fontFamily: bar.fontFamily anchors.verticalCenter: parent.verticalCenter } } } // Special workspace badge Rectangle { id: specialBadge z: 0 anchors.verticalCenter: leftGroup.verticalCenter height: 28 width: specialText.implicitWidth + (bar.specialWorkspaceVisible ? 30 : 10) topLeftRadius: 0 bottomLeftRadius: 0 topRightRadius: 100 bottomRightRadius: 100 color: bar.bgColor border.color: bar.accentColor border.width: 2 visible: bar.specialWorkspaceVisible x: bar.specialWorkspaceVisible ? leftGroup.x + leftGroup.width - 10 : leftGroup.x + leftGroup.width - specialBadge.width Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.InOutCubic } } Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutCubic } } Text { id: specialText anchors.centerIn: parent anchors.horizontalCenterOffset: 4 text: "\uf2d2" color: bar.fgColor font.pixelSize: 11 font.family: bar.fontFamily } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: Hyprland.dispatch("togglespecialworkspace magic") } } // Center module group Rectangle { id: centerGroup anchors.centerIn: parent height: 30 width: centerContent.width + 16 clip: true color: bar.bgColor border.color: bar.accentColor border.width: 2 radius: 100 Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Row { id: centerContent anchors.centerIn: parent spacing: 12 Text { text: bar.windowTitle color: bar.fgColor font.pixelSize: 13 font.family: bar.fontFamily elide: Text.ElideRight width: Math.min(implicitWidth, 900) anchors.verticalCenter: parent.verticalCenter } } } // Notification toasts Repeater { model: toastModel delegate: NotificationToast { bgColor: bar.bgColor fgColor: bar.fgColor accentColor: bar.accentColor fontFamily: bar.fontFamily rightAnchor: rightGroup sourceModel: toastModel } } // Right module group Rectangle { id: rightGroup z: 1 anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter height: 30 width: rightContent.width + 8 clip: true color: bar.bgColor border.color: bar.accentColor border.width: 2 radius: 100 Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Row { id: rightContent anchors.right: parent.right anchors.rightMargin: 4 anchors.verticalCenter: parent.verticalCenter spacing: 5 // Notification count Item { id: notifBadgeWrapper property real targetWidth: bar.notificationCount > 0 ? notifCountText.implicitWidth + 16 : 0 height: 22 width: targetWidth clip: true anchors.verticalCenter: parent.verticalCenter visible: width > 0 Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Rectangle { anchors.left: parent.left height: 22 width: notifCountText.implicitWidth + 16 topLeftRadius: 100 bottomLeftRadius: 100 radius: parent.visible ? 4 : 100 color: bar.accent7Color Behavior on radius { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Text { id: notifCountText anchors.centerIn: parent text: bar.notificationCount + " \uf0f3" color: bar.bgColor font.pixelSize: 13 font.family: bar.fontFamily } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { if (!bar.notifServer) return let notifs = bar.notifServer.trackedNotifications.values for (let i = notifs.length - 1; i >= 0; i--) notifs[i].dismiss() } } } } // Updates BarPill { id: updatesPill fontFamily: bar.fontFamily label: stats.updatesCount + (stats.updatesCount === 0 ? " 󰂪" : " 󱍷") pillColor: bar.accent6Color textColor: bar.bgColor tooltipText: stats.updatesList || "Up to date" topLeftRadius: bar.notificationCount > 0 ? 4 : 100 bottomLeftRadius: bar.notificationCount > 0 ? 4 : 100 Behavior on topLeftRadius { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } Behavior on bottomLeftRadius { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton cursorShape: Qt.PointingHandCursor onClicked: mouse => { if (mouse.button === Qt.RightButton) stats.checkUpdates() else updateProc.running = true } } Process { id: updateProc command: ["kitty", "--title", "System Update", "-e", "bash", "-c", "yay && echo 'Update completed successfully!' || { echo 'Update failed! Press any key to close...'; read -n 1; }"] } } // CPU BarPill { fontFamily: bar.fontFamily label: stats.cpuUsage + "% \uf2db" pillColor: bar.accent5Color textColor: bar.bgColor } // Memory BarPill { id: memPill fontFamily: bar.fontFamily label: stats.memUsage + "% \uf0c9" pillColor: bar.accent4Color textColor: bar.bgColor tooltipText: stats.memInfo } // Temperature BarPill { id: tempPill fontFamily: bar.fontFamily label: { let t = stats.temperature let icon = t >= 80 ? "\uf2c7" : "\uf2c9" return t + "\u00b0C " + icon } pillColor: bar.accent3Color textColor: bar.bgColor tooltipText: stats.cpuTemps || "No sensors" } // Clock BarPill { id: clockPill fontFamily: bar.fontFamily label: Qt.formatDateTime(clock.date, "hh:mm") pillColor: bar.accent2Color textColor: bar.bgColor tooltipText: Qt.formatDateTime(clock.date, "dddd, MMMM d, yyyy") } // System tray Rectangle { height: 22 width: trayRow.width + 16 topLeftRadius: 4 bottomLeftRadius: 4 topRightRadius: 100 bottomRightRadius: 100 color: bar.accentColor anchors.verticalCenter: parent.verticalCenter Row { id: trayRow anchors.centerIn: parent spacing: 6 Repeater { model: SystemTray.items delegate: Item { id: trayDelegate required property var modelData width: 16 height: 16 anchors.verticalCenter: parent.verticalCenter Image { anchors.fill: parent source: modelData.icon sourceSize.width: 16 sourceSize.height: 16 fillMode: Image.PreserveAspectFit } QsMenuAnchor { id: menuAnchor menu: modelData.menu anchor.window: bar anchor.rect.x: trayDelegate.mapToItem(bar.contentItem, 0, 0).x anchor.rect.y: trayDelegate.mapToItem(bar.contentItem, 0, 0).y anchor.rect.width: trayDelegate.width anchor.rect.height: trayDelegate.height } MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: mouse => { if (mouse.button === Qt.RightButton) menuAnchor.open() else modelData.activate() } } } } } } } } } // --- Tooltips --- BarTooltip { bgColor: bar.bgColor; fgColor: bar.fgColor; borderColor: bar.accentColor; fontFamily: bar.fontFamily target: updatesPill; text: updatesPill.tooltipText } BarTooltip { bgColor: bar.bgColor; fgColor: bar.fgColor; borderColor: bar.accentColor; fontFamily: bar.fontFamily target: memPill; text: memPill.tooltipText } BarTooltip { bgColor: bar.bgColor; fgColor: bar.fgColor; borderColor: bar.accentColor; fontFamily: bar.fontFamily target: tempPill; text: tempPill.tooltipText } BarTooltip { bgColor: bar.bgColor; fgColor: bar.fgColor; borderColor: bar.accentColor; fontFamily: bar.fontFamily target: clockPill; text: clockPill.tooltipText } }