From cb9515375a8ac20ff39796dca6b86e5eeee15bd2 Mon Sep 17 00:00:00 2001 From: Maarten Heremans Date: Fri, 19 Apr 2024 16:44:36 +0200 Subject: [PATCH] Added score display --- baseview.go | 9 +- dialog.go | 34 +++++- screen/assets.go | 46 ++++++++ screen/canvas.go | 197 +++++++++++++++++++++++++++++++++ screen/element.go | 42 +++++++ screen/screen.go | 7 ++ screen/widget.go | 67 +++++++++++ screenhandler.go | 9 ++ uiwidget/scoredisplay.go | 233 +++++++++++++++++++++++++++++++++++++++ view.go | 2 + 10 files changed, 641 insertions(+), 5 deletions(-) create mode 100644 screen/assets.go create mode 100644 screen/canvas.go create mode 100644 uiwidget/scoredisplay.go diff --git a/baseview.go b/baseview.go index 72d567d..0fbe6ca 100644 --- a/baseview.go +++ b/baseview.go @@ -2,6 +2,7 @@ package ui import ( "embed" + "io/fs" "fyne.io/fyne/v2" "fyne.io/fyne/v2/data/binding" @@ -57,6 +58,7 @@ func NewBaseViewWithScreen( filesystem embed.FS, screenName string, localizer *i18n.Localizer, + assetLoader func(string) (fs.File, error), ) *BaseView { v := NewBaseView(concreteView, viewModel) @@ -65,7 +67,8 @@ func NewBaseViewWithScreen( filesystem, screenName, viewModel, - localizer) + localizer, + assetLoader) if err != nil { LogWindowError(v, LoadScreen, err) return nil @@ -130,3 +133,7 @@ func (v *BaseView) OnHide() { func (v *BaseView) GetBinding(bindingName string) binding.DataItem { return v.ViewModel.GetBinding(bindingName) } + +func (v *BaseView) GetCanvasObject() fyne.CanvasObject { + return v.CanvasObject +} diff --git a/dialog.go b/dialog.go index 66c06b1..7d955d2 100644 --- a/dialog.go +++ b/dialog.go @@ -3,6 +3,7 @@ package ui import ( "embed" "fmt" + "io/fs" "fyne.io/fyne/v2" "fyne.io/fyne/v2/data/binding" @@ -91,6 +92,7 @@ func NewBaseDialogWithScreen( filesystem embed.FS, screenName string, localizer *i18n.Localizer, + assetLoader func(string) (fs.File, error), ) *BaseDialog { d := NewBaseDialog( concreteDialog, name, kind, viewModel, @@ -98,7 +100,7 @@ func NewBaseDialogWithScreen( var err error d.ScreenHandler, err = NewScreenHandler( - filesystem, screenName, viewModel, localizer) + filesystem, screenName, viewModel, localizer, assetLoader) if err != nil { LogWindowError(d, LoadScreen, err) return nil @@ -138,6 +140,7 @@ func NewBaseDialogWithDataAndScreen( filesystem embed.FS, screenName string, localizer *i18n.Localizer, + assetLoader func(string) (fs.File, error), ) *BaseDialog { d := NewBaseDialogWithData( concreteDialog, name, kind, viewModel, @@ -145,7 +148,7 @@ func NewBaseDialogWithDataAndScreen( var err error d.ScreenHandler, err = NewScreenHandler( - filesystem, screenName, viewModel, localizer) + filesystem, screenName, viewModel, localizer, assetLoader) if err != nil { LogWindowError(d, LoadScreen, err) return nil @@ -248,6 +251,10 @@ func (d *BaseDialog) OnClose(confirmed bool) { d.OnHide() } +func (d *BaseDialog) GetCanvasObject() fyne.CanvasObject { + return d.CanvasObj +} + type FormDialogItemsFn func() []*widget.FormItem type FormDialog struct { @@ -288,11 +295,12 @@ func NewFormDialogWithScreen( filesystem embed.FS, screenName string, localizer *i18n.Localizer, + assetLoader func(string) (fs.File, error), ) *FormDialog { return &FormDialog{ BaseDialog: NewBaseDialogWithScreen( concreteDialog, name, kind, viewModel, parent, confirm, - filesystem, screenName, localizer), + filesystem, screenName, localizer, assetLoader), confirmLabel: "OK", dismissLabel: "Cancel", } @@ -328,11 +336,12 @@ func NewFormDialogWithDataAndScreen( filesystem embed.FS, screenName string, localizer *i18n.Localizer, + assetLoader func(string) (fs.File, error), ) *FormDialog { return &FormDialog{ BaseDialog: NewBaseDialogWithDataAndScreen( concreteDialog, name, kind, viewModel, parent, confirm, data, - filesystem, screenName, localizer), + filesystem, screenName, localizer, assetLoader), confirmLabel: "OK", dismissLabel: "Cancel", } @@ -485,3 +494,20 @@ func NewDialogWithDataAndScreen( } 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() +} diff --git a/screen/assets.go b/screen/assets.go new file mode 100644 index 0000000..84c5721 --- /dev/null +++ b/screen/assets.go @@ -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 +} diff --git a/screen/canvas.go b/screen/canvas.go new file mode 100644 index 0000000..b3d8314 --- /dev/null +++ b/screen/canvas.go @@ -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 +} diff --git a/screen/element.go b/screen/element.go index 8d98e9e..a72c2f6 100644 --- a/screen/element.go +++ b/screen/element.go @@ -13,6 +13,18 @@ type Size struct { 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 type Element struct { ID string `yaml:"id"` @@ -20,6 +32,7 @@ type Element struct { Container Container `yaml:"container"` Layout Layout `yaml:"layout"` Widget Widget `yaml:"widget"` + Canvas Canvas `yaml:"canvas"` // Border Layout Elements Top *Element `yaml:"top"` @@ -45,6 +58,11 @@ type Element struct { // MinSize configuration MinSize *Size `yaml:"minSize"` + // Relative Size configuration + // Only valid for certain widgets: + // - ScoreDisplay + RelativeSize *RelativeSize `yaml:"relativeSize"` + // Widget Properties Text string `yaml:"text"` Localized bool `yaml:"localized"` @@ -75,6 +93,10 @@ type Element struct { // Check Properties Checked bool `yaml:"checked"` + // ScoreDisplay Properties + Score int `yaml:"score"` + NumDigits int `yaml:"numDigits"` + // Select Properties SelectOptionsBinding string `yaml:"selectOptionsBinding"` SelectOptions []string `yaml:"selectOptions"` @@ -83,6 +105,19 @@ type Element struct { FixHorizontal bool `yaml:"fixHorizontal"` 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 OnClicked string `yaml:"onClicked"` OnUpClicked string `yaml:"onUpClicked"` @@ -128,6 +163,13 @@ func (e *Element) BuildUI(s ScreenHandler) (obj fyne.CanvasObject, err error) { } 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 } diff --git a/screen/screen.go b/screen/screen.go index 86a4b40..7e36f7b 100644 --- a/screen/screen.go +++ b/screen/screen.go @@ -2,6 +2,7 @@ package screen import ( "embed" + "errors" "fmt" "io" "io/fs" @@ -35,6 +36,8 @@ type UIElement struct { type ScreenHandler interface { RegisterElement(string, fyne.CanvasObject, fyne.CanvasObject) + LoadAsset(string) (fs.File, error) + GetLocalizer() *i18n.Localizer GetIcon(string) fyne.Resource GetBinding(string) binding.DataItem @@ -133,6 +136,10 @@ func (d *TemplateScreenHandler) RegisterElement( 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 { elem, ok := d.elementsMap[name] if !ok { diff --git a/screen/widget.go b/screen/widget.go index e6a0bc5..f7b1a8f 100644 --- a/screen/widget.go +++ b/screen/widget.go @@ -3,6 +3,8 @@ package screen import ( "errors" "fmt" + "log" + "strconv" "time" "bitbucket.org/hevanto/ui/uiwidget" @@ -170,6 +172,9 @@ const ( RichText Widget = "RichText" + // ScoreDisplay Widget + ScoreDisplay Widget = "ScoreDisplay" + // Select Widget Select Widget = "Select" @@ -232,6 +237,8 @@ func (w Widget) Build( widget, err = w.buildListWidget(e, s) case RichText: widget, err = w.buildRichTextWidget(e, s) + case ScoreDisplay: + widget, err = w.buildScoreDisplayWidget(e, s) case Select: widget, err = w.buildSelectWidget(e, s) case SelectEntry: @@ -528,6 +535,66 @@ func (w Widget) buildRichTextWidget( 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( e *Element, s ScreenHandler, diff --git a/screenhandler.go b/screenhandler.go index 05492c7..ee75ee0 100644 --- a/screenhandler.go +++ b/screenhandler.go @@ -3,6 +3,7 @@ package ui import ( "embed" "fmt" + "io/fs" "log" "strings" @@ -26,6 +27,7 @@ type ScreenHandler struct { screenDefinition *screen.Screen localizer *i18n.Localizer viewModel BaseViewModel + assetLoader func(string) (fs.File, error) // Map of screen elements that got an id assigned exportedElements map[string]*screen.UIElement @@ -60,6 +62,7 @@ func NewScreenHandler( screenName string, viewModel BaseViewModel, localizer *i18n.Localizer, + assetLoader func(string) (fs.File, error), ) (h *ScreenHandler, err error) { h = new(ScreenHandler) @@ -72,6 +75,7 @@ func NewScreenHandler( return } h.screenDefinition = def + h.assetLoader = assetLoader h.exportedElements = make(map[string]*screen.UIElement) h.listItemTemplates = make(map[string]screen.ListItemTemplateFn) @@ -105,6 +109,11 @@ func (h *ScreenHandler) RegisterElement( 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. // // Use this function to register the templates for the lists used in the diff --git a/uiwidget/scoredisplay.go b/uiwidget/scoredisplay.go new file mode 100644 index 0000000..bc70307 --- /dev/null +++ b/uiwidget/scoredisplay.go @@ -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 +} diff --git a/view.go b/view.go index df86787..9c32678 100644 --- a/view.go +++ b/view.go @@ -52,4 +52,6 @@ type View interface { // OnHide is called when the view is removed from the screen OnHide() + + GetCanvasObject() fyne.CanvasObject }