From c6d9a3f909e75983fb62518d4f137e9ed3247a5b Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 18 Feb 2026 00:32:24 -0800 Subject: [PATCH] taffybar config: stacked RAM/SWAP, barLevels tray row, bump locks --- dotfiles/config/taffybar/flake.lock | 4 +- dotfiles/config/taffybar/taffybar | 2 +- dotfiles/config/taffybar/taffybar.css | 23 ++++- dotfiles/config/taffybar/taffybar.hs | 133 +++++++++++++++++++++++--- nixos/flake.lock | 12 +-- 5 files changed, 149 insertions(+), 25 deletions(-) diff --git a/dotfiles/config/taffybar/flake.lock b/dotfiles/config/taffybar/flake.lock index 157d717b..443c0e99 100644 --- a/dotfiles/config/taffybar/flake.lock +++ b/dotfiles/config/taffybar/flake.lock @@ -554,8 +554,8 @@ "xmonad-contrib": "xmonad-contrib" }, "locked": { - "lastModified": 1770970544, - "narHash": "sha256-rJV6yxYy77OJS994ICpNyecArjfhzGq6KKJrkbyYzvc=", + "lastModified": 1771379683, + "narHash": "sha256-849/9t0hXF1Xwv6ygMWoZFGKwZ3xNqUSk2bSm4jZi/E=", "path": "/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar", "type": "path" }, diff --git a/dotfiles/config/taffybar/taffybar b/dotfiles/config/taffybar/taffybar index 629e4941..0476afb1 160000 --- a/dotfiles/config/taffybar/taffybar +++ b/dotfiles/config/taffybar/taffybar @@ -1 +1 @@ -Subproject commit 629e4941d7456037dbaa139588316388f98e79d4 +Subproject commit 0476afb1aaff96f870d41b4a980a9b6b15d66c15 diff --git a/dotfiles/config/taffybar/taffybar.css b/dotfiles/config/taffybar/taffybar.css index 5f8d9120..6adce1b7 100644 --- a/dotfiles/config/taffybar/taffybar.css +++ b/dotfiles/config/taffybar/taffybar.css @@ -30,6 +30,14 @@ padding-right: 11px; } +/* Compact two-line RAM/SWAP widget: reduce icon padding a bit. */ +.ram-swap .icon-label > .icon { + /* Different glyphs have different visual widths; fix the icon column width + so the value text aligns between the RAM and SWAP rows. */ + min-width: 22px; + padding-right: 8px; +} + /* The main bar container. border-radius matches the widget squircles (6px) so the bar itself has softly rounded corners. */ .taffy-box { @@ -42,6 +50,18 @@ background-image: none; } +/* With barLevels enabled we render one row per level; keep the tray-only row + visually light so the tray feels detached from the main widget strip. */ +.level-2 { + background-color: transparent; + background-image: none; + box-shadow: none; +} + +.level-2 .outer-pad.sni-tray { + margin-top: 0px; +} + /* Each widget is wrapped in outer-pad > inner-pad > contents by buildContentsBox (Haskell). The outer-pad draws the squircle background pill. border-radius kept low (6px) for a more squared @@ -282,7 +302,8 @@ .clock label, .mpris label, -.battery label { +.battery label, +.ram-swap label { letter-spacing: 0.2px; } diff --git a/dotfiles/config/taffybar/taffybar.hs b/dotfiles/config/taffybar/taffybar.hs index d10eb4ac..a24e8cc2 100644 --- a/dotfiles/config/taffybar/taffybar.hs +++ b/dotfiles/config/taffybar/taffybar.hs @@ -4,7 +4,9 @@ module Main (main) where +import Control.Concurrent (threadDelay) import Control.Monad.IO.Class (MonadIO, liftIO) +import Data.Char (toLower) import Data.Int (Int32) import Data.List (nub) import qualified Data.Map as M @@ -15,7 +17,13 @@ import qualified Data.Text as T import qualified GI.GdkPixbuf.Objects.Pixbuf as Gdk import qualified GI.Gtk as Gtk import Network.HostName (getHostName) -import qualified StatusNotifier.Tray as SNITray (MenuBackend (HaskellDBusMenu), defaultTrayParams, trayMenuBackend, trayOverlayScale) +import System.Environment (lookupEnv) +import qualified StatusNotifier.Tray as SNITray + ( MenuBackend (HaskellDBusMenu, LibDBusMenu), + defaultTrayParams, + trayMenuBackend, + trayOverlayScale, + ) import System.Environment.XDG.BaseDir (getUserConfigFile) import System.Log.Logger (Priority (WARNING), rootLoggerName, setLevel, updateGlobalLogger) import System.Taffybar (startTaffybar) @@ -23,10 +31,11 @@ import System.Taffybar.Context (Backend (BackendWayland, BackendX11), TaffyIO, d import System.Taffybar.DBus import System.Taffybar.DBus.Toggle import System.Taffybar.Hooks (withLogLevels) +import System.Taffybar.Information.Memory (MemoryInfo (..), parseMeminfo) import System.Taffybar.Information.EWMHDesktopInfo (WorkspaceId (..)) import System.Taffybar.Information.X11DesktopInfo import System.Taffybar.SimpleConfig -import System.Taffybar.Util (getPixbufFromFilePath, maybeTCombine, (<|||>)) +import System.Taffybar.Util (getPixbufFromFilePath, maybeTCombine, postGUIASync, (<|||>)) import System.Taffybar.Widget import qualified System.Taffybar.Widget.ASUS as ASUS import qualified System.Taffybar.Widget.NetworkManager as NetworkManager @@ -37,12 +46,13 @@ import System.Taffybar.Widget.SNITray sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt, ) import qualified System.Taffybar.Widget.ScreenLock as ScreenLock -import System.Taffybar.Widget.Util (buildContentsBox, buildIconLabelBox, loadPixbufByName, widgetSetClassGI) +import System.Taffybar.Widget.Util (backgroundLoop, buildContentsBox, buildIconLabelBox, loadPixbufByName, widgetSetClassGI) import qualified System.Taffybar.Widget.Wlsunset as Wlsunset import qualified System.Taffybar.Widget.Workspaces.Config as WorkspaceWidgetConfig import qualified System.Taffybar.Widget.Workspaces.EWMH as X11Workspaces import qualified System.Taffybar.Widget.Workspaces.Hyprland as Hyprland import System.Taffybar.WindowIcon (pixBufFromColor) +import Text.Printf (printf) -- | Wrap the widget in a "TaffyBox" (via 'buildContentsBox') and add a CSS class. decorateWithClassAndBox :: (MonadIO m) => Text -> Gtk.Widget -> m Gtk.Widget @@ -342,6 +352,78 @@ diskUsageWidget :: TaffyIO Gtk.Widget diskUsageWidget = decorateWithClassAndBoxM "disk-usage" diskUsageNew +stackInPill :: Text -> [TaffyIO Gtk.Widget] -> TaffyIO Gtk.Widget +stackInPill klass builders = + decorateWithClassAndBoxM klass $ do + widgets <- sequence builders + liftIO $ do + box <- Gtk.boxNew Gtk.OrientationVertical 0 + mapM_ (\w -> Gtk.boxPackStart box w False False 0) widgets + Gtk.widgetShowAll box + Gtk.toWidget box + +meminfoPercentRowWidget :: + Text -> + Text -> + (MemoryInfo -> Maybe Double) -> + (MemoryInfo -> T.Text) -> + TaffyIO Gtk.Widget +meminfoPercentRowWidget rowClass iconText getRatio tooltipText = + liftIO $ do + iconW <- Gtk.toWidget =<< Gtk.labelNew (Just iconText) + valueLabel <- Gtk.labelNew (Just "") + valueW <- Gtk.toWidget valueLabel + row <- buildIconLabelBox iconW valueW + _ <- widgetSetClassGI row rowClass + + let fmtPercent :: Double -> T.Text + fmtPercent r = T.pack (printf "%.0f%%" (max 0 r * 100)) + updateOnce :: IO () + updateOnce = do + info <- parseMeminfo + let valueText = maybe "n/a" fmtPercent (getRatio info) + postGUIASync $ do + Gtk.labelSetText valueLabel valueText + Gtk.widgetSetTooltipText row (Just (tooltipText info)) + threadDelay (2 * 1000000) + + _ <- Gtk.onWidgetRealize row $ backgroundLoop updateOnce + pure row + +ramRowWidget :: TaffyIO Gtk.Widget +ramRowWidget = + meminfoPercentRowWidget + "ram-row" + "\xF538" -- Font Awesome: memory + (Just . memoryUsedRatio) + (\info -> "RAM " <> showMemoryInfo "$used$/$total$" 2 info) + +swapRowWidget :: TaffyIO Gtk.Widget +swapRowWidget = + meminfoPercentRowWidget + "swap-row" + "\xF0EC" -- Font Awesome: exchange (swap-ish) + (\info -> if memorySwapTotal info <= 0 then Nothing else Just (memorySwapUsedRatio info)) + (\info -> "SWAP " <> showMemoryInfo "$swapUsed$/$swapTotal$" 2 info) + +ramSwapWidget :: TaffyIO Gtk.Widget +ramSwapWidget = + stackInPill "ram-swap" [ramRowWidget, swapRowWidget] + +audioBacklightWidget :: TaffyIO Gtk.Widget +audioBacklightWidget = + stackInPill + "audio-backlight" + [ PulseAudio.pulseAudioNew, + backlightNewChanWith + defaultBacklightWidgetConfig + { backlightFormat = "$percent$%", + backlightUnknownFormat = "n/a", + backlightTooltipFormat = + Just "Device: $device$\nBrightness: $brightness$/$max$ ($percent$%)" + } + ] + asusWidget :: TaffyIO Gtk.Widget asusWidget = decorateWithClassAndBoxM "asus-profile" ASUS.asusWidgetNew @@ -363,10 +445,17 @@ wlsunsetWidget = } sniTrayWidget :: TaffyIO Gtk.Widget -sniTrayWidget = +sniTrayWidget = do + -- If the Haskell backend regresses, flip at runtime: + -- TAFFYBAR_SNI_MENU_BACKEND=lib + backendEnv <- liftIO (lookupEnv "TAFFYBAR_SNI_MENU_BACKEND") + let menuBackend = + case fmap (map toLower) backendEnv of + Just "lib" -> SNITray.LibDBusMenu + _ -> SNITray.HaskellDBusMenu decorateWithClassAndBoxM "sni-tray" - (sniTrayNewFromParams (SNITray.defaultTrayParams {SNITray.trayMenuBackend = SNITray.HaskellDBusMenu, SNITray.trayOverlayScale = 1 % 3})) + (sniTrayNewFromParams (SNITray.defaultTrayParams {SNITray.trayMenuBackend = menuBackend, SNITray.trayOverlayScale = 1 % 3})) -- ** Layout @@ -377,16 +466,15 @@ startWidgetsForBackend backend = -- These Wayland widgets are Hyprland-specific. BackendWayland -> [hyprlandWorkspacesWidget] -endWidgetsForHost :: String -> Backend -> [TaffyIO Gtk.Widget] -endWidgetsForHost hostName backend = - let baseEndWidgets = [audioWidget, diskUsageWidget, networkWidget, screenLockWidget, wlsunsetWidget, mprisWidget, sniTrayWidget] +endWidgetsForHost :: String -> [TaffyIO Gtk.Widget] +endWidgetsForHost hostName = + let baseEndWidgets = [audioWidget, ramSwapWidget, diskUsageWidget, networkWidget, screenLockWidget, wlsunsetWidget, mprisWidget] laptopEndWidgets = [ batteryWidget, - sniTrayWidget, asusWidget, - audioWidget, + audioBacklightWidget, + ramSwapWidget, diskUsageWidget, - backlightWidget, networkWidget, screenLockWidget, wlsunsetWidget, @@ -396,17 +484,32 @@ endWidgetsForHost hostName backend = then laptopEndWidgets else baseEndWidgets +barLevelsForHost :: String -> Backend -> [BarLevelConfig] +barLevelsForHost hostName backend = + [ BarLevelConfig + { levelStartWidgets = startWidgetsForBackend backend, + levelCenterWidgets = [clockWidget], + levelEndWidgets = endWidgetsForHost hostName + }, + BarLevelConfig + { levelStartWidgets = [], + levelCenterWidgets = [], + levelEndWidgets = [sniTrayWidget] + } + ] + mkSimpleTaffyConfig :: String -> Backend -> [FilePath] -> SimpleTaffyConfig mkSimpleTaffyConfig hostName backend cssFiles = defaultSimpleTaffyConfig - { startWidgets = startWidgetsForBackend backend, - endWidgets = endWidgetsForHost hostName backend, + { startWidgets = [], + centerWidgets = [], + endWidgets = [], + barLevels = Just $ barLevelsForHost hostName backend, barPosition = Top, widgetSpacing = 0, barPadding = 4, barHeight = ScreenRatio $ 1 / 33, - cssPaths = cssFiles, - centerWidgets = [clockWidget] + cssPaths = cssFiles } -- ** Entry Point diff --git a/nixos/flake.lock b/nixos/flake.lock index e534a87e..3d4340bc 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -1910,11 +1910,11 @@ "status-notifier-item": { "flake": false, "locked": { - "lastModified": 1770970375, - "narHash": "sha256-0G5dkc1bjAdbAJBBnipeEE2HbEkd4+bdkyRraumMRs4=", + "lastModified": 1770955859, + "narHash": "sha256-h95eUeTkNsdrVxAx0Nv0ViXHSkhhOfPF4L4DhtbfkNs=", "owner": "taffybar", "repo": "status-notifier-item", - "rev": "233aa43606036d06e58f19be156c48fa979506f2", + "rev": "a0958505f4e5fc145f34d7ad2615688d6668e02a", "type": "github" }, "original": { @@ -1990,11 +1990,11 @@ ] }, "locked": { - "lastModified": 1770971159, - "narHash": "sha256-FOMr9QGs6dzQ0AHfvq0/s5H8I8IWVl347h2NCu+Upz8=", + "lastModified": 1770956839, + "narHash": "sha256-LeFbcQ1i7PBbSNhGFkf6A2yj71NnWjJj0ASahmxaxwg=", "owner": "taffybar", "repo": "taffybar", - "rev": "276ed1fd83bb56d22cb854acb10aeebf64b97c5c", + "rev": "6a1cf87c87e4bcab01b81d5f7d257c4dc6cfa1af", "type": "github" }, "original": {