fyne-widgets/pkg/vumeter/vumeter.go

166 lines
3.5 KiB
Go

package vumeter
import (
"image/color"
"strconv"
"git.martyn.berlin/martyn/fyne-widgets/internal/cache"
col "git.martyn.berlin/martyn/fyne-widgets/internal/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type VUMeter struct {
widget.BaseWidget
Min, Max, Value float64
TextFormatter func() string
binder basicBinder
}
type vuMeterRenderer struct {
objects []fyne.CanvasObject
background, bar *canvas.Rectangle
label *canvas.Text
meter *VUMeter
}
func (p *vuMeterRenderer) MinSize() fyne.Size {
var tsize fyne.Size
if text := p.meter.TextFormatter; text != nil {
tsize = fyne.MeasureText(text(), p.label.TextSize, p.label.TextStyle)
} else {
tsize = fyne.MeasureText("100%", p.label.TextSize, p.label.TextStyle)
}
return fyne.NewSize(tsize.Width+theme.Padding()*4, tsize.Height+theme.Padding()*2)
}
func (p *vuMeterRenderer) updateBar() {
if p.meter.Value < p.meter.Min {
p.meter.Value = p.meter.Min
}
if p.meter.Value > p.meter.Max {
p.meter.Value = p.meter.Max
}
delta := float32(p.meter.Max - p.meter.Min)
ratio := float32(p.meter.Value-p.meter.Min) / delta
if text := p.meter.TextFormatter; text != nil {
p.label.Text = text()
} else {
p.label.Text = strconv.Itoa(int(ratio*100)) + "%"
}
size := p.meter.Size()
p.bar.Resize(fyne.NewSize(size.Width*ratio, size.Height))
}
func (p *vuMeterRenderer) Layout(size fyne.Size) {
p.background.Resize(size)
p.label.Resize(size)
p.updateBar()
}
func (p *vuMeterRenderer) applyTheme() {
p.background.FillColor = vuMeterBackgroundColor()
p.bar.FillColor = theme.PrimaryColor()
p.label.Color = theme.ForegroundColor()
p.label.TextSize = theme.TextSize()
}
func (p *vuMeterRenderer) Refresh() {
p.applyTheme()
p.updateBar()
p.background.Refresh()
p.bar.Refresh()
p.meter.super()
}
func (r *vuMeterRenderer) Objects() []fyne.CanvasObject {
return r.objects
}
// SetObjects updates the objects of the renderer.
func (r *vuMeterRenderer) SetObjects(objects []fyne.CanvasObject) {
r.objects = objects
}
func (r *vuMeterRenderer) Destroy() {
}
func (p *VUMeter) Bind(data binding.Float) {
p.binder.SetCallback(p.updateFromData)
p.binder.Bind(data)
}
func (p *VUMeter) SetValue(v float64) {
p.Value = v
p.Refresh()
}
func (p *VUMeter) MinSize() fyne.Size {
p.ExtendBaseWidget(p)
return p.BaseWidget.MinSize()
}
func (p *VUMeter) CreateRenderer() fyne.WidgetRenderer {
p.ExtendBaseWidget(p)
if p.Min == 0 && p.Max == 0 {
p.Max = 1.0
}
background := canvas.NewRectangle(vuMeterBackgroundColor())
bar := canvas.NewRectangle(theme.PrimaryColor())
label := canvas.NewText("0%", theme.ForegroundColor())
label.Alignment = fyne.TextAlignCenter
return &vuMeterRenderer{[]fyne.CanvasObject{background, bar, label}, background, bar, label, p}
}
func (p *VUMeter) Unbind() {
p.binder.Unbind()
}
func NewVUMeter() *VUMeter {
p := &VUMeter{Min: 0, Max: 1}
cache.Renderer(p).Layout(p.MinSize())
return p
}
func NewVUMeterWithData(data binding.Float) *VUMeter {
p := NewVUMeter()
p.Bind(data)
return p
}
func vuMeterBackgroundColor() color.Color {
r, g, b, a := col.ToNRGBA(theme.PrimaryColor())
faded := uint8(a) / 3
return &color.NRGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: faded}
}
func (p *VUMeter) updateFromData(data binding.DataItem) {
if data == nil {
return
}
floatSource, ok := data.(binding.Float)
if !ok {
return
}
val, err := floatSource.Get()
if err != nil {
fyne.LogError("Error getting current data value", err)
return
}
p.SetValue(val)
}