taffybar config: stacked RAM/SWAP, barLevels tray row, bump locks

This commit is contained in:
2026-02-18 00:32:24 -08:00
committed by Kat Huang
parent 89d470e489
commit c6d9a3f909
5 changed files with 149 additions and 25 deletions

View File

@@ -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"
},

View File

@@ -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;
}

View File

@@ -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