ui/dialog.go
2024-04-04 15:00:35 +02:00

487 lines
9.6 KiB
Go

package ui
import (
"embed"
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"github.com/google/uuid"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
type DialogName string
type DialogKind string
type OnConfirmFn func(bool, error, ...interface{})
type NewDialogFn func(
DialogName,
DialogKind,
fyne.Window,
OnConfirmFn,
binding.DataItem,
*embed.FS,
string,
*i18n.Localizer) Dialog
const (
DlgGeneric DialogKind = "Generic"
)
type Dialog interface {
View
Window() fyne.Window
View() View
SetInitialFocusItem(focusItem fyne.Focusable)
Show(parent ...fyne.Window)
OnClose(bool)
DialogName() DialogName
DialogKind() DialogKind
OnConfirmFn() OnConfirmFn
}
type BaseDialog struct {
id string
concreteDialog Dialog
dlgName DialogName
dlgKind DialogKind
dlg dialog.Dialog
window fyne.Window
onConfirm OnConfirmFn
CanvasObj fyne.CanvasObject
Data binding.DataItem
focusItem fyne.Focusable
ViewModel DialogViewModel
*ScreenHandler
}
func NewBaseDialog(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
parent fyne.Window,
confirm OnConfirmFn,
) *BaseDialog {
return &BaseDialog{
id: uuid.NewString(),
concreteDialog: concreteDialog,
dlgName: name,
dlgKind: kind,
window: parent,
onConfirm: confirm,
ViewModel: viewModel,
}
}
func NewBaseDialogWithScreen(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
parent fyne.Window,
confirm OnConfirmFn,
filesystem embed.FS,
screenName string,
localizer *i18n.Localizer,
) *BaseDialog {
d := NewBaseDialog(
concreteDialog, name, kind, viewModel,
parent, confirm)
var err error
d.ScreenHandler, err = NewScreenHandler(
filesystem, screenName, viewModel, localizer)
if err != nil {
LogWindowError(d, LoadScreen, err)
return nil
}
return d
}
func NewBaseDialogWithData(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
parent fyne.Window,
confirm OnConfirmFn,
data binding.DataItem,
) *BaseDialog {
return &BaseDialog{
id: uuid.NewString(),
concreteDialog: concreteDialog,
dlgName: name,
dlgKind: kind,
window: parent,
onConfirm: confirm,
Data: data,
ViewModel: viewModel,
}
}
func NewBaseDialogWithDataAndScreen(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
parent fyne.Window,
confirm OnConfirmFn,
data binding.DataItem,
filesystem embed.FS,
screenName string,
localizer *i18n.Localizer,
) *BaseDialog {
d := NewBaseDialogWithData(
concreteDialog, name, kind, viewModel,
parent, confirm, data)
var err error
d.ScreenHandler, err = NewScreenHandler(
filesystem, screenName, viewModel, localizer)
if err != nil {
LogWindowError(d, LoadScreen, err)
return nil
}
return d
}
func (d BaseDialog) Window() fyne.Window {
return d.window
}
func (d *BaseDialog) View() View {
return d
}
func (d BaseDialog) Equals(other View) bool {
return d.id == other.ID()
}
func (d *BaseDialog) SetInitialFocusItem(focusItem fyne.Focusable) {
d.focusItem = focusItem
}
func (d BaseDialog) DialogName() DialogName {
return d.dlgName
}
func (d BaseDialog) DialogKind() DialogKind {
return d.dlgKind
}
func (d BaseDialog) OnConfirmFn() OnConfirmFn {
return d.onConfirm
}
func (d *BaseDialog) Show(parent ...fyne.Window) {
d.concreteDialog.OnShow()
d.dlg = dialog.NewCustomConfirm(
d.concreteDialog.Title(),
"OK", "Cancel",
d.concreteDialog.Initialize(),
d.concreteDialog.OnClose,
d.window,
)
d.dlg.Show()
d.dlg.Resize(fyne.NewSize(640, 480))
if d.focusItem != nil {
window := d.window
if parent != nil {
window = parent[0]
}
window.Canvas().Focus(d.focusItem)
}
}
func (d BaseDialog) ID() string {
return d.id
}
func (d *BaseDialog) SetID(id string) {
d.id = id
}
func (d BaseDialog) Title() string {
return ""
}
func (d *BaseDialog) Initialize() fyne.CanvasObject {
if err := d.ViewModel.Initialize(d.Data); err != nil {
LogWindowError(d, Initialize, err)
}
if d.ScreenHandler != nil && d.ScreenDefinition() != nil {
obj, err := d.ScreenDefinition().Initialize()
if err != nil {
LogWindowError(d, Initialize, err)
return nil
}
d.CanvasObj = obj
}
return d.CanvasObj
}
func (d *BaseDialog) Refresh() {
if d.CanvasObj != nil {
d.CanvasObj.Refresh()
}
}
func (d *BaseDialog) OnShow() {
//LogWindowEvent(d, OnShow)
}
func (d *BaseDialog) OnHide() {
//LogWindowEvent(d, OnHide)
}
func (d *BaseDialog) OnClose(confirmed bool) {
LogWindowEventMessage(d, OnClose, fmt.Sprintf("confirmed: %v", confirmed))
d.OnHide()
}
type FormDialogItemsFn func() []*widget.FormItem
type FormDialog struct {
*BaseDialog
confirmLabel string
dismissLabel string
itemsFn FormDialogItemsFn
items []*widget.FormItem
}
func NewFormDialog(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
parent fyne.Window,
confirm OnConfirmFn,
itemsFn FormDialogItemsFn,
) *FormDialog {
return &FormDialog{
BaseDialog: NewBaseDialog(
concreteDialog, name, kind, viewModel, parent, confirm),
itemsFn: itemsFn,
confirmLabel: "OK",
dismissLabel: "Cancel",
}
}
func NewFormDialogWithScreen(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
parent fyne.Window,
confirm OnConfirmFn,
filesystem embed.FS,
screenName string,
localizer *i18n.Localizer,
) *FormDialog {
return &FormDialog{
BaseDialog: NewBaseDialogWithScreen(
concreteDialog, name, kind, viewModel, parent, confirm,
filesystem, screenName, localizer),
confirmLabel: "OK",
dismissLabel: "Cancel",
}
}
func NewformDialogWithData(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
parent fyne.Window,
confirm OnConfirmFn,
itemsFn FormDialogItemsFn,
data binding.DataItem,
) *FormDialog {
return &FormDialog{
BaseDialog: NewBaseDialogWithData(
concreteDialog, name, kind, viewModel, parent, confirm, data),
itemsFn: itemsFn,
confirmLabel: "OK",
dismissLabel: "Cancel",
}
}
func NewFormDialogWithDataAndScreen(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
parent fyne.Window,
confirm OnConfirmFn,
data binding.DataItem,
filesystem embed.FS,
screenName string,
localizer *i18n.Localizer,
) *FormDialog {
return &FormDialog{
BaseDialog: NewBaseDialogWithDataAndScreen(
concreteDialog, name, kind, viewModel, parent, confirm, data,
filesystem, screenName, localizer),
confirmLabel: "OK",
dismissLabel: "Cancel",
}
}
func (d *FormDialog) SetButtonLabels(
confirm string,
dismiss string,
) {
d.confirmLabel = confirm
d.dismissLabel = dismiss
}
func (d *FormDialog) Initialize() fyne.CanvasObject {
if d.itemsFn == nil {
form := d.BaseDialog.Initialize().(*fyne.Container)
d.itemsFn = func() []*widget.FormItem {
res := make([]*widget.FormItem, 0, len(form.Objects)/2)
for i := 0; i < len(form.Objects); i += 2 {
res = append(res, widget.NewFormItem(
form.Objects[i].(*widget.Label).Text,
form.Objects[i+1]))
}
return res
}
return nil
}
return nil
}
func (d *FormDialog) Show(parent ...fyne.Window) {
d.concreteDialog.Initialize()
d.items = d.itemsFn()
d.concreteDialog.OnShow()
d.dlg = dialog.NewForm(
d.concreteDialog.Title(),
d.confirmLabel,
d.dismissLabel,
d.items,
d.concreteDialog.OnClose,
d.window,
)
d.dlg.Show()
d.dlg.Resize(fyne.NewSize(640, 480))
if d.focusItem != nil {
window := d.window
if parent != nil {
window = parent[0]
}
window.Canvas().Focus(d.focusItem)
}
}
func (d *FormDialog) Refresh() {
for _, itm := range d.items {
itm.Widget.Refresh()
}
}
var dialogCatalog map[DialogName]map[DialogKind]NewDialogFn
func RegisterDialog(name DialogName, kind DialogKind, fn NewDialogFn) {
if dialogCatalog == nil {
dialogCatalog = make(map[DialogName]map[DialogKind]NewDialogFn)
}
if _, ok := dialogCatalog[name]; !ok {
dialogCatalog[name] = make(map[DialogKind]NewDialogFn)
}
dialogCatalog[name][kind] = fn
}
func NewDialog(
name DialogName,
kind DialogKind,
parent fyne.Window,
confirm OnConfirmFn,
) Dialog {
if dialogCatalog == nil {
return nil
}
if kindCatalog, ok := dialogCatalog[name]; ok {
if fn, ok := kindCatalog[kind]; ok {
return fn(name, kind, parent, confirm, nil, nil, "", nil)
}
}
return nil
}
func NewDialogWithScreen(
name DialogName,
kind DialogKind,
parent fyne.Window,
confirm OnConfirmFn,
filesystem embed.FS,
screenName string,
localizer *i18n.Localizer,
) Dialog {
if dialogCatalog == nil {
return nil
}
if kindCatalog, ok := dialogCatalog[name]; ok {
if fn, ok := kindCatalog[kind]; ok {
return fn(
name, kind, parent, confirm, nil,
&filesystem, screenName, localizer)
}
}
return nil
}
func NewDialogWithData(
name DialogName,
kind DialogKind,
parent fyne.Window,
confirm OnConfirmFn,
data binding.DataItem,
) Dialog {
if dialogCatalog == nil {
return nil
}
if kindCatalog, ok := dialogCatalog[name]; ok {
if fn, ok := kindCatalog[kind]; ok {
return fn(name, kind, parent, confirm, data, nil, "", nil)
}
}
return nil
}
func NewDialogWithDataAndScreen(
name DialogName,
kind DialogKind,
parent fyne.Window,
confirm OnConfirmFn,
data binding.DataItem,
filesystem embed.FS,
screenName string,
localizer *i18n.Localizer,
) Dialog {
if dialogCatalog == nil {
return nil
}
if kindCatalog, ok := dialogCatalog[name]; ok {
if fn, ok := kindCatalog[kind]; ok {
return fn(
name, kind, parent, confirm, data,
&filesystem, screenName, localizer)
}
}
return nil
}