ui/screen/screen.go
2024-08-06 21:30:11 +02:00

289 lines
6.8 KiB
Go

package screen
import (
"embed"
"errors"
"fmt"
"io"
"io/fs"
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n"
"gopkg.in/yaml.v2"
)
type ListItemTemplateFn func() fyne.CanvasObject
type ListDataItemRendererFn func(binding.DataItem, fyne.CanvasObject)
type ListItemRendererFn func(int, fyne.CanvasObject)
type ListLengthFn func() int
type ListItemHandlerFn func(widget.ListItemID)
type ClickHandlerFn func()
type CheckHandlerFn func(bool)
type DateSelectedHandlerFn func(time.Time)
type OptionSelectedHandlerFn func(string)
type ValidationChangedHandlerFn func(error)
type UIElement struct {
Object fyne.CanvasObject
Decorator fyne.CanvasObject
}
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
GetValidator(string) fyne.StringValidator
GetListItemTemplate(string) ListItemTemplateFn
GetListDataItemRenderer(string) ListDataItemRendererFn
GetListItemRenderer(string) ListItemRendererFn
GetListLength(string) ListLengthFn
GetOnListItemSelectedHandler(string) ListItemHandlerFn
GetOnListItemUnselectedHandler(string) ListItemHandlerFn
GetOnClickedHandler(string) ClickHandlerFn
GetOnCheckChangedHandler(string) CheckHandlerFn
GetOnDateSelectedHandler(string) DateSelectedHandlerFn
GetOnOptionSelectedHandler(string) OptionSelectedHandlerFn
GetOnValidationChangedHandler(string) ValidationChangedHandlerFn
}
type Screen struct {
fyne.CanvasObject
rootElement *Element
screenHandler ScreenHandler
resizeCallbacks []func(fyne.Size)
moveCallbacks []func(fyne.Position)
}
func New(
filesystem embed.FS,
name string,
screenHandler ScreenHandler,
) (scr *Screen, err error) {
var bytes []byte
var fh fs.File
if fh, err = filesystem.Open(name); err != nil {
err = fmt.Errorf("no screen definition found: %w", err)
return
}
if bytes, err = io.ReadAll(fh); err != nil {
err = fmt.Errorf("unable to read screen definition: %w", err)
return
}
root := &Element{}
if err = yaml.Unmarshal(bytes, root); err != nil {
err = fmt.Errorf("failure parsing screen definition: %w", err)
return
}
scr = &Screen{
rootElement: root,
screenHandler: screenHandler,
}
return
}
func NewTemplate(
filesystem embed.FS,
name string,
localizer *i18n.Localizer,
) (
scr *Screen,
handler *TemplateScreenHandler,
err error,
) {
handler = NewTemplateScreenHandler(localizer)
scr, err = New(filesystem, name, handler)
return
}
func (s *Screen) Initialize() (obj fyne.CanvasObject, err error) {
if s.CanvasObject, err = s.rootElement.BuildUI(s.screenHandler); err != nil {
err = fmt.Errorf("failed to build screen: %w", err)
return
}
obj = s
return
}
func (s *Screen) Move(p fyne.Position) {
s.CanvasObject.Move(p)
for _, cb := range s.moveCallbacks {
cb(p)
}
}
func (s *Screen) Resize(sz fyne.Size) {
s.CanvasObject.Resize(sz)
for _, cb := range s.resizeCallbacks {
cb(sz)
}
}
func (s *Screen) MinSize() fyne.Size {
return s.CanvasObject.MinSize()
}
func (s *Screen) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(s.AsContainer())
}
func (s *Screen) RegisterResizeCallback(cb func(fyne.Size)) {
s.resizeCallbacks = append(s.resizeCallbacks, cb)
}
func (s *Screen) RegisterMoveCallback(cb func(fyne.Position)) {
s.moveCallbacks = append(s.moveCallbacks, cb)
}
func (s *Screen) AsContainer() *fyne.Container {
return s.CanvasObject.(*fyne.Container)
}
type DialogScreen struct {
*Screen
}
func NewDialogScreen(
filesystem embed.FS,
name string,
screenHandler ScreenHandler,
) (src *DialogScreen, err error) {
screen, err := New(filesystem, name, screenHandler)
if err != nil {
return
}
src = &DialogScreen{screen}
return
}
func (s *DialogScreen) Initialize() (obj fyne.CanvasObject, err error) {
if s.CanvasObject, err = s.rootElement.BuildUI(s.screenHandler); err != nil {
err = fmt.Errorf("failed to build screen: %w", err)
return
}
obj = s
return
}
func (s *DialogScreen) Move(p fyne.Position) {
c := fyne.CurrentApp().Driver().CanvasForObject(s.CanvasObject)
movePos := fyne.Position{
X: p.X / c.Scale(),
Y: p.Y / c.Scale(),
}
s.CanvasObject.Move(movePos)
//fmt.Printf("%+v\n", s.CanvasObject.Position());
}
type TemplateScreenHandler struct {
localizer *i18n.Localizer
elementsMap map[string]*UIElement
}
func NewTemplateScreenHandler(
localizer *i18n.Localizer,
) *TemplateScreenHandler {
return &TemplateScreenHandler{
localizer: localizer,
elementsMap: make(map[string]*UIElement),
}
}
func (d *TemplateScreenHandler) RegisterElement(
name string,
element fyne.CanvasObject,
decorator fyne.CanvasObject,
) {
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 {
return nil
}
return elem
}
func (d *TemplateScreenHandler) GetLocalizer() *i18n.Localizer {
return d.localizer
}
func (d *TemplateScreenHandler) GetIcon(iconName string) fyne.Resource {
if strings.HasPrefix(iconName, "theme.") {
iconName := strings.SplitN(iconName, ".", 2)[1]
return theme.DefaultTheme().Icon(fyne.ThemeIconName(iconName))
}
return nil
}
func (*TemplateScreenHandler) GetBinding(string) binding.DataItem {
return nil
}
func (*TemplateScreenHandler) GetValidator(string) fyne.StringValidator {
return nil
}
func (*TemplateScreenHandler) GetListItemTemplate(string) ListItemTemplateFn {
return func() fyne.CanvasObject { return nil }
}
func (*TemplateScreenHandler) GetListDataItemRenderer(string) ListDataItemRendererFn {
return func(binding.DataItem, fyne.CanvasObject) {}
}
func (*TemplateScreenHandler) GetListItemRenderer(string) ListItemRendererFn {
return func(int, fyne.CanvasObject) {}
}
func (*TemplateScreenHandler) GetListLength(string) ListLengthFn {
return func() int { return 0 }
}
func (*TemplateScreenHandler) GetOnListItemSelectedHandler(string) ListItemHandlerFn {
return func(widget.ListItemID) {}
}
func (*TemplateScreenHandler) GetOnListItemUnselectedHandler(string) ListItemHandlerFn {
return func(widget.ListItemID) {}
}
func (*TemplateScreenHandler) GetOnClickedHandler(string) ClickHandlerFn {
return func() {}
}
func (*TemplateScreenHandler) GetOnCheckChangedHandler(string) CheckHandlerFn {
return func(bool) {}
}
func (*TemplateScreenHandler) GetOnDateSelectedHandler(string) DateSelectedHandlerFn {
return func(time.Time) {}
}
func (*TemplateScreenHandler) GetOnOptionSelectedHandler(string) OptionSelectedHandlerFn {
return func(string) {}
}
func (*TemplateScreenHandler) GetOnValidationChangedHandler(string) ValidationChangedHandlerFn {
return func(error) {}
}