From b9d2b680bcb9c121fdf058730f53f8e225acf955 Mon Sep 17 00:00:00 2001 From: Maarten Heremans Date: Sat, 30 Mar 2024 19:26:48 +0100 Subject: [PATCH] Added scroll container and minsize layout --- screen/container.go | 93 +++++++++++++++++++++++++++++++++++++++++++++ screen/element.go | 16 +++++++- screen/layout.go | 51 +++++++++++++++++++++---- uilayout/minsize.go | 33 ++++++++++++++++ 4 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 screen/container.go create mode 100644 uilayout/minsize.go diff --git a/screen/container.go b/screen/container.go new file mode 100644 index 0000000..6ea26b6 --- /dev/null +++ b/screen/container.go @@ -0,0 +1,93 @@ +package screen + +import ( + "errors" + "fmt" + + "bitbucket.org/hevanto/ui/uiwidget" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" +) + +type Container string + +const ( + HScroll Container = "HScroll" + Scroll Container = "Scroll" + VScroll Container = "VScroll" +) + +func (cnt Container) Build(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { + switch cnt { + case HScroll: + c, err = cnt.buildHScrollContainer(e, s) + case Scroll: + c, err = cnt.buildScrollContainer(e, s) + case VScroll: + c, err = cnt.buildVScrollContainer(e, s) + default: + err = errors.New("invalid container") + } + if err != nil { + return + } + if e.Decorators != nil { + + for _, dec := range e.Decorators { + switch dec { + case "Border": + c = uiwidget.NewWidgetBorder(c) + } + } + } + + if e.Hidden { + c.Hide() + } + return +} + +func (ctn Container) buildHScrollContainer(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { + if e.Content == nil { + err = errors.New("HScroll: no content defined") + return + } + + content, err := e.Content.BuildUI(s) + if err != nil { + err = fmt.Errorf("HScroll: failed to build content: %w", err) + return + } + c = container.NewHScroll(content) + return +} + +func (ctn Container) buildScrollContainer(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { + if e.Content == nil { + err = errors.New("Scroll: no content defined") + return + } + + content, err := e.Content.BuildUI(s) + if err != nil { + err = fmt.Errorf("Scroll: failed to build content: %w", err) + return + } + c = container.NewScroll(content) + return +} + +func (ctn Container) buildVScrollContainer(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { + if e.Content == nil { + err = errors.New("VScroll: no content defined") + return + } + + content, err := e.Content.BuildUI(s) + if err != nil { + err = fmt.Errorf("VScroll: failed to build content: %w", err) + return + } + c = container.NewScroll(content) + return +} diff --git a/screen/element.go b/screen/element.go index 60bbffc..c0bb57b 100644 --- a/screen/element.go +++ b/screen/element.go @@ -15,8 +15,9 @@ type Size struct { type Element struct { ID string `yaml:"id"` - Layout Layout `yaml:"layout"` - Widget Widget `yaml:"widget"` + Container Container `yaml:"container"` + Layout Layout `yaml:"layout"` + Widget Widget `yaml:"widget"` // Border Layout Elements Top *Element `yaml:"top"` @@ -27,6 +28,7 @@ type Element struct { // Other Layout Elements Children []*Element `yaml:"children"` + Content *Element `yaml:"content"` // Form Element Label *Element `yaml:"label"` @@ -38,6 +40,9 @@ type Element struct { RowColumns *int `yaml:"rowColumns"` ElementSize *Size `yaml:"elementSize"` + // MinSize configuration + MinSize *Size `yaml:"minSize"` + // Widget Properties Text string `yaml:"text"` Localized bool `yaml:"localized"` @@ -81,6 +86,13 @@ func (e *Element) BuildUI(s ScreenHandler) (obj fyne.CanvasObject, err error) { } }() + if e.Container != "" { + if obj, err = e.Container.Build(e, s); err != nil { + err = fmt.Errorf("failed to build container element: %w", err) + return + } + return + } if e.Layout != "" { if obj, err = e.Layout.Build(e, s); err != nil { err = fmt.Errorf("failed to build layout element: %w", err) diff --git a/screen/layout.go b/screen/layout.go index 5037f89..16b7aec 100644 --- a/screen/layout.go +++ b/screen/layout.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" + "bitbucket.org/hevanto/ui/uilayout" + "bitbucket.org/hevanto/ui/uiwidget" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/layout" @@ -12,15 +14,16 @@ import ( type Layout string const ( - Border Layout = "Border" - Form Layout = "Form" - Grid Layout = "Grid" - HBox Layout = "HBox" - Stack Layout = "Stack" - VBox Layout = "VBox" + Border Layout = "Border" + Form Layout = "Form" + Grid Layout = "Grid" + HBox Layout = "HBox" + MinSize Layout = "MinSize" + Stack Layout = "Stack" + VBox Layout = "VBox" ) -func (l Layout) Build(e *Element, s ScreenHandler) (c *fyne.Container, err error) { +func (l Layout) Build(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { switch l { case Border: c, err = l.buildBorderLayout(e, s) @@ -30,6 +33,8 @@ func (l Layout) Build(e *Element, s ScreenHandler) (c *fyne.Container, err error c, err = l.buildGridLayout(e, s) case HBox: c, err = l.buildHBoxLayout(e, s) + case MinSize: + c, err = l.buildMinSizeLayout(e, s) case Stack: c, err = l.buildStackLayout(e, s) case VBox: @@ -41,6 +46,15 @@ func (l Layout) Build(e *Element, s ScreenHandler) (c *fyne.Container, err error return } + if e.Decorators != nil { + for _, dec := range e.Decorators { + switch dec { + case "Border": + c = uiwidget.NewWidgetBorder(c) + } + } + } + if e.Hidden { c.Hide() } @@ -179,6 +193,29 @@ func (l Layout) buildHBoxLayout(e *Element, s ScreenHandler) (c *fyne.Container, return } +func (l Layout) buildMinSizeLayout(e *Element, s ScreenHandler) (c *fyne.Container, err error) { + children := make([]fyne.CanvasObject, 0, len(e.Children)) + for _, child := range e.Children { + var obj fyne.CanvasObject + if obj, err = child.BuildUI(s); err != nil { + err = fmt.Errorf("MinSizeLayout failed to create child element: %w", err) + return + } + children = append(children, obj) + } + if e.MinSize == nil { + e.MinSize = &Size{ + Width: 0, + Height: 0, + } + } + c = container.New( + uilayout.NewMinSizeLayout( + fyne.NewSize(e.MinSize.Width, e.MinSize.Height)), + children...) + return +} + func (l Layout) buildStackLayout(e *Element, s ScreenHandler) (c *fyne.Container, err error) { children := make([]fyne.CanvasObject, 0, len(e.Children)) for _, child := range e.Children { diff --git a/uilayout/minsize.go b/uilayout/minsize.go new file mode 100644 index 0000000..e570d76 --- /dev/null +++ b/uilayout/minsize.go @@ -0,0 +1,33 @@ +package uilayout + +import "fyne.io/fyne/v2" + +type MinSize struct { + minSize fyne.Size +} + +func NewMinSizeLayout(minSize fyne.Size) *MinSize { + return &MinSize{minSize: minSize} +} + +func (l *MinSize) MinSize(objects []fyne.CanvasObject) fyne.Size { + size := l.minSize + for _, o := range objects { + childSize := o.MinSize() + if size.Width < childSize.Width { + size.Width = childSize.Width + } + if size.Height < childSize.Height { + size.Height = childSize.Height + } + } + return size +} + +func (l *MinSize) Layout(objects []fyne.CanvasObject, containerSize fyne.Size) { + pos := fyne.NewPos(0, 0) + for _, o := range objects { + o.Resize(containerSize) + o.Move(pos) + } +}