Using color inside separator | fixed width of xmobar command field

4.6k views Asked by At

I have some xmobarrc config

Config { lowerOnStart = False,
         font    = "xft:Terminus-12"
       , bgColor = "#000000"
       , fgColor = "#8080A1"
       , position = Top
       , commands = [ Run Network "eth0" ["-L","0","-H","32","--normal","#429942","--high","#A36666"] 10
                    , Run Cpu ["-L","3","-H","50","--normal","green","--high","red"] 10
                    , Run Memory ["-t","Mem: <usedratio>%"] 10
                    , Run Date "/%a/ %_d.%m.%Y / %H:%M" "date" 10
                    , Run Com "sh" ["~/bin/weather.sh"] "weather" 60
                    , Run StdinReader
                    ]
       , sepChar = "%"
       , alignSep = "}{"
       , template = " %StdinReader% }{  W:<fc=#fce94f>%weather%</fc> | %cpu% | %memory% | %eth0% | %date%"
       }

And I have 2 questions:

  • If I need a coloring | separator I should change all | on <fc=#ffffff>|</fc>, for example. So how can I use variable separator = <fc=#ffffff>|</fc> in template?
  • Text, that Network and Cpu returning, always have a different length, so all stroke have different size all the time. How can I set Network's text for fixed size?
2

There are 2 answers

1
Thomas M. DuBuisson On

For the first question: how to use separator in your template.

Your template could be transformed to a list of strings, then you just use intersperse separator and concat:

let temp = [" %StdinReader% }{  W:<fc=#fce94f>%weather%</fc> ", " %cpu% "," %memory% ", " %eth0% ", " %date%"]
    separator = "<fc=#ffffff>|</fc>"
in concat $ Data.List.intersperse separator temp

This expression yields:

" %StdinReader% }{  W:<fc=#fce94f>%weather%</fc> <fc=#ffffff>|</fc> %cpu% <fc=#ffffff>|</fc> %memory% <fc=#ffffff>|</fc> %eth0% <fc=#ffffff>|</fc> %date%"

If you're not used to Haskell, you can just put this whole expression of let ... in ... after template = and it will work fine. Alternatively, you can put it at a top level:

myTemplate = let temp = ... separator = ... in ...

and set the template code you have now to be:

...
, template = myTemplate
}
1
DarthFennec On

For the first question:

Thomas' answer seems like it would work, but it doesn't. The xmobarrc file syntax looks like Haskell, but it's actually very restrictive about how it's formatted, and it doesn't support any of the Haskell syntax except for what's already in the file.

That said, the obvious answer is to just color each of them individually. The result is a template line that's a bit longer and less pretty than it probably should be, and it would be more work to change it later on, but honestly, there are only four | characters, so it isn't that bad.

A nicer looking solution is to set fgColor = "#ffffff", and then add "--low","#8080A1" to the options list of each of your commands, and wrap %StdinReader% and %date% in <fc=#8080A1></fc>. Basically, set the default color to the | color, and explicitly color everything else. This way, you avoid writing all those fc tags, because that color is implicit. I wouldn't recommend doing it this way though, as it decreases the clarity of the file somewhat.

Now, there actually is a much better way to do this, that fixes this problem and all related problems entirely: write it in real Haskell. What you would do is, write a Haskell program that ultimately builds and formats the proper xmobarrc, and then prints it to a file. I personally think this is a good option for any annoying config file, but xmobarrc is an especially good candidate for it, because its syntax is so close to Haskell's. More specifically, the syntax xmobarrc does use is entirely the syntax Haskell uses to describe literal data, which means all we have to do is build a Config object and run its default show, and all the rest of the work is done for us, other than a few tiny differences. Actually, here, I'll write this up really quick for you ... here. Now, if you import that, and define a Main file like so:

module Main (main) where

import XMobarHs

main = export $ config
  { lowerOnStart = False
  , font     = "xft:Terminus-12"
  , bgColor  = "#000000"
  , fgColor  = "#8080A1"
  , position = Top
  , commands = [ Run $ Network "eth0" ["-L","0","-H","32","--normal","#429942","--high","#A36666"] 10
               , Run $ Cpu ["-L","3","-H","50","--normal","green","--high","red"] 10
               , Run $ Memory ["-t","Mem: <usedratio>%"] 10
               , Run $ Date "/%a/ %_d.%m.%Y / %H:%M" "date" 10
               , Run $ Com "sh" ["~/bin/weather.sh"] "weather" 60
               , Run $ StdinReader
               ]
  , sepChar  = "%"
  , alignSep = "}{"
  , template = " %StdinReader% }{  W:<fc=#fce94f>%weather%</fc> | %cpu% | %memory% | %eth0% | %date%"
  }

It's almost exactly the same. But it doesn't have to be, because it's literally Haskell now, so you're actually allowed to use Thomas' idea, or go much, much further than that, to whatever degree you feel is best. The only drawback is, every time you edit the file, you have to compile and run it again, to get an updated xmobarrc.

For the second question:

In a command's option list, add "-w","3", replacing 3 with the size (in number of characters) that you want the command's fields to be fixed at. This will truncate a field if it goes over that size, and pad the field with spaces if it goes under. If you want to pad with 0's (or some other character) instead of spaces, add "-c","0" as well (or "-c","n", replacing n with the character you want to pad with).