5bar5/bar.go

153 lines
3.2 KiB
Go

package main
import (
"fmt"
"image"
"log"
"github.com/BurntSushi/xgb/xproto"
"github.com/BurntSushi/xgbutil/xgraphics"
"github.com/BurntSushi/xgbutil/xwindow"
"github.com/elliotchance/orderedmap"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
// Bar is a struct with information about the bar.
type Bar struct {
// Bar window, and bar image.
win *xwindow.Window
img *xgraphics.Image
// The width and height of the bar.
w, h int
// This is a sum of all of the block widths, used to draw a block to the
// right of the last block.
xsum int
// Text drawer.
drawer *font.Drawer
// A map that stores the various blocks.
blocks *orderedmap.OrderedMap
// A map that stores the various popups.
popups *orderedmap.OrderedMap
// Store is an interface to store variables and objects to be used by other
// blocks or popups.
store map[string]interface{}
// A channel where the block should be send to to once its ready to be
// redrawn.
redraw chan *Block
}
func initBar(x, y, w, h int) (*Bar, error) {
bar := new(Bar)
var err error
// Create a window for the bar. This window listens to button press events
// in order to respond to them.
bar.win, err = xwindow.Generate(X)
if err != nil {
return nil, err
}
bar.win.Create(X.RootWin(), x, y, w, h, xproto.CwBackPixel|xproto.
CwEventMask, 0x000000, xproto.EventMaskButtonPress)
// EWMH stuff to make the window behave like an actual bar.
if err := initEWMH(bar.win.Id); err != nil {
return nil, err
}
// Map window.
bar.win.Map()
// XXX: Moving the window is again a hack to keep OpenBox happy.
bar.win.Move(x, y)
// Create the bar image.
bar.img = xgraphics.New(X, image.Rect(0, 0, w, h))
if err := bar.img.XSurfaceSet(bar.win.Id); err != nil {
return nil, err
}
bar.img.XDraw()
// Set bar width and height.
bar.w = w
bar.h = h
// Set bar font face.
bar.drawer = &font.Drawer{
Dst: bar.img,
Face: face,
}
// Creat blocks and popups map.
bar.blocks = orderedmap.NewOrderedMap()
bar.popups = orderedmap.NewOrderedMap()
// Create store map.
bar.store = make(map[string]interface{})
// Create redraw channel.
bar.redraw = make(chan *Block)
return bar, nil
}
func (bar *Bar) draw(block *Block) error {
// Calculate the required x coordinate for the different aligments.
var x int
tw := bar.drawer.MeasureString(block.txt).Round()
switch block.align {
case 'l':
x = block.x
case 'c':
x = block.x + ((block.w / 2) - (tw / 2))
case 'r':
x = (block.x + block.w) - tw
case 'a':
x = (bar.w / 2) - (tw / 2)
default:
return fmt.Errorf("draw %#U: Not a valid aligment rune", block.align)
}
x += block.xoff
x += 2
// Color the background.
block.img.For(func(cx, cy int) xgraphics.BGRA {
// XXX: Hack for music block.
if block.w == 660 {
if cx < x+block.xoff {
return xgraphics.BGRA{B: 103, G: 89, R: 68, A: 0xFF}
}
}
return block.bg
})
// Set foreground color.
bar.drawer.Src = image.NewUniform(block.fg)
// Draw the text.
bar.drawer.Dot = fixed.P(x, 18)
bar.drawer.DrawString(block.txt)
// Redraw the bar.
block.img.XDraw()
bar.img.XPaint(bar.win.Id)
return nil
}
func (bar *Bar) listen() {
for {
if err := bar.draw(<-bar.redraw); err != nil {
log.Fatalln(err)
}
}
}