Added score display
This commit is contained in:
		@@ -2,6 +2,7 @@ package ui
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
 | 
						"io/fs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"fyne.io/fyne/v2"
 | 
						"fyne.io/fyne/v2"
 | 
				
			||||||
	"fyne.io/fyne/v2/data/binding"
 | 
						"fyne.io/fyne/v2/data/binding"
 | 
				
			||||||
@@ -57,6 +58,7 @@ func NewBaseViewWithScreen(
 | 
				
			|||||||
	filesystem embed.FS,
 | 
						filesystem embed.FS,
 | 
				
			||||||
	screenName string,
 | 
						screenName string,
 | 
				
			||||||
	localizer *i18n.Localizer,
 | 
						localizer *i18n.Localizer,
 | 
				
			||||||
 | 
						assetLoader func(string) (fs.File, error),
 | 
				
			||||||
) *BaseView {
 | 
					) *BaseView {
 | 
				
			||||||
	v := NewBaseView(concreteView, viewModel)
 | 
						v := NewBaseView(concreteView, viewModel)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,7 +67,8 @@ func NewBaseViewWithScreen(
 | 
				
			|||||||
		filesystem,
 | 
							filesystem,
 | 
				
			||||||
		screenName,
 | 
							screenName,
 | 
				
			||||||
		viewModel,
 | 
							viewModel,
 | 
				
			||||||
		localizer)
 | 
							localizer,
 | 
				
			||||||
 | 
							assetLoader)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		LogWindowError(v, LoadScreen, err)
 | 
							LogWindowError(v, LoadScreen, err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -130,3 +133,7 @@ func (v *BaseView) OnHide() {
 | 
				
			|||||||
func (v *BaseView) GetBinding(bindingName string) binding.DataItem {
 | 
					func (v *BaseView) GetBinding(bindingName string) binding.DataItem {
 | 
				
			||||||
	return v.ViewModel.GetBinding(bindingName)
 | 
						return v.ViewModel.GetBinding(bindingName)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v *BaseView) GetCanvasObject() fyne.CanvasObject {
 | 
				
			||||||
 | 
						return v.CanvasObject
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								dialog.go
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								dialog.go
									
									
									
									
									
								
							@@ -3,6 +3,7 @@ package ui
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/fs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"fyne.io/fyne/v2"
 | 
						"fyne.io/fyne/v2"
 | 
				
			||||||
	"fyne.io/fyne/v2/data/binding"
 | 
						"fyne.io/fyne/v2/data/binding"
 | 
				
			||||||
@@ -91,6 +92,7 @@ func NewBaseDialogWithScreen(
 | 
				
			|||||||
	filesystem embed.FS,
 | 
						filesystem embed.FS,
 | 
				
			||||||
	screenName string,
 | 
						screenName string,
 | 
				
			||||||
	localizer *i18n.Localizer,
 | 
						localizer *i18n.Localizer,
 | 
				
			||||||
 | 
						assetLoader func(string) (fs.File, error),
 | 
				
			||||||
) *BaseDialog {
 | 
					) *BaseDialog {
 | 
				
			||||||
	d := NewBaseDialog(
 | 
						d := NewBaseDialog(
 | 
				
			||||||
		concreteDialog, name, kind, viewModel,
 | 
							concreteDialog, name, kind, viewModel,
 | 
				
			||||||
@@ -98,7 +100,7 @@ func NewBaseDialogWithScreen(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	d.ScreenHandler, err = NewScreenHandler(
 | 
						d.ScreenHandler, err = NewScreenHandler(
 | 
				
			||||||
		filesystem, screenName, viewModel, localizer)
 | 
							filesystem, screenName, viewModel, localizer, assetLoader)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		LogWindowError(d, LoadScreen, err)
 | 
							LogWindowError(d, LoadScreen, err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -138,6 +140,7 @@ func NewBaseDialogWithDataAndScreen(
 | 
				
			|||||||
	filesystem embed.FS,
 | 
						filesystem embed.FS,
 | 
				
			||||||
	screenName string,
 | 
						screenName string,
 | 
				
			||||||
	localizer *i18n.Localizer,
 | 
						localizer *i18n.Localizer,
 | 
				
			||||||
 | 
						assetLoader func(string) (fs.File, error),
 | 
				
			||||||
) *BaseDialog {
 | 
					) *BaseDialog {
 | 
				
			||||||
	d := NewBaseDialogWithData(
 | 
						d := NewBaseDialogWithData(
 | 
				
			||||||
		concreteDialog, name, kind, viewModel,
 | 
							concreteDialog, name, kind, viewModel,
 | 
				
			||||||
@@ -145,7 +148,7 @@ func NewBaseDialogWithDataAndScreen(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	d.ScreenHandler, err = NewScreenHandler(
 | 
						d.ScreenHandler, err = NewScreenHandler(
 | 
				
			||||||
		filesystem, screenName, viewModel, localizer)
 | 
							filesystem, screenName, viewModel, localizer, assetLoader)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		LogWindowError(d, LoadScreen, err)
 | 
							LogWindowError(d, LoadScreen, err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -248,6 +251,10 @@ func (d *BaseDialog) OnClose(confirmed bool) {
 | 
				
			|||||||
	d.OnHide()
 | 
						d.OnHide()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *BaseDialog) GetCanvasObject() fyne.CanvasObject {
 | 
				
			||||||
 | 
						return d.CanvasObj
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type FormDialogItemsFn func() []*widget.FormItem
 | 
					type FormDialogItemsFn func() []*widget.FormItem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type FormDialog struct {
 | 
					type FormDialog struct {
 | 
				
			||||||
@@ -288,11 +295,12 @@ func NewFormDialogWithScreen(
 | 
				
			|||||||
	filesystem embed.FS,
 | 
						filesystem embed.FS,
 | 
				
			||||||
	screenName string,
 | 
						screenName string,
 | 
				
			||||||
	localizer *i18n.Localizer,
 | 
						localizer *i18n.Localizer,
 | 
				
			||||||
 | 
						assetLoader func(string) (fs.File, error),
 | 
				
			||||||
) *FormDialog {
 | 
					) *FormDialog {
 | 
				
			||||||
	return &FormDialog{
 | 
						return &FormDialog{
 | 
				
			||||||
		BaseDialog: NewBaseDialogWithScreen(
 | 
							BaseDialog: NewBaseDialogWithScreen(
 | 
				
			||||||
			concreteDialog, name, kind, viewModel, parent, confirm,
 | 
								concreteDialog, name, kind, viewModel, parent, confirm,
 | 
				
			||||||
			filesystem, screenName, localizer),
 | 
								filesystem, screenName, localizer, assetLoader),
 | 
				
			||||||
		confirmLabel: "OK",
 | 
							confirmLabel: "OK",
 | 
				
			||||||
		dismissLabel: "Cancel",
 | 
							dismissLabel: "Cancel",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -328,11 +336,12 @@ func NewFormDialogWithDataAndScreen(
 | 
				
			|||||||
	filesystem embed.FS,
 | 
						filesystem embed.FS,
 | 
				
			||||||
	screenName string,
 | 
						screenName string,
 | 
				
			||||||
	localizer *i18n.Localizer,
 | 
						localizer *i18n.Localizer,
 | 
				
			||||||
 | 
						assetLoader func(string) (fs.File, error),
 | 
				
			||||||
) *FormDialog {
 | 
					) *FormDialog {
 | 
				
			||||||
	return &FormDialog{
 | 
						return &FormDialog{
 | 
				
			||||||
		BaseDialog: NewBaseDialogWithDataAndScreen(
 | 
							BaseDialog: NewBaseDialogWithDataAndScreen(
 | 
				
			||||||
			concreteDialog, name, kind, viewModel, parent, confirm, data,
 | 
								concreteDialog, name, kind, viewModel, parent, confirm, data,
 | 
				
			||||||
			filesystem, screenName, localizer),
 | 
								filesystem, screenName, localizer, assetLoader),
 | 
				
			||||||
		confirmLabel: "OK",
 | 
							confirmLabel: "OK",
 | 
				
			||||||
		dismissLabel: "Cancel",
 | 
							dismissLabel: "Cancel",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -485,3 +494,20 @@ func NewDialogWithDataAndScreen(
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ShowConfirmDialog(
 | 
				
			||||||
 | 
						title string,
 | 
				
			||||||
 | 
						message string,
 | 
				
			||||||
 | 
						lblOk string,
 | 
				
			||||||
 | 
						lblCancel string,
 | 
				
			||||||
 | 
						callback func(bool),
 | 
				
			||||||
 | 
						parent fyne.Window,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						label := widget.NewLabel(message)
 | 
				
			||||||
 | 
						label.Wrapping = fyne.TextWrapBreak
 | 
				
			||||||
 | 
						dlg := dialog.NewCustomConfirm(
 | 
				
			||||||
 | 
							title, lblOk, lblCancel,
 | 
				
			||||||
 | 
							label, callback, parent)
 | 
				
			||||||
 | 
						dlg.Resize(fyne.NewSize(480, 100))
 | 
				
			||||||
 | 
						dlg.Show()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										46
									
								
								screen/assets.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								screen/assets.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					package screen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"fyne.io/fyne/v2"
 | 
				
			||||||
 | 
						"fyne.io/fyne/v2/canvas"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func AssetToImage(
 | 
				
			||||||
 | 
						s ScreenHandler,
 | 
				
			||||||
 | 
						asset string,
 | 
				
			||||||
 | 
					) (
 | 
				
			||||||
 | 
						img *canvas.Image,
 | 
				
			||||||
 | 
						err error,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						fh, err := s.LoadAsset(asset)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							err = fmt.Errorf("failed to load asset: %w", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						img = canvas.NewImageFromReader(fh, asset)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func AssetToResource(
 | 
				
			||||||
 | 
						s ScreenHandler,
 | 
				
			||||||
 | 
						asset string,
 | 
				
			||||||
 | 
					) (
 | 
				
			||||||
 | 
						res fyne.Resource,
 | 
				
			||||||
 | 
						err error,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						fh, err := s.LoadAsset(asset)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							err = fmt.Errorf("failed to load asset: %w", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bytes, err := io.ReadAll(fh)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							err = fmt.Errorf("failed to read asset: %w", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res = fyne.NewStaticResource(asset, bytes)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										197
									
								
								screen/canvas.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								screen/canvas.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
				
			|||||||
 | 
					package screen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"image/color"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"bitbucket.org/hevanto/ui/uiwidget"
 | 
				
			||||||
 | 
						"fyne.io/fyne/v2"
 | 
				
			||||||
 | 
						"fyne.io/fyne/v2/canvas"
 | 
				
			||||||
 | 
						"fyne.io/fyne/v2/container"
 | 
				
			||||||
 | 
						"fyne.io/fyne/v2/data/binding"
 | 
				
			||||||
 | 
						"fyne.io/fyne/v2/layout"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Canvas string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						Image     Canvas = "Image"
 | 
				
			||||||
 | 
						Rectangle Canvas = "Rectangle"
 | 
				
			||||||
 | 
						Text      Canvas = "Text"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c Canvas) Build(
 | 
				
			||||||
 | 
						e *Element,
 | 
				
			||||||
 | 
						s ScreenHandler,
 | 
				
			||||||
 | 
					) (
 | 
				
			||||||
 | 
						cnv fyne.CanvasObject,
 | 
				
			||||||
 | 
						decorator fyne.CanvasObject,
 | 
				
			||||||
 | 
						err error,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						e.localize(s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch c {
 | 
				
			||||||
 | 
						case Image:
 | 
				
			||||||
 | 
							cnv, err = c.BuildImageCanvas(e, s)
 | 
				
			||||||
 | 
						case Rectangle:
 | 
				
			||||||
 | 
							cnv, err = c.BuildRectangleCanvas(e, s)
 | 
				
			||||||
 | 
						case Text:
 | 
				
			||||||
 | 
							cnv, err = c.BuildTextCanvas(e, s)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							err = errors.New("invalid canvas")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						decorator = cnv
 | 
				
			||||||
 | 
						if e.Decorators != nil {
 | 
				
			||||||
 | 
							for _, dec := range e.Decorators {
 | 
				
			||||||
 | 
								switch dec {
 | 
				
			||||||
 | 
								case "Border":
 | 
				
			||||||
 | 
									decorator = uiwidget.NewWidgetBorder(decorator)
 | 
				
			||||||
 | 
								case "HCenter":
 | 
				
			||||||
 | 
									decorator = container.NewHBox(
 | 
				
			||||||
 | 
										layout.NewSpacer(),
 | 
				
			||||||
 | 
										decorator,
 | 
				
			||||||
 | 
										layout.NewSpacer())
 | 
				
			||||||
 | 
								case "VCenter":
 | 
				
			||||||
 | 
									decorator = container.NewVBox(
 | 
				
			||||||
 | 
										layout.NewSpacer(),
 | 
				
			||||||
 | 
										decorator,
 | 
				
			||||||
 | 
										layout.NewSpacer())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if e.Hidden {
 | 
				
			||||||
 | 
							decorator.Hide()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Canvas) BuildImageCanvas(
 | 
				
			||||||
 | 
						e *Element,
 | 
				
			||||||
 | 
						s ScreenHandler,
 | 
				
			||||||
 | 
					) (obj fyne.CanvasObject, err error) {
 | 
				
			||||||
 | 
						img, err := AssetToImage(s, e.ImageName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							err = fmt.Errorf("failed to load image: %w", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if e.Translucency != nil {
 | 
				
			||||||
 | 
							img.Translucency = *e.Translucency
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for opt, val := range e.Options {
 | 
				
			||||||
 | 
							switch opt {
 | 
				
			||||||
 | 
							case "scaleMode":
 | 
				
			||||||
 | 
								img.ScaleMode = getImageScale(val)
 | 
				
			||||||
 | 
							case "fillMode":
 | 
				
			||||||
 | 
								img.FillMode = getImageFill(val)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						obj = img
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c Canvas) BuildRectangleCanvas(
 | 
				
			||||||
 | 
						e *Element,
 | 
				
			||||||
 | 
						s ScreenHandler,
 | 
				
			||||||
 | 
					) (obj fyne.CanvasObject, err error) {
 | 
				
			||||||
 | 
						fClr := color.RGBA{R: 255, G: 255, B: 255, A: 255}
 | 
				
			||||||
 | 
						if e.FillColor != nil {
 | 
				
			||||||
 | 
							fClr = color.RGBA{
 | 
				
			||||||
 | 
								R: e.FillColor.Red,
 | 
				
			||||||
 | 
								G: e.FillColor.Green,
 | 
				
			||||||
 | 
								B: e.FillColor.Blue,
 | 
				
			||||||
 | 
								A: e.FillColor.Alpha,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rect := canvas.NewRectangle(fClr)
 | 
				
			||||||
 | 
						if e.StrokeColor != nil {
 | 
				
			||||||
 | 
							rect.StrokeColor = color.RGBA{
 | 
				
			||||||
 | 
								R: e.StrokeColor.Red,
 | 
				
			||||||
 | 
								G: e.StrokeColor.Green,
 | 
				
			||||||
 | 
								B: e.StrokeColor.Blue,
 | 
				
			||||||
 | 
								A: e.StrokeColor.Alpha,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rect.StrokeWidth = 0
 | 
				
			||||||
 | 
						rect.CornerRadius = 0
 | 
				
			||||||
 | 
						if e.StrokeWidth != nil {
 | 
				
			||||||
 | 
							rect.StrokeWidth = *e.StrokeWidth
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if e.CornerRadius != nil {
 | 
				
			||||||
 | 
							rect.CornerRadius = *e.CornerRadius
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						obj = rect
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c Canvas) BuildTextCanvas(
 | 
				
			||||||
 | 
						e *Element,
 | 
				
			||||||
 | 
						s ScreenHandler,
 | 
				
			||||||
 | 
					) (obj fyne.CanvasObject, err error) {
 | 
				
			||||||
 | 
						txtClr := color.RGBA{R: 255, G: 255, B: 255, A: 255}
 | 
				
			||||||
 | 
						if e.TextColor != nil {
 | 
				
			||||||
 | 
							txtClr = color.RGBA{
 | 
				
			||||||
 | 
								R: e.TextColor.Red,
 | 
				
			||||||
 | 
								G: e.TextColor.Green,
 | 
				
			||||||
 | 
								B: e.TextColor.Blue,
 | 
				
			||||||
 | 
								A: e.TextColor.Alpha,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						txt := canvas.NewText(e.Text, txtClr)
 | 
				
			||||||
 | 
						if e.Binding != "" {
 | 
				
			||||||
 | 
							bnd := s.GetBinding(e.Binding).(binding.String)
 | 
				
			||||||
 | 
							bnd.AddListener(binding.NewDataListener(func() {
 | 
				
			||||||
 | 
								txt.Text, _ = bnd.Get()
 | 
				
			||||||
 | 
								txt.Refresh()
 | 
				
			||||||
 | 
							}))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if e.TextSize != nil {
 | 
				
			||||||
 | 
							txt.TextSize = *e.TextSize
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for opt, val := range e.Options {
 | 
				
			||||||
 | 
							switch opt {
 | 
				
			||||||
 | 
							case "alignment":
 | 
				
			||||||
 | 
								txt.Alignment = getTextAlignment(val)
 | 
				
			||||||
 | 
							case "textStyle":
 | 
				
			||||||
 | 
								txt.TextStyle = getTextStyle(val)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						obj = txt
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getImageFill(v any) canvas.ImageFill {
 | 
				
			||||||
 | 
						switch v.(string) {
 | 
				
			||||||
 | 
						case "Stretch":
 | 
				
			||||||
 | 
							return canvas.ImageFillStretch
 | 
				
			||||||
 | 
						case "Contain":
 | 
				
			||||||
 | 
							return canvas.ImageFillContain
 | 
				
			||||||
 | 
						case "Original":
 | 
				
			||||||
 | 
							return canvas.ImageFillOriginal
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return canvas.ImageFillStretch
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getImageScale(v any) canvas.ImageScale {
 | 
				
			||||||
 | 
						switch v.(string) {
 | 
				
			||||||
 | 
						case "Smooth":
 | 
				
			||||||
 | 
							return canvas.ImageScaleSmooth
 | 
				
			||||||
 | 
						case "Pixels":
 | 
				
			||||||
 | 
							return canvas.ImageScalePixels
 | 
				
			||||||
 | 
						case "Fastest":
 | 
				
			||||||
 | 
							return canvas.ImageScaleFastest
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return canvas.ImageScaleSmooth
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -13,6 +13,18 @@ type Size struct {
 | 
				
			|||||||
	Height float32 `yaml:"height"`
 | 
						Height float32 `yaml:"height"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RelativeSize struct {
 | 
				
			||||||
 | 
						Width  float32 `yaml:"width"`
 | 
				
			||||||
 | 
						Height float32 `yaml:"height"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Color struct {
 | 
				
			||||||
 | 
						Red   uint8 `yaml:"red"`
 | 
				
			||||||
 | 
						Green uint8 `yaml:"green"`
 | 
				
			||||||
 | 
						Blue  uint8 `yaml:"blue"`
 | 
				
			||||||
 | 
						Alpha uint8 `yaml:"alpha"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Element represents the yaml description of a ui element
 | 
					// Element represents the yaml description of a ui element
 | 
				
			||||||
type Element struct {
 | 
					type Element struct {
 | 
				
			||||||
	ID string `yaml:"id"`
 | 
						ID string `yaml:"id"`
 | 
				
			||||||
@@ -20,6 +32,7 @@ type Element struct {
 | 
				
			|||||||
	Container Container `yaml:"container"`
 | 
						Container Container `yaml:"container"`
 | 
				
			||||||
	Layout    Layout    `yaml:"layout"`
 | 
						Layout    Layout    `yaml:"layout"`
 | 
				
			||||||
	Widget    Widget    `yaml:"widget"`
 | 
						Widget    Widget    `yaml:"widget"`
 | 
				
			||||||
 | 
						Canvas    Canvas    `yaml:"canvas"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Border Layout Elements
 | 
						// Border Layout Elements
 | 
				
			||||||
	Top    *Element   `yaml:"top"`
 | 
						Top    *Element   `yaml:"top"`
 | 
				
			||||||
@@ -45,6 +58,11 @@ type Element struct {
 | 
				
			|||||||
	// MinSize configuration
 | 
						// MinSize configuration
 | 
				
			||||||
	MinSize *Size `yaml:"minSize"`
 | 
						MinSize *Size `yaml:"minSize"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Relative Size configuration
 | 
				
			||||||
 | 
						// Only valid for certain widgets:
 | 
				
			||||||
 | 
						//	- ScoreDisplay
 | 
				
			||||||
 | 
						RelativeSize *RelativeSize `yaml:"relativeSize"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Widget Properties
 | 
						// Widget Properties
 | 
				
			||||||
	Text       string         `yaml:"text"`
 | 
						Text       string         `yaml:"text"`
 | 
				
			||||||
	Localized  bool           `yaml:"localized"`
 | 
						Localized  bool           `yaml:"localized"`
 | 
				
			||||||
@@ -75,6 +93,10 @@ type Element struct {
 | 
				
			|||||||
	// Check Properties
 | 
						// Check Properties
 | 
				
			||||||
	Checked bool `yaml:"checked"`
 | 
						Checked bool `yaml:"checked"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ScoreDisplay Properties
 | 
				
			||||||
 | 
						Score     int `yaml:"score"`
 | 
				
			||||||
 | 
						NumDigits int `yaml:"numDigits"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Select Properties
 | 
						// Select Properties
 | 
				
			||||||
	SelectOptionsBinding string   `yaml:"selectOptionsBinding"`
 | 
						SelectOptionsBinding string   `yaml:"selectOptionsBinding"`
 | 
				
			||||||
	SelectOptions        []string `yaml:"selectOptions"`
 | 
						SelectOptions        []string `yaml:"selectOptions"`
 | 
				
			||||||
@@ -83,6 +105,19 @@ type Element struct {
 | 
				
			|||||||
	FixHorizontal bool `yaml:"fixHorizontal"`
 | 
						FixHorizontal bool `yaml:"fixHorizontal"`
 | 
				
			||||||
	FixVertical   bool `yaml:"fixVertical"`
 | 
						FixVertical   bool `yaml:"fixVertical"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Canvas Properties
 | 
				
			||||||
 | 
						FillColor    *Color   `yaml:"fillColor"`
 | 
				
			||||||
 | 
						StrokeColor  *Color   `yaml:"strokeColor"`
 | 
				
			||||||
 | 
						TextColor    *Color   `yaml:"textColor"`
 | 
				
			||||||
 | 
						StrokeWidth  *float32 `yaml:"strokeWidth"`
 | 
				
			||||||
 | 
						CornerRadius *float32 `yaml:"cornerRadius"`
 | 
				
			||||||
 | 
						TextSize     *float32 `yaml:"textSize"`
 | 
				
			||||||
 | 
						ImageName    string   `yaml:"imageName"`
 | 
				
			||||||
 | 
						Translucency *float64 `yaml:"translucency"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Widgets requiring a set of resources (e.g ScoreDisplay)
 | 
				
			||||||
 | 
						ResourceSet map[string]string `yaml:"resourceSet"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handlers
 | 
						// Handlers
 | 
				
			||||||
	OnClicked           string `yaml:"onClicked"`
 | 
						OnClicked           string `yaml:"onClicked"`
 | 
				
			||||||
	OnUpClicked         string `yaml:"onUpClicked"`
 | 
						OnUpClicked         string `yaml:"onUpClicked"`
 | 
				
			||||||
@@ -128,6 +163,13 @@ func (e *Element) BuildUI(s ScreenHandler) (obj fyne.CanvasObject, err error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if e.Canvas != "" {
 | 
				
			||||||
 | 
							if baseObject, obj, err = e.Canvas.Build(e, s); err != nil {
 | 
				
			||||||
 | 
								err = fmt.Errorf("failed to build canvas element: %w", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package screen
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/fs"
 | 
						"io/fs"
 | 
				
			||||||
@@ -35,6 +36,8 @@ type UIElement struct {
 | 
				
			|||||||
type ScreenHandler interface {
 | 
					type ScreenHandler interface {
 | 
				
			||||||
	RegisterElement(string, fyne.CanvasObject, fyne.CanvasObject)
 | 
						RegisterElement(string, fyne.CanvasObject, fyne.CanvasObject)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LoadAsset(string) (fs.File, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GetLocalizer() *i18n.Localizer
 | 
						GetLocalizer() *i18n.Localizer
 | 
				
			||||||
	GetIcon(string) fyne.Resource
 | 
						GetIcon(string) fyne.Resource
 | 
				
			||||||
	GetBinding(string) binding.DataItem
 | 
						GetBinding(string) binding.DataItem
 | 
				
			||||||
@@ -133,6 +136,10 @@ func (d *TemplateScreenHandler) RegisterElement(
 | 
				
			|||||||
	d.elementsMap[name] = &UIElement{element, decorator}
 | 
						d.elementsMap[name] = &UIElement{element, decorator}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *TemplateScreenHandler) LoadAsset(name string) (fs.File, error) {
 | 
				
			||||||
 | 
						return nil, errors.New("not implemented")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *TemplateScreenHandler) GetElement(name string) *UIElement {
 | 
					func (d *TemplateScreenHandler) GetElement(name string) *UIElement {
 | 
				
			||||||
	elem, ok := d.elementsMap[name]
 | 
						elem, ok := d.elementsMap[name]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,8 @@ package screen
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"bitbucket.org/hevanto/ui/uiwidget"
 | 
						"bitbucket.org/hevanto/ui/uiwidget"
 | 
				
			||||||
@@ -170,6 +172,9 @@ const (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	RichText Widget = "RichText"
 | 
						RichText Widget = "RichText"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ScoreDisplay Widget
 | 
				
			||||||
 | 
						ScoreDisplay Widget = "ScoreDisplay"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Select Widget
 | 
						// Select Widget
 | 
				
			||||||
	Select Widget = "Select"
 | 
						Select Widget = "Select"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -232,6 +237,8 @@ func (w Widget) Build(
 | 
				
			|||||||
		widget, err = w.buildListWidget(e, s)
 | 
							widget, err = w.buildListWidget(e, s)
 | 
				
			||||||
	case RichText:
 | 
						case RichText:
 | 
				
			||||||
		widget, err = w.buildRichTextWidget(e, s)
 | 
							widget, err = w.buildRichTextWidget(e, s)
 | 
				
			||||||
 | 
						case ScoreDisplay:
 | 
				
			||||||
 | 
							widget, err = w.buildScoreDisplayWidget(e, s)
 | 
				
			||||||
	case Select:
 | 
						case Select:
 | 
				
			||||||
		widget, err = w.buildSelectWidget(e, s)
 | 
							widget, err = w.buildSelectWidget(e, s)
 | 
				
			||||||
	case SelectEntry:
 | 
						case SelectEntry:
 | 
				
			||||||
@@ -528,6 +535,66 @@ func (w Widget) buildRichTextWidget(
 | 
				
			|||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (w Widget) buildScoreDisplayWidget(
 | 
				
			||||||
 | 
						e *Element,
 | 
				
			||||||
 | 
						s ScreenHandler,
 | 
				
			||||||
 | 
					) (c fyne.CanvasObject, err error) {
 | 
				
			||||||
 | 
						var sd *uiwidget.ScoreDisplay
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resources := &uiwidget.ScoreDisplayResources{}
 | 
				
			||||||
 | 
						for k, v := range e.ResourceSet {
 | 
				
			||||||
 | 
							switch k {
 | 
				
			||||||
 | 
							case "Empty":
 | 
				
			||||||
 | 
								resources.Empty, err = AssetToResource(s, v)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								var digit int
 | 
				
			||||||
 | 
								digit, err = strconv.Atoi(k)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Printf("failed to parse score digit: %v", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if digit > 9 {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								resources.Digits[digit], err = AssetToResource(s, v)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var relSize, fixedSize *fyne.Size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if e.RelativeSize != nil {
 | 
				
			||||||
 | 
							relSize = &fyne.Size{
 | 
				
			||||||
 | 
								Width:  e.RelativeSize.Width,
 | 
				
			||||||
 | 
								Height: e.RelativeSize.Height,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if e.ElementSize != nil {
 | 
				
			||||||
 | 
							fixedSize = &fyne.Size{
 | 
				
			||||||
 | 
								Width:  e.ElementSize.Width,
 | 
				
			||||||
 | 
								Height: e.ElementSize.Height,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if e.Binding != "" {
 | 
				
			||||||
 | 
							sd = uiwidget.NewScoreDisplayWithData(
 | 
				
			||||||
 | 
								s.GetBinding(e.Binding).(binding.Int),
 | 
				
			||||||
 | 
								e.NumDigits, resources, relSize, fixedSize)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							sd = uiwidget.NewScoreDisplay(
 | 
				
			||||||
 | 
								e.Score, e.NumDigits, resources,
 | 
				
			||||||
 | 
								relSize, fixedSize)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c = sd
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (w Widget) buildSelectWidget(
 | 
					func (w Widget) buildSelectWidget(
 | 
				
			||||||
	e *Element,
 | 
						e *Element,
 | 
				
			||||||
	s ScreenHandler,
 | 
						s ScreenHandler,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ package ui
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/fs"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,6 +27,7 @@ type ScreenHandler struct {
 | 
				
			|||||||
	screenDefinition *screen.Screen
 | 
						screenDefinition *screen.Screen
 | 
				
			||||||
	localizer        *i18n.Localizer
 | 
						localizer        *i18n.Localizer
 | 
				
			||||||
	viewModel        BaseViewModel
 | 
						viewModel        BaseViewModel
 | 
				
			||||||
 | 
						assetLoader      func(string) (fs.File, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Map of screen elements that got an id assigned
 | 
						// Map of screen elements that got an id assigned
 | 
				
			||||||
	exportedElements map[string]*screen.UIElement
 | 
						exportedElements map[string]*screen.UIElement
 | 
				
			||||||
@@ -60,6 +62,7 @@ func NewScreenHandler(
 | 
				
			|||||||
	screenName string,
 | 
						screenName string,
 | 
				
			||||||
	viewModel BaseViewModel,
 | 
						viewModel BaseViewModel,
 | 
				
			||||||
	localizer *i18n.Localizer,
 | 
						localizer *i18n.Localizer,
 | 
				
			||||||
 | 
						assetLoader func(string) (fs.File, error),
 | 
				
			||||||
) (h *ScreenHandler, err error) {
 | 
					) (h *ScreenHandler, err error) {
 | 
				
			||||||
	h = new(ScreenHandler)
 | 
						h = new(ScreenHandler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -72,6 +75,7 @@ func NewScreenHandler(
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	h.screenDefinition = def
 | 
						h.screenDefinition = def
 | 
				
			||||||
 | 
						h.assetLoader = assetLoader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	h.exportedElements = make(map[string]*screen.UIElement)
 | 
						h.exportedElements = make(map[string]*screen.UIElement)
 | 
				
			||||||
	h.listItemTemplates = make(map[string]screen.ListItemTemplateFn)
 | 
						h.listItemTemplates = make(map[string]screen.ListItemTemplateFn)
 | 
				
			||||||
@@ -105,6 +109,11 @@ func (h *ScreenHandler) RegisterElement(
 | 
				
			|||||||
	h.exportedElements[id] = &screen.UIElement{Object: elem, Decorator: decorator}
 | 
						h.exportedElements[id] = &screen.UIElement{Object: elem, Decorator: decorator}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *ScreenHandler) LoadAsset(name string) (fh fs.File, err error) {
 | 
				
			||||||
 | 
						fh, err = h.assetLoader(name)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RegisterListItemTemplate registers a ListItemTemplate with the ScreenHandler.
 | 
					// RegisterListItemTemplate registers a ListItemTemplate with the ScreenHandler.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Use this function to register the templates for the lists used in the
 | 
					// Use this function to register the templates for the lists used in the
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										233
									
								
								uiwidget/scoredisplay.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								uiwidget/scoredisplay.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,233 @@
 | 
				
			|||||||
 | 
					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) 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user