2024-03-30 17:45:07 +01:00
|
|
|
package screen
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
2024-03-30 19:26:48 +01:00
|
|
|
"bitbucket.org/hevanto/ui/uilayout"
|
2024-03-30 17:45:07 +01:00
|
|
|
"fyne.io/fyne/v2"
|
|
|
|
"fyne.io/fyne/v2/container"
|
|
|
|
"fyne.io/fyne/v2/layout"
|
|
|
|
)
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
// The constants for the Layouts
|
|
|
|
//
|
|
|
|
// Available yaml options for all layouts:
|
|
|
|
// - id: The ID for exporting the layout to code
|
|
|
|
// - decorators: The decorators to wrap the layout into
|
|
|
|
// - hidden: Wether the layout is hidden or not
|
2024-03-30 17:45:07 +01:00
|
|
|
type Layout string
|
|
|
|
|
|
|
|
const (
|
2024-04-03 16:07:24 +02:00
|
|
|
// Border Layout
|
|
|
|
//
|
|
|
|
// Available yaml options
|
|
|
|
// - top: The top pane (contains 1 widget)
|
|
|
|
// - bottom: The bottom pane (contains 1 widget)
|
|
|
|
// - left: The left pane (contains 1 widget)
|
|
|
|
// - right: The right pane (contains 1 widget)
|
|
|
|
// - center: The center pane (contains multiple widgets)
|
|
|
|
Border Layout = "Border"
|
|
|
|
|
|
|
|
// Form Layout
|
|
|
|
//
|
|
|
|
// Available yaml options
|
|
|
|
// - children: The children of the form
|
|
|
|
// - label: The label of the form element
|
|
|
|
// - field: The field of the form element
|
|
|
|
Form Layout = "Form"
|
|
|
|
|
|
|
|
// Grid Layout
|
|
|
|
//
|
|
|
|
// Available yaml options
|
|
|
|
// - columns: The number of columns (GridWithColumns)
|
|
|
|
// - rows: The number of rows (GridWithRows)
|
|
|
|
// - rowColumns: The number of rows and columns (AdaptiveGrid)
|
|
|
|
// - elementSize: The size of the elements (GridWrap)
|
|
|
|
// - children: The children of the grid
|
|
|
|
Grid Layout = "Grid"
|
|
|
|
|
|
|
|
// HBox Layout
|
|
|
|
//
|
|
|
|
// Available yaml options
|
|
|
|
// - children: The children of the HBox
|
|
|
|
HBox Layout = "HBox"
|
|
|
|
|
|
|
|
// MinSize Layout
|
|
|
|
//
|
|
|
|
// Available yaml options
|
|
|
|
// - minSize: The minimum size of the layout
|
|
|
|
// - children: The children of the MinSize
|
2024-03-30 19:26:48 +01:00
|
|
|
MinSize Layout = "MinSize"
|
2024-04-03 16:07:24 +02:00
|
|
|
|
|
|
|
// Stack Layout
|
|
|
|
//
|
|
|
|
// Available yaml options
|
|
|
|
// - children: The children of the Stack
|
|
|
|
Stack Layout = "Stack"
|
|
|
|
|
|
|
|
// VBox Layout
|
|
|
|
//
|
|
|
|
// Available yaml options
|
|
|
|
// - children: The children of the VBox
|
|
|
|
VBox Layout = "VBox"
|
2024-03-30 17:45:07 +01:00
|
|
|
)
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
// Build builds the layout based on the given Layout and Element
|
|
|
|
// using the provided ScreenHandler to fetch functions, data bindings, etc.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// - e: The Element to build the Layout for
|
|
|
|
// - s: The ScreenHandler to use to fetch functions, data bindings, etc.
|
|
|
|
// Returns the CanvasObject for the Layout, the CanvasObject for the decorator
|
|
|
|
// and an error if any.
|
|
|
|
// If no decorators are specified, the widget and decorator will be the same.
|
|
|
|
func (l Layout) Build(
|
|
|
|
e *Element,
|
|
|
|
s ScreenHandler,
|
|
|
|
) (
|
2024-04-11 17:19:57 +02:00
|
|
|
lay fyne.CanvasObject,
|
2024-04-03 16:07:24 +02:00
|
|
|
decorator fyne.CanvasObject,
|
|
|
|
err error,
|
|
|
|
) {
|
2024-03-30 17:45:07 +01:00
|
|
|
switch l {
|
|
|
|
case Border:
|
2024-04-11 17:19:57 +02:00
|
|
|
lay, err = l.buildBorderLayout(e, s)
|
2024-03-30 17:45:07 +01:00
|
|
|
case Form:
|
2024-04-11 17:19:57 +02:00
|
|
|
lay, err = l.buildFormLayout(e, s)
|
2024-03-30 17:45:07 +01:00
|
|
|
case Grid:
|
2024-04-11 17:19:57 +02:00
|
|
|
lay, err = l.buildGridLayout(e, s)
|
2024-03-30 17:45:07 +01:00
|
|
|
case HBox:
|
2024-04-11 17:19:57 +02:00
|
|
|
lay, err = l.buildHBoxLayout(e, s)
|
2024-03-30 19:26:48 +01:00
|
|
|
case MinSize:
|
2024-04-11 17:19:57 +02:00
|
|
|
lay, err = l.buildMinSizeLayout(e, s)
|
2024-03-30 17:45:07 +01:00
|
|
|
case Stack:
|
2024-04-11 17:19:57 +02:00
|
|
|
lay, err = l.buildStackLayout(e, s)
|
2024-03-30 17:45:07 +01:00
|
|
|
case VBox:
|
2024-04-11 17:19:57 +02:00
|
|
|
lay, err = l.buildVBoxLayout(e, s)
|
2024-03-30 17:45:07 +01:00
|
|
|
default:
|
|
|
|
err = errors.New("invalid layout")
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-23 09:35:21 +02:00
|
|
|
decorator = applyDecorators(e, lay)
|
2024-03-30 19:26:48 +01:00
|
|
|
|
2024-03-30 17:45:07 +01:00
|
|
|
if e.Hidden {
|
2024-04-03 16:07:24 +02:00
|
|
|
decorator.Hide()
|
2024-03-30 17:45:07 +01:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
func (l Layout) buildBorderLayout(
|
|
|
|
e *Element,
|
|
|
|
s ScreenHandler,
|
|
|
|
) (c *fyne.Container, err error) {
|
2024-03-30 17:45:07 +01:00
|
|
|
var top, left, right, bottom fyne.CanvasObject
|
|
|
|
var center []fyne.CanvasObject
|
|
|
|
|
|
|
|
if e.Top != nil {
|
|
|
|
if top, err = e.Top.BuildUI(s); err != nil {
|
|
|
|
err = fmt.Errorf("BorderLayout: failed to create top element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Left != nil {
|
|
|
|
if left, err = e.Left.BuildUI(s); err != nil {
|
|
|
|
err = fmt.Errorf("BorderLayout: failed to create left element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Center != nil {
|
|
|
|
center = make([]fyne.CanvasObject, 0, len(e.Center))
|
|
|
|
for _, elem := range e.Center {
|
|
|
|
var ce fyne.CanvasObject
|
|
|
|
if ce, err = elem.BuildUI(s); err != nil {
|
|
|
|
err = fmt.Errorf("BorderLayout: failed to create center element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
center = append(center, ce)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Right != nil {
|
|
|
|
if right, err = e.Right.BuildUI(s); err != nil {
|
|
|
|
err = fmt.Errorf("BorderLayout: failed to create right element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Bottom != nil {
|
|
|
|
if bottom, err = e.Bottom.BuildUI(s); err != nil {
|
|
|
|
err = fmt.Errorf("BorderLayout: failed to create bottom element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c = container.NewBorder(top, bottom, left, right, center...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
func (l Layout) buildFormLayout(
|
|
|
|
e *Element,
|
|
|
|
s ScreenHandler,
|
|
|
|
) (c *fyne.Container, err error) {
|
2024-03-30 17:45:07 +01:00
|
|
|
children := make([]fyne.CanvasObject, 0, len(e.Children)*2)
|
|
|
|
for _, child := range e.Children {
|
|
|
|
if child.Label == nil {
|
|
|
|
child.Label = &Element{
|
|
|
|
Text: "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if child.Field == nil {
|
|
|
|
child.Field = &Element{
|
|
|
|
Widget: Label,
|
|
|
|
Text: "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
child.Label.Widget = Label
|
|
|
|
if child.Label.Options == nil {
|
|
|
|
child.Label.Options = make(map[string]any)
|
|
|
|
}
|
|
|
|
if child.Label.Options["TextStyle"] == nil {
|
|
|
|
child.Label.Options["TextStyle"] = make(map[string]any)
|
|
|
|
}
|
|
|
|
child.Label.Options["TextStyle"].(map[string]any)["Bold"] = true
|
|
|
|
|
|
|
|
var label, field fyne.CanvasObject
|
|
|
|
if label, err = child.Label.BuildUI(s); err != nil {
|
|
|
|
err = fmt.Errorf("FormLayout: failed to create label element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if field, err = child.Field.BuildUI(s); err != nil {
|
|
|
|
err = fmt.Errorf("FormLayout: failed to create field element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
children = append(children, label, field)
|
|
|
|
}
|
2024-04-11 14:52:32 +02:00
|
|
|
|
2024-03-30 17:45:07 +01:00
|
|
|
c = container.New(layout.NewFormLayout(), children...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
func (l Layout) buildGridLayout(
|
|
|
|
e *Element,
|
|
|
|
s ScreenHandler,
|
|
|
|
) (c *fyne.Container, err error) {
|
2024-03-30 17:45:07 +01:00
|
|
|
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("GridLayout failed to create child element %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
children = append(children, obj)
|
|
|
|
}
|
|
|
|
if e.Columns != nil {
|
|
|
|
c = container.NewGridWithColumns(*e.Columns, children...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if e.Rows != nil {
|
|
|
|
c = container.NewGridWithRows(*e.Rows, children...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if e.RowColumns != nil {
|
|
|
|
c = container.NewAdaptiveGrid(*e.RowColumns, children...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if e.ElementSize != nil {
|
|
|
|
c = container.NewGridWrap(fyne.NewSize(e.ElementSize.Width, e.ElementSize.Height), children...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = errors.New("GridLayout failed due to incomplete grid type definition")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
func (l Layout) buildHBoxLayout(
|
|
|
|
e *Element,
|
|
|
|
s ScreenHandler,
|
|
|
|
) (c *fyne.Container, err error) {
|
2024-03-30 17:45:07 +01:00
|
|
|
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("HBoxLayout failed to create child element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
children = append(children, obj)
|
|
|
|
}
|
|
|
|
c = container.New(layout.NewHBoxLayout(), children...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
func (l Layout) buildMinSizeLayout(
|
|
|
|
e *Element,
|
|
|
|
s ScreenHandler,
|
|
|
|
) (c *fyne.Container, err error) {
|
2024-03-30 19:26:48 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
func (l Layout) buildStackLayout(
|
|
|
|
e *Element,
|
|
|
|
s ScreenHandler,
|
|
|
|
) (c *fyne.Container, err error) {
|
2024-03-30 17:45:07 +01:00
|
|
|
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("StackLayout failed to create child element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
children = append(children, obj)
|
|
|
|
}
|
|
|
|
c = container.New(layout.NewStackLayout(), children...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-03 16:07:24 +02:00
|
|
|
func (l Layout) buildVBoxLayout(
|
|
|
|
e *Element,
|
|
|
|
s ScreenHandler,
|
|
|
|
) (c *fyne.Container, err error) {
|
2024-03-30 17:45:07 +01:00
|
|
|
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("VBoxLayout failed to create child element: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
children = append(children, obj)
|
|
|
|
}
|
|
|
|
c = container.New(layout.NewVBoxLayout(), children...)
|
|
|
|
return
|
|
|
|
}
|