ui/uiwidget/scoredisplay.go
2024-05-04 16:25:30 +02:00

240 lines
5.2 KiB
Go

package uiwidget
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
)
type ScoreDisplayResources struct {
Empty fyne.Resource
Digits [10]fyne.Resource
}
func NewScoreDisplayResources(
empty fyne.Resource,
digits [10]fyne.Resource,
) *ScoreDisplayResources {
return &ScoreDisplayResources{
Empty: empty,
Digits: digits,
}
}
type ScoreDisplay struct {
widget.BaseWidget
Score int
scoreBinding binding.Int
numDigits int
digits []*canvas.Image
resources *ScoreDisplayResources
layout *ScoreDisplayLayout
cont *fyne.Container
relSize *fyne.Size
fixedSize *fyne.Size
}
func NewScoreDisplay(
score int,
numDigits int,
resources *ScoreDisplayResources,
relSize *fyne.Size,
fizedSize *fyne.Size,
) *ScoreDisplay {
sd := new(ScoreDisplay)
sd.Score = score
sd.numDigits = numDigits
sd.resources = resources
sd.digits = make([]*canvas.Image, numDigits)
for i := 0; i < numDigits; i++ {
sd.digits[i] = canvas.NewImageFromResource(resources.Empty)
}
sd.relSize = relSize
sd.fixedSize = fizedSize
sd.ExtendBaseWidget(sd)
sd.updateScore()
return sd
}
func NewScoreDisplayWithData(
scoreBinding binding.Int,
numDigits int,
resources *ScoreDisplayResources,
relSize *fyne.Size,
fixedSize *fyne.Size,
) *ScoreDisplay {
sd := new(ScoreDisplay)
sd.scoreBinding = scoreBinding
sd.numDigits = numDigits
sd.resources = resources
sd.digits = make([]*canvas.Image, numDigits)
for i := 0; i < numDigits; i++ {
sd.digits[i] = canvas.NewImageFromResource(resources.Empty)
}
sd.relSize = relSize
sd.fixedSize = fixedSize
sd.ExtendBaseWidget(sd)
sd.scoreBinding.AddListener(sd)
return sd
}
func (sd *ScoreDisplay) CreateRenderer() fyne.WidgetRenderer {
sd.layout = NewScoreDisplayLayout(sd.relSize, sd.fixedSize)
sd.cont = container.New(sd.layout)
for i := sd.numDigits; i > 0; i-- {
digit := sd.digits[i-1]
sd.cont.Add(digit)
}
return widget.NewSimpleRenderer(sd.cont)
}
func (sd *ScoreDisplay) Refresh() {
sd.updateScore()
sd.BaseWidget.Refresh()
}
func (sd *ScoreDisplay) Resize(size fyne.Size) {
if sd.fixedSize != nil {
sd.fixedSize = &size
}
}
func (sd *ScoreDisplay) Size() fyne.Size {
if sd.layout == nil {
if sd.fixedSize != nil {
return *sd.fixedSize
} else if sd.relSize != nil {
canvas := fyne.CurrentApp().Driver().AllWindows()[0].Canvas()
if canvas == nil {
return fyne.NewSize(0, 0)
}
size := fyne.NewSize(
canvas.Size().Width*sd.relSize.Width,
canvas.Size().Height*sd.relSize.Height,
)
return size
} else {
return fyne.NewSize(0, 0)
}
}
return sd.layout.CurrentSize()
}
func (sd *ScoreDisplay) MinSize() fyne.Size {
return sd.Size()
}
func (sd *ScoreDisplay) DataChanged() {
var err error
sd.Score, err = sd.scoreBinding.Get()
if err != nil {
return
}
sd.Refresh()
}
func (sd *ScoreDisplay) updateScore() {
aspect := float32(0)
score := sd.Score
for i := 0; i < sd.numDigits; i++ {
digit := score % 10
score /= 10
if i != 0 && digit == 0 && score == 0 {
sd.digits[i].Resource = sd.resources.Empty
} else {
sd.digits[i].Resource = sd.resources.Digits[digit]
}
sd.digits[i].ScaleMode = canvas.ImageScaleSmooth
sd.digits[i].FillMode = canvas.ImageFillContain
sd.digits[i].Resize(fyne.NewSize(200, 300))
aspect += sd.digits[i].Aspect()
}
if sd.layout != nil {
sd.layout.setAspect(aspect)
}
}
type ScoreDisplayLayout struct {
canvas fyne.Canvas
relSize *fyne.Size
fixedSize *fyne.Size
aspect float32
currentSize fyne.Size
}
func NewScoreDisplayLayout(relSize *fyne.Size, fixedSize *fyne.Size) *ScoreDisplayLayout {
return &ScoreDisplayLayout{
relSize: relSize,
fixedSize: fixedSize,
aspect: float32(0),
}
}
func (sdl *ScoreDisplayLayout) setAspect(a float32) {
sdl.aspect = a
}
func (sdl *ScoreDisplayLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
if sdl.fixedSize != nil {
return *sdl.fixedSize
}
return sdl.calculateSize(objects)
}
func (sdl *ScoreDisplayLayout) Layout(objects []fyne.CanvasObject, containerSize fyne.Size) {
size := sdl.calculateSize(objects)
sdl.currentSize = size
numObjects := len(objects)
objSize := fyne.NewSize(
size.Width/float32(numObjects),
size.Height)
pos := fyne.NewPos(0, 0)
for _, o := range objects {
o.Resize(objSize)
o.Move(pos)
pos = pos.Add(fyne.NewPos(objSize.Width, 0))
}
}
func (sdl *ScoreDisplayLayout) CurrentSize() fyne.Size {
return sdl.currentSize
}
func (sdl *ScoreDisplayLayout) calculateSize(objects []fyne.CanvasObject) fyne.Size {
size := fyne.NewSize(0, 0)
if sdl.relSize != nil {
if sdl.canvas == nil && len(objects) > 0 {
sdl.canvas = fyne.CurrentApp().Driver().CanvasForObject(objects[0])
}
if sdl.canvas != nil {
size = fyne.NewSize(
sdl.canvas.Size().Width*sdl.relSize.Width,
sdl.canvas.Size().Height*sdl.relSize.Height,
)
}
} else if sdl.fixedSize != nil {
size = *sdl.fixedSize
sdl.currentSize = size
}
if sdl.aspect > 0 {
curAspect := size.Width / size.Height
if curAspect > sdl.aspect {
size.Width = size.Height * sdl.aspect
} else {
size.Height = size.Width / sdl.aspect
}
sdl.currentSize = size
}
return size
}