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 }