Many minor popup improvements, add weather popup

This commit is contained in:
Camille Scholtz 2018-12-30 14:31:30 +01:00
parent 0b8a9d2e01
commit 9d5cf20d28
10 changed files with 238 additions and 54 deletions

@ -13,7 +13,8 @@ Or for a binary that includes embedded static files:
`packr2 get github.com/onodera-punpun/melonbar` `packr2 get github.com/onodera-punpun/melonbar`
`melonbar` depends on Go 1.9 or newer and [packr2](https://github.com/gobuffalo/packr/tree/master/v2). `melonbar` depends on Go 1.9 or newer, gnuplot, and
[packr2](https://github.com/gobuffalo/packr/tree/master/v2).
## USAGE ## USAGE

@ -2,3 +2,4 @@
* Fix weird `'` characters. * Fix weird `'` characters.
* Fix `?` character. * Fix `?` character.
* Fix `\n` character. * Fix `\n` character.
* Fix and add `º` character.

34
bar.go

@ -30,6 +30,9 @@ type Bar struct {
// right of the last block. // right of the last block.
xsum int xsum int
// Text drawer.
drawer *font.Drawer
// A map with information about the block, see the `Block` type. // A map with information about the block, see the `Block` type.
blocks *sync.Map blocks *sync.Map
@ -90,6 +93,11 @@ func initBar(x, y, w, h int) (*Bar, error) {
bar.w = w bar.w = w
bar.h = h bar.h = h
bar.drawer = &font.Drawer{
Dst: bar.img,
Face: face,
}
bar.blocks = new(sync.Map) bar.blocks = new(sync.Map)
bar.redraw = make(chan *Block) bar.redraw = make(chan *Block)
@ -111,6 +119,17 @@ func initBar(x, y, w, h int) (*Bar, error) {
return true return true
} }
// XXX: Hack for clock block.
if name == "clock" {
tw := bar.drawer.MeasureString(block.txt).Ceil()
if ev.EventX >= int16(((bar.w/2)-(tw/2))-13) && ev.
EventX < int16(((bar.w/2)+(tw/2))+13) {
return false
}
block = nil
return true
}
if ev.EventX >= int16(block.x) && ev.EventX < int16(block.x+block. if ev.EventX >= int16(block.x) && ev.EventX < int16(block.x+block.
w) { w) {
return false return false
@ -132,15 +151,9 @@ func initBar(x, y, w, h int) (*Bar, error) {
} }
func (bar *Bar) draw(block *Block) error { func (bar *Bar) draw(block *Block) error {
d := &font.Drawer{
Dst: block.img,
Src: image.NewUniform(hexToBGRA(block.fg)),
Face: face,
}
// Calculate the required x coordinate for the different aligments. // Calculate the required x coordinate for the different aligments.
var x int var x int
tw := d.MeasureString(block.txt).Ceil() tw := bar.drawer.MeasureString(block.txt).Ceil()
switch block.align { switch block.align {
case 'l': case 'l':
x = block.x x = block.x
@ -168,9 +181,12 @@ func (bar *Bar) draw(block *Block) error {
return hexToBGRA(block.bg) return hexToBGRA(block.bg)
}) })
// Set text color.
bar.drawer.Src = image.NewUniform(hexToBGRA(block.fg))
// Draw the text. // Draw the text.
d.Dot = fixed.P(x, 18) bar.drawer.Dot = fixed.P(x, 18)
d.DrawString(block.txt) bar.drawer.DrawString(block.txt)
// Redraw the bar. // Redraw the bar.
block.img.XDraw() block.img.XDraw()

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"log" "log"
"os" "os"
"os/exec"
"path" "path"
"strconv" "strconv"
"time" "time"
@ -25,6 +26,23 @@ func (bar *Bar) clock() {
// Notify that the next block can be initialized. // Notify that the next block can be initialized.
bar.ready <- true bar.ready <- true
// Show popup on clicking the left mouse button.
block.actions["button1"] = func() error {
if block.popup != nil {
block.popup = block.popup.destroy()
return nil
}
var err error
block.popup, err = initPopup((bar.w/2)-(178/2), 29, 178, 129, "#EEEEEE",
"#021B21")
if err != nil {
return err
}
return block.popup.clock()
}
for { for {
// Compose block text. // Compose block text.
txt := time.Now().Format("Monday, January 2th 03:04 PM") txt := time.Now().Format("Monday, January 2th 03:04 PM")
@ -147,6 +165,14 @@ func (bar *Bar) todo() {
// Notify that the next block can be initialized. // Notify that the next block can be initialized.
bar.ready <- true bar.ready <- true
// Show popup on clicking the left mouse button.
block.actions["button1"] = func() error {
cmd := exec.Command("st", "micro", "-savecursor", "false", path.Join(
basedir.Home, ".todo"))
cmd.Stdout = os.Stdout
return cmd.Run()
}
// Watch file for events. // Watch file for events.
w, err := fsnotify.NewWatcher() w, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
@ -226,9 +252,7 @@ func (bar *Bar) window() {
txt = "?" txt = "?"
} }
} }
if len(txt) > 34 { txt = trim(txt, 34)
txt = txt[0:34] + "..."
}
// Redraw block. // Redraw block.
if block.diff(txt) { if block.diff(txt) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 B

@ -73,11 +73,11 @@ func initFont() error {
fr := func(name string) ([]byte, error) { fr := func(name string) ([]byte, error) {
return box.Find(path.Join("fonts", name)) return box.Find(path.Join("fonts", name))
} }
font, err := box.Find("fonts/cure.font") fp, err := box.Find("fonts/cure.font")
if err != nil { if err != nil {
return err return err
} }
face, err = plan9font.ParseFont(font, fr) face, err = plan9font.ParseFont(fp, fr)
if err != nil { if err != nil {
return err return err
} }

@ -7,6 +7,7 @@ import (
"github.com/BurntSushi/xgbutil/ewmh" "github.com/BurntSushi/xgbutil/ewmh"
"github.com/BurntSushi/xgbutil/xgraphics" "github.com/BurntSushi/xgbutil/xgraphics"
"github.com/BurntSushi/xgbutil/xwindow" "github.com/BurntSushi/xgbutil/xwindow"
"golang.org/x/image/font"
) )
// Popup is a struct with information about the popup. // Popup is a struct with information about the popup.
@ -21,9 +22,8 @@ type Popup struct {
// The foreground and background colors in hex. // The foreground and background colors in hex.
bg, fg string bg, fg string
// A channel where the popup should be send to to once its ready // Text drawer.
// to be redrawn. drawer *font.Drawer
redraw chan *Popup
} }
func initPopup(x, y, w, h int, bg, fg string) (*Popup, error) { func initPopup(x, y, w, h int, bg, fg string) (*Popup, error) {
@ -77,16 +77,31 @@ func initPopup(x, y, w, h int, bg, fg string) (*Popup, error) {
popup.bg = bg popup.bg = bg
popup.fg = fg popup.fg = fg
popup.redraw = make(chan *Popup) popup.drawer = &font.Drawer{
Dst: popup.img,
Face: face,
}
// Color the background.
popup.img.For(func(cx, cy int) xgraphics.BGRA {
return hexToBGRA(popup.bg)
})
// Draw the popup.
popup.draw()
return popup, nil return popup, nil
} }
func (popup *Popup) draw() {
popup.img.XDraw()
popup.img.XPaint(popup.win.Id)
}
// TODO: I don't know if this actually frees memory and shit. // TODO: I don't know if this actually frees memory and shit.
func (popup *Popup) destroy() *Popup { func (popup *Popup) destroy() *Popup {
popup.win.Destroy() popup.win.Destroy()
popup.img.Destroy() popup.img.Destroy()
close(popup.redraw)
popup = nil popup = nil
return popup return popup

187
popups.go

@ -1,27 +1,29 @@
package main package main
import ( import (
"bufio"
"bytes"
"fmt"
"image" "image"
"io" "io"
"io/ioutil"
"math" "math"
"net/http"
"os" "os"
"os/exec"
"path" "path"
"strconv" "strconv"
"github.com/BurntSushi/xgbutil/xgraphics" "github.com/BurntSushi/xgbutil/xgraphics"
"github.com/antchfx/xmlquery"
"github.com/fhs/gompd/mpd" "github.com/fhs/gompd/mpd"
"github.com/rkoesters/xdg/userdirs" "github.com/rkoesters/xdg/userdirs"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
) )
// TODO: Make progressbar clickable.
// TODO: Make progressbar update every X milliseconds.
func (popup *Popup) music(c *mpd.Client) error { func (popup *Popup) music(c *mpd.Client) error {
d := &font.Drawer{
Dst: popup.img,
Src: image.NewUniform(hexToBGRA(popup.fg)),
Face: face,
}
// Color the background. // Color the background.
popup.img.For(func(cx, cy int) xgraphics.BGRA { popup.img.For(func(cx, cy int) xgraphics.BGRA {
return hexToBGRA(popup.bg) return hexToBGRA(popup.bg)
@ -36,31 +38,47 @@ func (popup *Popup) music(c *mpd.Client) error {
return err return err
} }
// Draw album info text. // Set text color.
d.Dot = fixed.P(10, 20) popup.drawer.Src = image.NewUniform(hexToBGRA(popup.fg))
d.DrawString("Album: " + cur["Album"])
d.Dot = fixed.P(10, 20+16)
d.DrawString("Artist: " + cur["AlbumArtist"])
d.Dot = fixed.P(10, 20+16+16)
d.DrawString("Date: " + cur["Date"])
// Find album art. // Draw album text.
var f interface{} album := trim(cur["Album"], 32)
f, err = os.Open(path.Join(userdirs.Music, path.Dir( popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(album).Ceil()/2)+82,
cur["file"]), "cover_popup.png")) 48)
if err != nil { popup.drawer.DrawString(album)
f, err = box.Open("images/cover.png")
// Draw artist text.
artist := trim("Artist: "+cur["AlbumArtist"], 32)
popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(artist).Ceil()/2)+
82, 58+16)
popup.drawer.DrawString(artist)
// Draw rlease date text.
date := trim("Release date: "+cur["Date"], 32)
popup.drawer.Dot = fixed.P(-(popup.drawer.MeasureString(date).Ceil()/2)+82,
58+16+16)
popup.drawer.DrawString(date)
// Check if album art file exists.
fp := path.Join(userdirs.Music, path.Dir(cur["file"]), "cover_popup.png")
if _, err := os.Stat(fp); !os.IsNotExist(err) {
f, err := os.Open(fp)
if err != nil { if err != nil {
return err return err
} }
} defer f.Close()
// Draw album art. // Draw album art.
img, _, err := image.Decode(f.(io.Reader)) img, _, err := image.Decode(f)
if err != nil { if err != nil {
return err return err
}
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, img), image.Point{
-166, -10})
} else {
popup.drawer.Dot = fixed.P(200, 78)
popup.drawer.DrawString("No cover found!")
} }
xgraphics.Blend(popup.img, img, image.Point{-166, -10})
// Calculate progressbar lengths. // Calculate progressbar lengths.
e, err := strconv.ParseFloat(sts["elapsed"], 32) e, err := strconv.ParseFloat(sts["elapsed"], 32)
@ -75,19 +93,120 @@ func (popup *Popup) music(c *mpd.Client) error {
pu := 29 - pf pu := 29 - pf
// Draw progressbar. // Draw progressbar.
d.Dot = fixed.P(10, 132) popup.drawer.Dot = fixed.P(10, 132)
d.Src = image.NewUniform(hexToBGRA("#5394C9"))
for i := 1; i <= pf; i++ { for i := 1; i <= pf; i++ {
d.DrawString("-") popup.drawer.DrawString("-")
} }
d.Src = image.NewUniform(hexToBGRA(popup.fg)) popup.drawer.Src = image.NewUniform(hexToBGRA("#72A7D3"))
for i := 1; i <= pu; i++ { for i := 1; i <= pu; i++ {
d.DrawString("-") popup.drawer.DrawString("-")
} }
// Draw the popup. // Redraw the bar.
popup.img.XDraw() popup.draw()
popup.img.XPaint(popup.win.Id)
return nil
}
func (popup *Popup) clock() error {
// Color the background.
f, err := box.Open("images/clock-popup-bg.png")
if err != nil {
return err
}
defer f.Close()
// Draw album art.
bg, _, err := image.Decode(f.(io.Reader))
if err != nil {
return err
}
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, bg), image.Point{0, 0})
// Redraw the popup.
popup.draw()
// Set location.
lat := "52.0646"
lon := "5.2065"
// Get rainfall information.
r, err := http.Get("https://gps.buienradar.nl/getrr.php?lat=" + lat +
"&lon=" + lon)
if err != nil {
return err
}
defer r.Body.Close()
// Create rainfall tmp files.
td, err := ioutil.TempFile(os.TempDir(), "melonbar-rain-*.dat")
if err != nil {
return err
}
defer os.Remove(td.Name())
ti, err := ioutil.TempFile(os.TempDir(), "melonbar-rain-*.png")
if err != nil {
return err
}
defer os.Remove(ti.Name())
// Compose rainfall data tmp file contents.
var d []byte
s := bufio.NewScanner(r.Body)
for s.Scan() {
d = append(d, bytes.Split(s.Bytes(), []byte("|"))[0]...)
d = append(d, []byte("\n")...)
}
// Write rainfall data tmp file.
if _, err = td.Write(d); err != nil {
return err
}
if err := td.Close(); err != nil {
return err
}
// Create rainfall graph.
cmd := exec.Command("gnuplot", "-e", `
set terminal png transparent size 211,107;
set output '`+ti.Name()+`';
set yrange [0:255];
set noborder;
set nolabel;
set nokey;
set notics;
set notitle;
set style fill solid border rgb '#5394C9';
plot '`+td.Name()+`' smooth csplines with filledcurve x1 lc rgb '#72A7D3'
`)
if err := cmd.Run(); err != nil {
return err
}
// Draw rainfall graph.
img, _, err := image.Decode(ti)
if err != nil {
return err
}
xgraphics.Blend(popup.img, xgraphics.NewConvert(X, img), image.Point{14, 0})
// Redraw the popup.
popup.draw()
// Get weather information.
x, err := xmlquery.LoadURL("https://xml.buienradar.nl")
if err != nil {
return err
}
w := xmlquery.FindOne(x, "//weerstation[@id=6260]")
fmt.Println()
popup.drawer.Src = image.NewUniform(hexToBGRA(popup.fg))
popup.drawer.Dot = fixed.P(10, 100)
popup.drawer.DrawString("Rainfall graph, it's " + w.SelectElement(
"temperatuurGC").InnerText() + "ºC")
// Redraw the popup.
popup.draw()
return nil return nil
} }

@ -13,3 +13,11 @@ func hexToBGRA(h string) xgraphics.BGRA {
return xgraphics.BGRA{B: d[2], G: d[1], R: d[0], A: 0xFF} return xgraphics.BGRA{B: d[2], G: d[1], R: d[0], A: 0xFF}
} }
// TODO: Instead of doing this using rune-count, do this using pixel-count.
func trim(txt string, l int) string {
if len(txt) > l {
return txt[0:l] + "..."
}
return txt
}