Skip to content

Commit

Permalink
feat(dynamicicon): Automatically determine the max font size
Browse files Browse the repository at this point in the history
  • Loading branch information
gabe565 committed Aug 18, 2024
1 parent 8fd8e8d commit 6a67368
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 55 deletions.
8 changes: 4 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ type Config struct {
}

type DynamicIcon struct {
Enabled bool `toml:"enabled"`
FontColor HexColor `toml:"font_color" comment:"Hex code used to render text."`
FontFile string `toml:"font_file" comment:"If left blank, an embedded font will be used."`
FontSize float64 `toml:"font_size" comment:"Font size in points."`
Enabled bool `toml:"enabled"`
FontColor HexColor `toml:"font_color" comment:"Hex code used to render text."`
FontFile string `toml:"font_file" comment:"If left blank, an embedded font will be used."`
MaxFontSize float64 `toml:"max_font_size" comment:"Maximum font size in points."`
}

type Arrows struct {
Expand Down
6 changes: 3 additions & 3 deletions internal/config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ func New() *Config {
Title: "Nightscout",
Units: UnitsMgdl,
DynamicIcon: DynamicIcon{
Enabled: true,
FontColor: White(),
FontSize: 28,
Enabled: true,
FontColor: White(),
MaxFontSize: 40,
},
Arrows: Arrows{
DoubleUp: "⇈",
Expand Down
98 changes: 51 additions & 47 deletions internal/dynamicicon/dynamicicon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,66 +3,50 @@ package dynamicicon
import (
"bytes"
_ "embed"
"errors"
"image"
"image/draw"
"os"
"sync"

"fyne.io/systray"
"github.com/gabe565/nightscout-menu-bar/internal/assets"
"github.com/gabe565/nightscout-menu-bar/internal/config"
"github.com/gabe565/nightscout-menu-bar/internal/nightscout"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)

const (
width, height = 32, 32
widthF, heightF = fixed.Int26_6(width << 6), fixed.Int26_6(height << 6)
)

//go:embed Inconsolata_Condensed-Black.ttf
var defaultFont []byte

type DynamicIcon struct {
config *config.Config
mu sync.Mutex
callbackOffset int
config *config.Config
mu sync.Mutex

face font.Face
drawer *font.Drawer
img *image.RGBA
font *truetype.Font
img *image.RGBA
}

func New(conf *config.Config) *DynamicIcon {
d := &DynamicIcon{config: conf}
d.callbackOffset = conf.AddCallback(d.reloadConfig)
return d
}

func (d *DynamicIcon) Close() error {
d.mu.Lock()
defer d.mu.Unlock()
var err error
if d.face != nil {
err = d.face.Close()
d := &DynamicIcon{
config: conf,
img: image.NewRGBA(image.Rectangle{Max: image.Point{X: width, Y: height}}),
}
d.config.RemoveCallback(d.callbackOffset)
systray.SetTemplateIcon(assets.Nightscout, assets.Nightscout)
return err
return d
}

func (d *DynamicIcon) reloadConfig() {
_ = d.Close()
d.config.AddCallback(d.reloadConfig)
}
var ErrFontSize = errors.New("unable to determine the correct font size")

func (d *DynamicIcon) Generate(p *nightscout.Properties) ([]byte, error) {
d.mu.Lock()
defer d.mu.Unlock()

const (
width, height = 32, 32
widthF, heightF = fixed.Int26_6(width << 6), fixed.Int26_6(height << 6)
)

if d.face == nil {
if d.font == nil {
var b []byte
if d.config.DynamicIcon.FontFile == "" {
b = defaultFont
Expand All @@ -78,27 +62,47 @@ func (d *DynamicIcon) Generate(p *nightscout.Properties) ([]byte, error) {
return nil, err
}

d.face = truetype.NewFace(f, &truetype.Options{
Size: d.config.DynamicIcon.FontSize,
d.font = f
}

bgnow := p.Bgnow.DisplayBg(d.config.Units)

var face font.Face
defer func() {
if face != nil {
_ = face.Close()
}
}()

drawer := &font.Drawer{
Dst: d.img,
Src: image.NewUniform(d.config.DynamicIcon.FontColor.RGBA()),
}

fontSize := d.config.DynamicIcon.MaxFontSize
for {
face = truetype.NewFace(d.font, &truetype.Options{
Size: fontSize,
})
drawer.Face = face

d.img = image.NewRGBA(image.Rectangle{Max: image.Point{X: width, Y: height}})
if textWidth := drawer.MeasureString(bgnow); textWidth <= widthF {
break
}

m := d.face.Metrics()
src := image.NewUniform(d.config.DynamicIcon.FontColor.RGBA())
d.drawer = &font.Drawer{
Dst: d.img,
Src: src,
Face: d.face,
Dot: fixed.Point26_6{Y: (heightF + m.Ascent - m.Descent) / 2},
_ = face.Close()
if fontSize <= 1 {
return nil, ErrFontSize
}
} else {
draw.Draw(d.img, d.img.Bounds(), image.Transparent, image.Point{}, draw.Src)
fontSize -= 0.5
}

bgnow := p.Bgnow.DisplayBg(d.config.Units)
d.drawer.Dot.X = (widthF - d.drawer.MeasureString(bgnow)) / 2
d.drawer.DrawString(bgnow)
metrics := face.Metrics()

draw.Draw(d.img, d.img.Bounds(), image.Transparent, image.Point{}, draw.Src)
drawer.Dot.X = (widthF - drawer.MeasureString(bgnow)) / 2
drawer.Dot.Y = (heightF + metrics.Ascent - metrics.Descent) / 2
drawer.DrawString(bgnow)

var buf bytes.Buffer
if err := encode(&buf, d.img); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/tray/systray.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ func (t *Tray) onReady() { //nolint:gocyclo
t.dynamicIcon = dynamicicon.New(t.config)
} else {
if t.dynamicIcon != nil {
_ = t.dynamicIcon.Close()
t.dynamicIcon = nil
systray.SetTemplateIcon(assets.Nightscout, assets.Nightscout)
}
t.dynamicIcon = nil
}
Expand Down
Binary file added test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6a67368

Please sign in to comment.