ui/screenhandler.go

441 lines
13 KiB
Go
Raw Normal View History

2024-04-03 16:07:24 +02:00
package ui
import (
"embed"
"fmt"
2024-04-19 16:44:36 +02:00
"io/fs"
"log"
2024-04-03 16:07:24 +02:00
"strings"
"bitbucket.org/hevanto/ui/screen"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/theme"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
// ScreenHandler represents the handler for a screen.
//
// It provides:
// - The screen definition
// - The localizer
// - The screen controller
// - Exported elements
// - List item templates and renderers
// - User interaction handlers
type ScreenHandler struct {
screenDefinition *screen.Screen
localizer *i18n.Localizer
viewModel BaseViewModel
2024-04-19 16:44:36 +02:00
assetLoader func(string) (fs.File, error)
2024-04-03 16:07:24 +02:00
// Map of screen elements that got an id assigned
exportedElements map[string]*screen.UIElement
listItemTemplates map[string]screen.ListItemTemplateFn
listItemRenderers map[string]screen.ListItemRendererFn
listDataItemRenderers map[string]screen.ListDataItemRendererFn
listLengths map[string]screen.ListLengthFn
onListItemSelectedHandlers map[string]screen.ListItemHandlerFn
onListItemUnselectedHandlers map[string]screen.ListItemHandlerFn
onClickedHandlers map[string]screen.ClickHandlerFn
onCheckChangedHandlers map[string]screen.CheckHandlerFn
onDateSelectedHandlers map[string]screen.DateSelectedHandlerFn
2024-04-11 14:52:32 +02:00
onOptionSelectedHandlers map[string]screen.OptionSelectedHandlerFn
2024-04-03 16:07:24 +02:00
onValidationChangedHandlers map[string]screen.ValidationChangedHandlerFn
}
// NewScreenHandler initializes a new ScreenHandler with the provided filesystem, screen name, ScreenController, and localizer.
//
// Arguments:
// - filesystem: the embedded filesystem to access screen resources.
// - screenName: the name of the screen to load.
// - ctrl: the ScreenController for the screen.
// - localizer: the i18n.Localizer for localization.
//
// Returns:
// - h: the initialized ScreenHandler.
// - err: an error if loading the screen fails.
func NewScreenHandler(
filesystem embed.FS,
screenName string,
viewModel BaseViewModel,
2024-04-03 16:07:24 +02:00
localizer *i18n.Localizer,
2024-04-19 16:44:36 +02:00
assetLoader func(string) (fs.File, error),
2024-04-03 16:07:24 +02:00
) (h *ScreenHandler, err error) {
h = new(ScreenHandler)
h.viewModel = viewModel
2024-04-03 16:07:24 +02:00
h.localizer = localizer
def, err := screen.New(filesystem, screenName, h)
if err != nil {
err = fmt.Errorf("failure loading screen: %w", err)
return
}
h.screenDefinition = def
2024-04-19 16:44:36 +02:00
h.assetLoader = assetLoader
2024-04-03 16:07:24 +02:00
h.exportedElements = make(map[string]*screen.UIElement)
h.listItemTemplates = make(map[string]screen.ListItemTemplateFn)
h.listItemRenderers = make(map[string]screen.ListItemRendererFn)
h.listDataItemRenderers = make(map[string]screen.ListDataItemRendererFn)
h.listLengths = make(map[string]screen.ListLengthFn)
h.onListItemSelectedHandlers = make(map[string]screen.ListItemHandlerFn)
h.onListItemUnselectedHandlers = make(map[string]screen.ListItemHandlerFn)
h.onClickedHandlers = make(map[string]screen.ClickHandlerFn)
h.onCheckChangedHandlers = make(map[string]screen.CheckHandlerFn)
h.onDateSelectedHandlers = make(map[string]screen.DateSelectedHandlerFn)
2024-04-11 14:52:32 +02:00
h.onOptionSelectedHandlers = make(map[string]screen.OptionSelectedHandlerFn)
2024-04-03 16:07:24 +02:00
h.onValidationChangedHandlers = make(map[string]screen.ValidationChangedHandlerFn)
return
}
// ScreenDefinition returns the screen definition for the ScreenHandler.
func (h *ScreenHandler) ScreenDefinition() *screen.Screen {
return h.screenDefinition
}
// RegisterElement registers an element with the ScreenHandler.
//
// This function is called for every element in the screen definition fow which
// an ID is defined.
func (h *ScreenHandler) RegisterElement(
id string,
elem fyne.CanvasObject,
decorator fyne.CanvasObject,
) {
h.exportedElements[id] = &screen.UIElement{Object: elem, Decorator: decorator}
}
2024-04-19 16:44:36 +02:00
func (h *ScreenHandler) LoadAsset(name string) (fh fs.File, err error) {
fh, err = h.assetLoader(name)
return
}
2024-04-03 16:07:24 +02:00
// RegisterListItemTemplate registers a ListItemTemplate with the ScreenHandler.
//
// Use this function to register the templates for the lists used in the
// screen definition.
func (h *ScreenHandler) RegisterListItemTemplate(
name string,
fn screen.ListItemTemplateFn,
) {
h.listItemTemplates[name] = fn
}
// RegisterListDataItemRenderer registers a ListDataItemRenderer with the ScreenHandler.
//
// Use this function to register the renderers for the lists used in the
// screen definition.
func (h *ScreenHandler) RegisterListDataItemRenderer(
name string,
fn screen.ListDataItemRendererFn,
) {
h.listDataItemRenderers[name] = fn
}
// RegisterListItemRenderer registers a ListItemRenderer with the ScreenHandler.
//
// Use this function to register the renderers for the lists used in the
// screen definition.
func (h *ScreenHandler) RegisterListItemRenderer(
name string,
fn screen.ListItemRendererFn,
) {
h.listItemRenderers[name] = fn
}
// RegisterListLength registers a ListLength with the ScreenHandler.
//
// Use this function to register the lengths for the lists used in the
// screen definition.
func (h *ScreenHandler) RegisterListLength(
name string,
fn screen.ListLengthFn,
) {
h.listLengths[name] = fn
}
// RegisterOnListItemSelectedHandler registers a OnListItemSelectedHandler with the ScreenHandler.
//
// Use this function to register the handler functions used in the screen
// definition.
func (h *ScreenHandler) RegisterOnListItemSelectedHandler(
name string,
fn screen.ListItemHandlerFn,
) {
h.onListItemSelectedHandlers[name] = fn
}
// RegisterOnListItemUnselectedHandler registers a OnListItemUnselectedHandler with the ScreenHandler.
//
// Use this function to register the handler functions used in the screen
// definition.
func (h *ScreenHandler) RegisterOnListItemUnselectedHandler(
name string,
fn screen.ListItemHandlerFn,
) {
h.onListItemUnselectedHandlers[name] = fn
}
// RegisterOnClickHandler registers a OnClickHandler with the ScreenHandler.
//
// Use this function to register the handler functions used in the screen
// definition.
func (h *ScreenHandler) RegisterOnClickHandler(
name string,
fn screen.ClickHandlerFn,
) {
h.onClickedHandlers[name] = fn
}
// RegisterOnCheckChangedHandler registers a OnCheckChangedHandler with the ScreenHandler.
//
// Use this function to register the handler functions used in the screen
// definition.
func (h *ScreenHandler) RegisterOnCheckChangedHandler(
name string,
fn screen.CheckHandlerFn,
) {
h.onCheckChangedHandlers[name] = fn
}
// RegisterOnDateSelectedHandler registers a OnDateSelectedHandler with the ScreenHandler.
//
// Use this function to register the handler functions used in the screen
// definition.
func (h *ScreenHandler) RegisterOnDateSelectedHandler(
name string,
fn screen.DateSelectedHandlerFn,
) {
h.onDateSelectedHandlers[name] = fn
}
2024-04-11 14:52:32 +02:00
// RegisterOnOptionSelectedHandler registers a OnOptionSelectedHandler with the ScreenHandler.
//
// Use this function to register the handler functions used in the screen
// definition.
func (h *ScreenHandler) RegisterOnOptionSelectedHandler(
name string,
fn screen.OptionSelectedHandlerFn,
) {
h.onOptionSelectedHandlers[name] = fn
}
2024-04-03 16:07:24 +02:00
// RegisterOnValidationChangedHandler registers a OnValidationChangedHandler with the ScreenHandler.
//
// Use this function to register the handler functions used in the screen
// definition.
func (h *ScreenHandler) RegisterOnValidationChangedHandler(
name string,
fn screen.ValidationChangedHandlerFn,
) {
h.onValidationChangedHandlers[name] = fn
}
// GetBinding returns the binding with the given name.
//
// This function forwards the call to the controller. In the controller the
// required bindings should be mapped to the screen elements.
func (h *ScreenHandler) GetBinding(name string) binding.DataItem {
return h.viewModel.GetBinding(name)
2024-04-03 16:07:24 +02:00
}
// GetValidator returns the validator with the given name.
//
// This function forwards the call to the controller. In the controller the
// required validators should be mapped to the screen elements.
func (h *ScreenHandler) GetValidator(name string) fyne.StringValidator {
return h.viewModel.GetValidator(name)
2024-04-03 16:07:24 +02:00
}
// GetElement returns the UIElement with the given ID.
//
// This function allows you to retrieve the exported elements (given an ID in
// the screen definition). The UIElement contains the object and the decorator.
// Should no decorator be present, both point to the same widget. If a decorator
// is present it's advices to do hide and show operations on the decorator, and
// all other operators on the object.
func (h *ScreenHandler) GetElement(id string) *screen.UIElement {
elem, ok := h.exportedElements[id]
if !ok {
log.Printf("element %s not found", id)
return nil
2024-04-03 16:07:24 +02:00
}
return elem
2024-04-03 16:07:24 +02:00
}
2024-04-11 14:52:32 +02:00
func (h *ScreenHandler) ReplaceElementObject(
containerId string,
id string,
newDecorator fyne.CanvasObject,
newObject fyne.CanvasObject,
) {
containerElement := h.GetElement(containerId)
if containerElement == nil {
return
}
containerObj := containerElement.Object.(*fyne.Container)
element := h.GetElement(id)
if element == nil {
return
}
decorator := element.Decorator
allObjs := containerObj.Objects
replaced := false
for i, obj := range allObjs {
if obj == decorator {
allObjs[i] = newDecorator
replaced = true
break
}
}
if replaced {
h.exportedElements[id].Decorator = newDecorator
h.exportedElements[id].Object = newObject
}
containerObj.Refresh()
}
2024-04-03 16:07:24 +02:00
// GetLocalizer returns the i18n.Localizer used for localization.
func (h *ScreenHandler) GetLocalizer() *i18n.Localizer {
return h.localizer
}
// GetIcon returns the icon with the given name.
//
// This handler only implements the default theme icons. If you wish to support
// other items, wrap this ScreenHandler and implement the loading of the icons
// yourself.
func (h *ScreenHandler) GetIcon(iconName string) fyne.Resource {
if strings.HasPrefix(iconName, "theme.") {
iconName := strings.SplitN(iconName, ".", 2)[1]
return theme.DefaultTheme().Icon(fyne.ThemeIconName(iconName))
}
log.Printf("icon %s not found", iconName)
2024-04-03 16:07:24 +02:00
return nil
}
// GetListItemTemplate returns the ListItemTemplate with the given name.
func (h *ScreenHandler) GetListItemTemplate(name string) screen.ListItemTemplateFn {
fn, ok := h.listItemTemplates[name]
if !ok {
log.Printf("list template %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
// GetListDataItemRenderer returns the ListDataItemRenderer with the given name.
func (h *ScreenHandler) GetListDataItemRenderer(name string) screen.ListDataItemRendererFn {
fn, ok := h.listDataItemRenderers[name]
if !ok {
log.Printf("list renderer %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
// GetListItemRenderer returns the ListItemRenderer with the given name.
func (h *ScreenHandler) GetListItemRenderer(name string) screen.ListItemRendererFn {
fn, ok := h.listItemRenderers[name]
if !ok {
log.Printf("list renderer %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
// GetListLength returns the ListLength with the given name.
func (h *ScreenHandler) GetListLength(name string) screen.ListLengthFn {
fn, ok := h.listLengths[name]
if !ok {
log.Printf("list length %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
// GetOnListItemSelectedHandler returns the OnListItemSelectedHandler with the given name.
func (h *ScreenHandler) GetOnListItemSelectedHandler(name string) screen.ListItemHandlerFn {
fn, ok := h.onListItemSelectedHandlers[name]
if !ok {
log.Printf("list item selected handler %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
// GetOnListItemUnselectedHandler returns the OnListItemUnselectedHandler with the given name.
func (h *ScreenHandler) GetOnListItemUnselectedHandler(name string) screen.ListItemHandlerFn {
fn, ok := h.onListItemUnselectedHandlers[name]
if !ok {
log.Printf("list item unselected handler %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
// GetOnClickedHandler returns the OnClickedHandler with the given name.
func (h *ScreenHandler) GetOnClickedHandler(name string) screen.ClickHandlerFn {
fn, ok := h.onClickedHandlers[name]
if !ok {
log.Printf("clicked handler %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
// GetOnCheckChangedHandler returns the OnCheckChangedHandler with the given name.
func (h *ScreenHandler) GetOnCheckChangedHandler(name string) screen.CheckHandlerFn {
fn, ok := h.onCheckChangedHandlers[name]
if !ok {
log.Printf("check changed handler %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
// GetOnDateSelectedHandler returns the OnDateSelectedHandler with the given name.
func (h *ScreenHandler) GetOnDateSelectedHandler(name string) screen.DateSelectedHandlerFn {
fn, ok := h.onDateSelectedHandlers[name]
if !ok {
log.Printf("date selected handler %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
2024-04-11 14:52:32 +02:00
func (h *ScreenHandler) GetOnOptionSelectedHandler(name string) screen.OptionSelectedHandlerFn {
fn, ok := h.onOptionSelectedHandlers[name]
if !ok {
log.Printf("option selected handler %s not found", name)
return nil
}
return fn
}
2024-04-03 16:07:24 +02:00
// GetOnValidationChangedHandler returns the OnValidationChangedHandler with the given name.
func (h *ScreenHandler) GetOnValidationChangedHandler(name string) screen.ValidationChangedHandlerFn {
fn, ok := h.onValidationChangedHandlers[name]
if !ok {
log.Printf("validation changed handler %s not found", name)
2024-04-03 16:07:24 +02:00
return nil
}
return fn
}
2024-06-14 14:15:47 +02:00
func (h *ScreenHandler) RegisterResizeCallback(cb ResizeCallbackFn) {
h.screenDefinition.RegisterResizeCallback(cb)
}
func (h *ScreenHandler) RegisterMoveCallback(cb MoveCallbackFn) {
h.screenDefinition.RegisterMoveCallback(cb)
}