taffybar: use submodule and improve hyprland config

This commit is contained in:
2026-02-05 00:12:51 -08:00
committed by Kat Huang
parent 7005b042f0
commit 9c40ba1013
5 changed files with 224 additions and 65 deletions

View File

@@ -115,11 +115,11 @@
"gtk-sni-tray": {
"flake": false,
"locked": {
"lastModified": 1760550917,
"narHash": "sha256-oELopLiVb7D1fzYHAiw/cbfNPHLQcOJ3Rz/I1gbk8IY=",
"lastModified": 1770069502,
"narHash": "sha256-jreuryLGfbyNwx5yEtOYGt2PX+uyGRsgmakYfCQ+OdM=",
"owner": "taffybar",
"repo": "gtk-sni-tray",
"rev": "f6d1bf5dd64ac0f532b03ae01f5e1cc051116f09",
"rev": "733a43f187b35bf65b443a7ede70d62f684932ef",
"type": "github"
},
"original": {
@@ -148,16 +148,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1764252443,
"narHash": "sha256-U4G4dUSYWZYKtrF7/ozebD1B96at08SIhY4R9OaK1nw=",
"owner": "colonelpanic8",
"lastModified": 1770181073,
"narHash": "sha256-ksTL7P9QC1WfZasNlaAdLOzqD8x5EPyods69YBqxSfk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e1fc6c25b91d3d49dd02a156237721f12dbd86b2",
"rev": "bf922a59c5c9998a6584645f7d0de689512e444c",
"type": "github"
},
"original": {
"owner": "colonelpanic8",
"ref": "remove-gi-gtk-hs-patch",
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
@@ -310,18 +310,14 @@
]
},
"locked": {
"lastModified": 1760591109,
"narHash": "sha256-O21ayp5v2eccXdcwNjTK5ZB99ruK0Zt9CUaw5Rye42g=",
"owner": "taffybar",
"repo": "taffybar",
"rev": "b256a711416036ca124fc9b3f89e7c957535e465",
"type": "github"
"lastModified": 1770273353,
"narHash": "sha256-0VQfT0bxyU+xmEMLDRQq+hbJX35+PWPjSXiAVFGOlzQ=",
"path": "/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar",
"type": "path"
},
"original": {
"owner": "taffybar",
"ref": "master",
"repo": "taffybar",
"type": "github"
"path": "/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar",
"type": "path"
}
},
"unstable": {
@@ -349,11 +345,11 @@
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1748252779,
"narHash": "sha256-kSyD/VDUX2m9c2vcuLBT2wnpYiVSHHlP9vuDTtsAtD8=",
"lastModified": 1764753633,
"narHash": "sha256-6552zbHzdNnkREnOluE6xePIib5cc/8Nc5OnPyHORUo=",
"owner": "NorfairKing",
"repo": "weeder-nix",
"rev": "388df7a6f00220d1960118e1ad37cd86150d2c5a",
"rev": "2203c43ab9f1c4e52c2cff8e3d01bbb53159b922",
"type": "github"
},
"original": {

View File

@@ -1,10 +1,10 @@
{
inputs = {
flake-utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:colonelpanic8/nixpkgs/remove-gi-gtk-hs-patch";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
xmonad.url = "github:xmonad/xmonad/master";
taffybar = {
url = "github:taffybar/taffybar/master";
url = "path:/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar";
inputs.nixpkgs.follows = "nixpkgs";
inputs.xmonad.follows = "xmonad";
};
@@ -16,7 +16,7 @@
taffybar = prev.haskell.lib.overrideCabal hsuper.taffybar (oa: {
doHaddock = false;
doCheck = false;
# Fix for GHC 9.4 where liftA2 is not in Prelude
# Legacy fix for older GHC (harmless on newer)
postPatch = (oa.postPatch or "") + ''
substituteInPlace src/System/Taffybar/DBus/Client/Util.hs \
--replace-fail "import Control.Monad (forM)" \
@@ -47,7 +47,7 @@ import Control.Applicative (liftA2)"
final.xorg.libXtst.out
];
};
defComp = { compiler = "ghc94"; };
defComp = { compiler = "ghc98"; };
overlay = xmonad.lib.fromHOL hoverlay defComp;
overlayList = [ taffybar.overlays.default overlay ];
in flake-utils.lib.eachDefaultSystem (system:
@@ -57,10 +57,21 @@ import Control.Applicative (liftA2)"
{
devShell = hpkgs.shellFor {
packages = p: [ p.imalison-taffybar p.taffybar ];
nativeBuildInputs = with hpkgs; [
nativeBuildInputs = (with hpkgs; [
cabal-install
# ghcid ormolu implicit-hie haskell-language-server hlint
]) ++ [
pkgs.gdk-pixbuf
pkgs.librsvg
];
shellHook = ''
if [ -z "''${GDK_PIXBUF_MODULE_FILE:-}" ]; then
export GDK_PIXBUF_MODULE_FILE="${pkgs.gdk-pixbuf}/lib/gdk-pixbuf-2.0/${pkgs.gdk-pixbuf.version}/loaders.cache"
fi
if [ -z "''${GDK_PIXBUF_MODULEDIR:-}" ]; then
export GDK_PIXBUF_MODULEDIR="${pkgs.gdk-pixbuf}/lib/gdk-pixbuf-2.0/${pkgs.gdk-pixbuf.version}/loaders"
fi
'';
};
defaultPackage = hpkgs.imalison-taffybar;
}) // {

View File

@@ -22,6 +22,7 @@ executable taffybar
, directory
, filepath
, gi-gtk3
, gi-gdkpixbuf
, gtk-sni-tray
, gtk-strut
, haskell-gi-base

View File

@@ -15,6 +15,7 @@ import qualified Data.Map as M
import Data.Maybe
import qualified Data.Text
import Data.Time
import qualified GI.GdkPixbuf.Objects.Pixbuf as Gdk
import qualified GI.Gtk as Gtk
import qualified GI.Gtk.Objects.Overlay as Gtk
import Network.HostName
@@ -28,7 +29,7 @@ import System.Log.Handler.Simple
import System.Log.Logger
import System.Process
import System.Taffybar
import System.Taffybar.Context (appendHook)
import System.Taffybar.Context (Backend(..), TaffyIO, appendHook, detectBackend)
import System.Taffybar.DBus
import System.Taffybar.DBus.Toggle
import System.Taffybar.Hooks
@@ -43,9 +44,12 @@ import System.Taffybar.Widget.Generic.Icon
import System.Taffybar.Widget.Generic.PollingGraph
import System.Taffybar.Widget.Generic.PollingLabel
import System.Taffybar.Widget.Util
import System.Taffybar.Widget.Workspaces
import qualified System.Taffybar.Widget.HyprlandWorkspaces as Hyprland
import qualified System.Taffybar.Widget.Workspaces as X11Workspaces
import System.Taffybar.WindowIcon (pixBufFromColor)
import Text.Printf
import Text.Read hiding (lift)
import Data.Int (Int32)
setClassAndBoundingBoxes :: MonadIO m => Data.Text.Text -> Gtk.Widget -> m Gtk.Widget
setClassAndBoundingBoxes klass = buildContentsBox >=> flip widgetSetClassGI klass
@@ -97,7 +101,7 @@ getFullWorkspaceNames = go <$> readAsListOfString Nothing "_NET_DESKTOP_FULL_NAM
where go = zip [WorkspaceId i | i <- [0..]]
workspaceNamesLabelSetter workspace =
remapNSP . fromMaybe "" . lookup (workspaceIdx workspace) <$>
remapNSP . fromMaybe "" . lookup (X11Workspaces.workspaceIdx workspace) <$>
liftX11Def [] getFullWorkspaceNames
where remapNSP "NSP" = "S"
remapNSP n = n
@@ -124,6 +128,106 @@ logDebug = do
-- enableLogger "System.Taffybar.WindowIcon" DEBUG
-- enableLogger "System.Taffybar.Widget.Generic.PollingLabel" DEBUG
iconRemap :: [(Data.Text.Text, [Data.Text.Text])]
iconRemap =
[ ("spotify", ["spotify-client", "spotify"])
]
iconRemapMap :: M.Map Data.Text.Text [Data.Text.Text]
iconRemapMap =
M.fromList [ (Data.Text.toLower k, v) | (k, v) <- iconRemap ]
lookupIconRemap :: Data.Text.Text -> [Data.Text.Text]
lookupIconRemap name = fromMaybe [] $ M.lookup (Data.Text.toLower name) iconRemapMap
iconNameVariants :: Data.Text.Text -> [Data.Text.Text]
iconNameVariants raw =
let lower = Data.Text.toLower raw
stripped = fromMaybe lower (Data.Text.stripSuffix ".desktop" lower)
suffixes = ["-gtk", "-client", "-desktop"]
stripSuffixes name =
let variants = mapMaybe (`Data.Text.stripSuffix` name) suffixes
in nub $ variants ++ [name]
baseNames = stripSuffixes stripped ++ [raw]
toDash c
| c == ' ' || c == '_' || c == '.' || c == '/' = '-'
| otherwise = c
toUnderscore c
| c == ' ' || c == '-' || c == '.' || c == '/' = '_'
| otherwise = c
variantsFor name =
let dotted =
case Data.Text.splitOn "." name of
[] -> name
xs -> last xs
dashed = Data.Text.map toDash name
dashedDotted = Data.Text.map toDash dotted
underscored = Data.Text.map toUnderscore name
underscoredDotted = Data.Text.map toUnderscore dotted
in [dotted, dashed, dashedDotted, underscored, underscoredDotted, name]
in nub $ concatMap variantsFor baseNames
isSpecialHyprWorkspace :: Hyprland.HyprlandWorkspace -> Bool
isSpecialHyprWorkspace ws =
let name = Data.Text.toLower $ Data.Text.pack $ Hyprland.workspaceName ws
in Data.Text.isPrefixOf "special" name || Hyprland.workspaceIdx ws < 0
hyprlandIconCandidates :: Hyprland.HyprlandWindow -> [Data.Text.Text]
hyprlandIconCandidates windowData =
let baseNames = map Data.Text.pack $ catMaybes
[ Hyprland.windowClass windowData
, Hyprland.windowInitialClass windowData
]
remapped = concatMap lookupIconRemap baseNames
remappedExpanded = concatMap iconNameVariants remapped
baseExpanded = concatMap iconNameVariants baseNames
in nub (remappedExpanded ++ baseExpanded)
isPathCandidate :: Data.Text.Text -> Bool
isPathCandidate name =
Data.Text.isInfixOf "/" name ||
any (`Data.Text.isSuffixOf` name) [".png", ".svg", ".xpm"]
hyprlandIconFromCandidate size name
| isPathCandidate name =
liftIO $ getPixbufFromFilePath (Data.Text.unpack name)
| otherwise =
maybeTCombine
(Hyprland.getWindowIconFromDesktopEntryByAppId size (Data.Text.unpack name))
(liftIO $ loadPixbufByName size name)
hyprlandManualIconGetter :: Hyprland.HyprlandWindowIconPixbufGetter
hyprlandManualIconGetter =
Hyprland.handleIconGetterException $ \size windowData ->
foldl maybeTCombine (return Nothing) $
map (hyprlandIconFromCandidate size) (hyprlandIconCandidates windowData)
fallbackIconPixbuf :: Int32 -> TaffyIO (Maybe Gdk.Pixbuf)
fallbackIconPixbuf size = do
let fallbackNames =
[ "application-x-executable"
, "application"
, "image-missing"
, "gtk-missing-image"
, "dialog-question"
, "utilities-terminal"
, "system-run"
, "window"
]
tryNames =
foldl
maybeTCombine
(return Nothing)
(map (liftIO . loadPixbufByName size) fallbackNames)
result <- tryNames
case result of
Just _ -> return result
Nothing -> Just <$> pixBufFromColor size 0x5f5f5fff
hyprlandFallbackIcon :: Hyprland.HyprlandWindowIconPixbufGetter
hyprlandFallbackIcon size _ =
fallbackIconPixbuf size
cssFilesByHostname =
[ ("uber-loaner", ["uber-loaner.css"])
, ("imalison-home", ["taffybar.css"])
@@ -136,7 +240,7 @@ main = do
enableLogger "Graphics.UI.GIGtkStrut" DEBUG
hostName <- getHostName
homeDirectory <- getHomeDirectory
backend <- detectBackend
let relativeFiles = fromMaybe ["taffybar.css"] $ lookup hostName cssFilesByHostname
cssFiles <- mapM (getUserConfigFile "taffybar") relativeFiles
@@ -152,19 +256,31 @@ main = do
windowsNew defaultWindowsConfig
myWorkspaces =
flip widgetSetClassGI "workspaces" =<<
workspacesNew defaultWorkspacesConfig
X11Workspaces.workspacesNew X11Workspaces.defaultWorkspacesConfig
{ minIcons = 1
, getWindowIconPixbuf =
scaledWindowIconPixbufGetter $
getWindowIconPixbufFromChrome <|||>
unscaledDefaultGetWindowIconPixbuf <|||>
(\size _ -> lift $ loadPixbufByName size
"application-default-icon")
X11Workspaces.scaledWindowIconPixbufGetter $
X11Workspaces.getWindowIconPixbufFromChrome <|||>
X11Workspaces.unscaledDefaultGetWindowIconPixbuf <|||>
(\size _ -> fallbackIconPixbuf size)
, widgetGap = 0
, showWorkspaceFn = hideEmpty
, showWorkspaceFn = X11Workspaces.hideEmpty
, updateRateLimitMicroseconds = 100000
, labelSetter = workspaceNamesLabelSetter
, widgetBuilder = buildLabelOverlayController
, widgetBuilder = X11Workspaces.buildLabelOverlayController
}
myHyprWorkspaces =
flip widgetSetClassGI "workspaces" =<<
Hyprland.hyprlandWorkspacesNew Hyprland.defaultHyprlandWorkspacesConfig
{ Hyprland.widgetGap = 0
, Hyprland.minIcons = 1
, Hyprland.showWorkspaceFn =
(\ws -> Hyprland.workspaceState ws /= Hyprland.Empty &&
not (isSpecialHyprWorkspace ws))
, Hyprland.getWindowIconPixbuf =
hyprlandManualIconGetter <|||>
Hyprland.defaultHyprlandGetWindowIconPixbuf <|||>
hyprlandFallbackIcon
}
myClock = deocrateWithSetClassAndBoxes "clock" $
textClockNewWith
@@ -193,7 +309,7 @@ main = do
]
fullEndWidgets = baseEndWidgets ++ [ myCPU, myMem, myNet, myMpris ]
laptopEndWidgets = batteryWidgets ++ baseEndWidgets
baseConfig =
baseConfigX11 =
defaultSimpleTaffyConfig
{ startWidgets = [ myWorkspaces, myLayout, myWindows ]
, endWidgets = baseEndWidgets
@@ -204,30 +320,65 @@ main = do
, cssPaths = cssFiles
, centerWidgets = [ myClock ]
}
selectedConfig =
fromMaybe baseConfig $ lookup hostName
baseConfigWayland =
defaultSimpleTaffyConfig
{ startWidgets = [ myHyprWorkspaces ]
, endWidgets = baseEndWidgets
, barPosition = Top
, widgetSpacing = 0
, barPadding = 0
, barHeight = ScreenRatio $ 1/27
, cssPaths = cssFiles
, centerWidgets = [ myClock ]
}
x11Overrides =
[ ( "uber-loaner"
, baseConfig { endWidgets = laptopEndWidgets }
, baseConfigX11 { endWidgets = laptopEndWidgets }
)
, ( "adell"
, baseConfig { endWidgets = laptopEndWidgets }
, baseConfigX11 { endWidgets = laptopEndWidgets }
)
, ( "stevie-nixos"
, baseConfig { endWidgets = laptopEndWidgets
, startWidgets = [myWorkspaces, myLayout]
, baseConfigX11 { endWidgets = laptopEndWidgets
, startWidgets = [ myWorkspaces, myLayout ]
}
)
, ( "strixi-minaj"
, baseConfig { endWidgets = laptopEndWidgets }
, baseConfigX11 { endWidgets = laptopEndWidgets }
)
, ( "jay-lenovo"
, baseConfig { endWidgets = laptopEndWidgets }
, baseConfigX11 { endWidgets = laptopEndWidgets }
)
, ( "nixquick"
, baseConfig { endWidgets = [ myTray , myMpris ] }
, baseConfigX11 { endWidgets = [ myTray , myMpris ] }
)
]
waylandOverrides =
[ ( "uber-loaner"
, baseConfigWayland { endWidgets = laptopEndWidgets }
)
, ( "adell"
, baseConfigWayland { endWidgets = laptopEndWidgets }
)
, ( "stevie-nixos"
, baseConfigWayland { endWidgets = laptopEndWidgets }
)
, ( "strixi-minaj"
, baseConfigWayland { endWidgets = laptopEndWidgets }
)
, ( "jay-lenovo"
, baseConfigWayland { endWidgets = laptopEndWidgets }
)
, ( "nixquick"
, baseConfigWayland { endWidgets = [ myTray , myMpris ] }
)
]
selectedConfig =
case backend of
BackendX11 ->
fromMaybe baseConfigX11 $ lookup hostName x11Overrides
BackendWayland ->
fromMaybe baseConfigWayland $ lookup hostName waylandOverrides
simpleTaffyConfig = selectedConfig
{ centerWidgets = [ myClock ]
-- , endWidgets = []
@@ -237,4 +388,4 @@ main = do
appendHook (void $ getTrayHost False) $
withLogServer $
withToggleServer $
toTaffyConfig simpleTaffyConfig
toTaffybarConfig simpleTaffyConfig