From 8e914e313105f39566604048a6dfdf0b94056e3c Mon Sep 17 00:00:00 2001 From: rope Date: Tue, 26 May 2026 11:15:01 +0100 Subject: [PATCH] quickshell: clock, calendar, battery; remove waybar Co-Authored-By: Claude Opus 4.6 --- settings/hyprland.nix | 342 +++++++++++++++++++++++++----------------- 1 file changed, 205 insertions(+), 137 deletions(-) diff --git a/settings/hyprland.nix b/settings/hyprland.nix index 197016a..3b56e85 100644 --- a/settings/hyprland.nix +++ b/settings/hyprland.nix @@ -60,7 +60,6 @@ in networkmanagerapplet pavucontrol polkit_gnome - ] ++ lib.optionals isGaming [ quickshell qt6.qt5compat ]; @@ -423,145 +422,11 @@ in }; }; - # Scope all HM Wayland services (hyprpaper, waybar, …) to the + # Scope all HM Wayland services (hyprpaper, etc.) to the # Hyprland session so they don't crash-loop in a GNOME session. wayland.systemd.target = "hyprland-session.target"; - programs.waybar = { - enable = true; - systemd.enable = true; - - settings.mainBar = { - layer = "top"; - position = "top"; - height = 30; - spacing = 6; - - modules-left = [ "hyprland/workspaces" ]; - modules-center = [ "clock" ]; - modules-right = lib.optionals isMacbook [ "battery" ] ++ [ "group/tray-drawer" ]; - - "hyprland/workspaces" = { - format = "{name}"; - on-click = "activate"; - sort-by-number = true; - }; - - clock = { - format = "{:%H:%M}"; - tooltip-format = "{:%A, %d %B %Y}\n{calendar}"; - }; - - "group/tray-drawer" = { - orientation = "horizontal"; - drawer = { - transition-duration = 500; - transition-left-to-right = false; - }; - modules = [ "custom/tray-handle" "pulseaudio" "tray" ]; - }; - - "custom/tray-handle" = { - format = builtins.fromJSON ''"\ue0b2"''; # U+E0B2 Nerd Font powerline filled left-arrow - tooltip = false; - }; - - # Pulseaudio module, now conditionally visible - pulseaudio = { - format = "{icon} {volume}%"; - format-muted = " muted"; - format-icons = { - default = [ "" "" "" ]; - headphone = ""; - headset = ""; - }; - on-click = "pavucontrol"; - scroll-step = 5; - }; - - # Tray module, now conditionally visible - tray = { - icon-size = 16; - spacing = 8; - }; - } // lib.optionalAttrs isMacbook { - battery = { - format = "{capacity}% {icon}"; - format-charging = "{capacity}% "; - format-icons = [ "" "" "" "" "" ]; - states = { warning = 30; critical = 15; }; - }; - }; - - style = '' - * { - font-family: "FiraMono Nerd Font", monospace; - font-size: 13px; - min-height: 0; - border: none; - border-radius: 0; - } - - window#waybar { - background: alpha(@base00, 0.82); - color: @base05; - } - - #workspaces { - margin-left: 6px; - } - - #workspaces button { - padding: 0 8px; - color: @base03; - background: transparent; - } - - #workspaces button.active { - color: @base05; - } - - #workspaces button:hover { - background: alpha(@base05, 0.08); - color: @base05; - box-shadow: none; - text-shadow: none; - } - - #clock { - color: @base05; - font-weight: 500; - } - - #pulseaudio, - #tray { - padding: 0 10px; - color: @base05; - } - - #pulseaudio.muted { - color: @base03; - } - - #tray { - margin-right: 6px; - } - - #custom-tray-handle { - padding: 0 0px; - color: @base05; - } - - #battery { - padding: 0 10px; - color: @base05; - } - #battery.warning { color: @base0A; } - #battery.critical { color: @base08; } - ''; - }; - - xdg.configFile."quickshell/shell.qml" = lib.mkIf isGaming { + xdg.configFile."quickshell/shell.qml" = { text = '' //@ pragma UseQApplication import Quickshell @@ -637,6 +502,32 @@ in // Spacer Item { Layout.fillWidth: true } + // Clock + Text { + id: clockText + property date now: new Date() + text: now.toLocaleTimeString(Qt.locale(), "HH:mm") + color: "#${c.base05}" + font.family: "FiraMono Nerd Font" + font.pixelSize: 13 + font.weight: Font.Medium + + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: clockText.now = new Date() + } + + MouseArea { + anchors.fill: parent + onClicked: calPopup.visible = !calPopup.visible + } + } + + // Spacer + Item { Layout.fillWidth: true } + // Network status Item { id: netWidget @@ -703,6 +594,78 @@ in } } + ${lib.optionalString isMacbook '' + // Battery + Item { + id: batteryWidget + Layout.preferredHeight: 30 + Layout.preferredWidth: batteryRow.width + Layout.rightMargin: 10 + + property int batteryLevel: 0 + property bool charging: false + property string batteryIcon: "\u{f008e}" + + function updateIcon() { + if (charging) { batteryIcon = "\u{f0084}"; return; } + if (batteryLevel >= 90) batteryIcon = "\u{f0079}"; + else if (batteryLevel >= 70) batteryIcon = "\u{f0082}"; + else if (batteryLevel >= 50) batteryIcon = "\u{f007f}"; + else if (batteryLevel >= 30) batteryIcon = "\u{f007c}"; + else if (batteryLevel >= 15) batteryIcon = "\u{f007a}"; + else batteryIcon = "\u{f008e}"; + } + + Timer { + interval: 10000 + running: true + repeat: true + triggeredOnStart: true + onTriggered: batteryProc.running = true + } + + Process { + id: batteryProc + command: ["sh", "-c", "cat /sys/class/power_supply/BAT0/capacity; cat /sys/class/power_supply/BAT0/status"] + stdout: SplitParser { + onRead: data => { + let trimmed = data.trim(); + if (/^\\d+$/.test(trimmed)) { + batteryWidget.batteryLevel = parseInt(trimmed); + } else { + batteryWidget.charging = (trimmed === "Charging"); + } + batteryWidget.updateIcon(); + } + } + } + + RowLayout { + id: batteryRow + anchors.verticalCenter: parent.verticalCenter + spacing: 4 + + Text { + text: batteryWidget.batteryLevel + "%" + color: batteryWidget.batteryLevel <= 15 ? "#${c.base08}" + : batteryWidget.batteryLevel <= 30 ? "#${c.base0A}" + : "#${c.base05}" + font.family: "FiraMono Nerd Font" + font.pixelSize: 13 + } + + Text { + text: batteryWidget.batteryIcon + color: batteryWidget.batteryLevel <= 15 ? "#${c.base08}" + : batteryWidget.batteryLevel <= 30 ? "#${c.base0A}" + : "#${c.base05}" + font.family: "FiraMono Nerd Font" + font.pixelSize: 14 + } + } + } + ''} + // Tray icons inline RowLayout { id: trayArea @@ -851,6 +814,111 @@ in } } } + + // Calendar popup + PopupWindow { + id: calPopup + anchor.item: clockText + anchor.edges: Edges.Bottom + anchor.gravity: Edges.Bottom + anchor.adjustment: PopupAdjustment.Slide + grabFocus: true + visible: false + color: "transparent" + implicitWidth: calContent.width + 2 + implicitHeight: calContent.height + 2 + + Rectangle { + id: calContent + width: calCol.width + 32 + height: calCol.height + 24 + color: "#${c.base00}" + border.color: "#${c.base03}" + border.width: 1 + radius: 8 + + Column { + id: calCol + anchors.centerIn: parent + spacing: 8 + + // Date header + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: clockText.now.toLocaleDateString(Qt.locale(), "dddd, d MMMM yyyy") + color: "#${c.base05}" + font.family: "FiraMono Nerd Font" + font.pixelSize: 14 + font.weight: Font.Medium + } + + // Day headers + Row { + spacing: 0 + Repeater { + model: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"] + Text { + required property var modelData + width: 28 + horizontalAlignment: Text.AlignHCenter + text: modelData + color: "#${c.base03}" + font.family: "FiraMono Nerd Font" + font.pixelSize: 11 + } + } + } + + // Calendar grid + Grid { + columns: 7 + spacing: 0 + + Repeater { + id: calRepeater + model: 42 + + Rectangle { + required property int index + width: 28 + height: 24 + radius: 4 + color: { + let d = clockText.now; + let first = new Date(d.getFullYear(), d.getMonth(), 1); + let startDay = (first.getDay() + 6) % 7; + let dayNum = index - startDay + 1; + let daysInMonth = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate(); + return (dayNum === d.getDate() && dayNum >= 1 && dayNum <= daysInMonth) + ? "#${c.base02}" : "transparent"; + } + + Text { + anchors.centerIn: parent + text: { + let d = clockText.now; + let first = new Date(d.getFullYear(), d.getMonth(), 1); + let startDay = (first.getDay() + 6) % 7; + let dayNum = parent.index - startDay + 1; + let daysInMonth = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate(); + return (dayNum >= 1 && dayNum <= daysInMonth) ? dayNum.toString() : ""; + } + color: { + let d = clockText.now; + let first = new Date(d.getFullYear(), d.getMonth(), 1); + let startDay = (first.getDay() + 6) % 7; + let dayNum = parent.index - startDay + 1; + return (dayNum === d.getDate()) ? "#${c.base05}" : "#${c.base04}"; + } + font.family: "FiraMono Nerd Font" + font.pixelSize: 11 + } + } + } + } + } + } + } } } }