ui/dialog.go

519 lines
10 KiB
Go
Raw Normal View History

2024-03-30 17:45:07 +01:00
package ui
import (
2024-04-03 16:07:24 +02:00
"embed"
2024-03-30 17:45:07 +01:00
"fmt"
2024-04-19 16:44:36 +02:00
"io/fs"
2024-03-30 17:45:07 +01:00
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"gitea.hevanto-it.com/hevanto/ui/screen"
2024-03-30 17:45:07 +01:00
"github.com/google/uuid"
2024-04-03 16:07:24 +02:00
"github.com/nicksnyder/go-i18n/v2/i18n"
2024-03-30 17:45:07 +01:00
)
type DialogName string
type DialogKind string
type OnConfirmFn func(bool, error, ...interface{})
2024-04-03 16:07:24 +02:00
type NewDialogFn func(
DialogName,
DialogKind,
fyne.Window,
OnConfirmFn,
binding.DataItem,
*embed.FS,
string,
*i18n.Localizer) Dialog
2024-03-30 17:45:07 +01:00
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
2024-04-03 16:07:24 +02:00
ViewModel DialogViewModel
2024-04-03 16:07:24 +02:00
*ScreenHandler
2024-03-30 17:45:07 +01:00
}
func NewBaseDialog(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
2024-03-30 17:45:07 +01:00
parent fyne.Window,
confirm OnConfirmFn,
) *BaseDialog {
return &BaseDialog{
id: uuid.NewString(),
concreteDialog: concreteDialog,
dlgName: name,
dlgKind: kind,
window: parent,
onConfirm: confirm,
ViewModel: viewModel,
2024-04-03 16:07:24 +02:00
}
}
func NewBaseDialogWithScreen(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
2024-04-03 16:07:24 +02:00
parent fyne.Window,
confirm OnConfirmFn,
filesystem embed.FS,
screenName string,
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
) *BaseDialog {
d := NewBaseDialog(
concreteDialog, name, kind, viewModel,
2024-04-03 16:07:24 +02:00
parent, confirm)
var err error
2024-08-06 21:30:11 +02:00
d.ScreenHandler, err = NewDialogScreenHandler(
2024-04-19 16:44:36 +02:00
filesystem, screenName, viewModel, localizer, assetLoader)
2024-04-03 16:07:24 +02:00
if err != nil {
LogWindowError(d, LoadScreen, err)
return nil
2024-03-30 17:45:07 +01:00
}
2024-04-03 16:07:24 +02:00
return d
2024-03-30 17:45:07 +01:00
}
func NewBaseDialogWithData(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
2024-03-30 17:45:07 +01:00
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,
2024-03-30 17:45:07 +01:00
}
}
2024-04-03 16:07:24 +02:00
func NewBaseDialogWithDataAndScreen(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
2024-04-03 16:07:24 +02:00
parent fyne.Window,
confirm OnConfirmFn,
data binding.DataItem,
filesystem embed.FS,
screenName string,
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
) *BaseDialog {
d := NewBaseDialogWithData(
concreteDialog, name, kind, viewModel,
2024-04-03 16:07:24 +02:00
parent, confirm, data)
var err error
2024-08-06 21:30:11 +02:00
d.ScreenHandler, err = NewDialogScreenHandler(
2024-04-19 16:44:36 +02:00
filesystem, screenName, viewModel, localizer, assetLoader)
2024-04-03 16:07:24 +02:00
if err != nil {
LogWindowError(d, LoadScreen, err)
return nil
}
return d
}
2024-03-30 17:45:07 +01:00
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) {
2024-04-03 16:07:24 +02:00
2024-03-30 17:45:07 +01:00
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 {
2024-04-03 16:07:24 +02:00
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
}
2024-08-06 21:30:11 +02:00
d.CanvasObj = obj.(*screen.DialogScreen).AsContainer()
2024-04-03 16:07:24 +02:00
}
2024-03-30 17:45:07 +01:00
return d.CanvasObj
}
func (d *BaseDialog) Refresh() {
if d.CanvasObj != nil {
d.CanvasObj.Refresh()
}
}
func (d *BaseDialog) OnShow() {
//LogWindowEvent(d, OnShow)
2024-03-30 17:45:07 +01:00
}
func (d *BaseDialog) OnHide() {
//LogWindowEvent(d, OnHide)
2024-03-30 17:45:07 +01:00
}
func (d *BaseDialog) OnClose(confirmed bool) {
LogWindowEventMessage(d, OnClose, fmt.Sprintf("confirmed: %v", confirmed))
d.OnHide()
}
2024-04-19 16:44:36 +02:00
func (d *BaseDialog) GetCanvasObject() fyne.CanvasObject {
return d.CanvasObj
}
func (d BaseDialog) PreventClose() bool {
return false
}
2024-03-30 17:45:07 +01:00
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,
2024-03-30 17:45:07 +01:00
parent fyne.Window,
confirm OnConfirmFn,
itemsFn FormDialogItemsFn,
) *FormDialog {
return &FormDialog{
2024-04-03 16:07:24 +02:00
BaseDialog: NewBaseDialog(
concreteDialog, name, kind, viewModel, parent, confirm),
2024-03-30 17:45:07 +01:00
itemsFn: itemsFn,
confirmLabel: "OK",
dismissLabel: "Cancel",
}
}
2024-04-03 16:07:24 +02:00
func NewFormDialogWithScreen(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
2024-04-03 16:07:24 +02:00
parent fyne.Window,
confirm OnConfirmFn,
filesystem embed.FS,
screenName string,
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
) *FormDialog {
return &FormDialog{
BaseDialog: NewBaseDialogWithScreen(
concreteDialog, name, kind, viewModel, parent, confirm,
2024-04-19 16:44:36 +02:00
filesystem, screenName, localizer, assetLoader),
2024-04-03 16:07:24 +02:00
confirmLabel: "OK",
dismissLabel: "Cancel",
}
}
2024-03-30 17:45:07 +01:00
func NewformDialogWithData(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
2024-03-30 17:45:07 +01:00
parent fyne.Window,
confirm OnConfirmFn,
itemsFn FormDialogItemsFn,
data binding.DataItem,
) *FormDialog {
return &FormDialog{
2024-04-03 16:07:24 +02:00
BaseDialog: NewBaseDialogWithData(
concreteDialog, name, kind, viewModel, parent, confirm, data),
2024-03-30 17:45:07 +01:00
itemsFn: itemsFn,
confirmLabel: "OK",
dismissLabel: "Cancel",
}
}
2024-04-03 16:07:24 +02:00
func NewFormDialogWithDataAndScreen(
concreteDialog Dialog,
name DialogName,
kind DialogKind,
viewModel DialogViewModel,
2024-04-03 16:07:24 +02:00
parent fyne.Window,
confirm OnConfirmFn,
data binding.DataItem,
filesystem embed.FS,
screenName string,
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
) *FormDialog {
return &FormDialog{
BaseDialog: NewBaseDialogWithDataAndScreen(
concreteDialog, name, kind, viewModel, parent, confirm, data,
2024-04-19 16:44:36 +02:00
filesystem, screenName, localizer, assetLoader),
2024-04-03 16:07:24 +02:00
confirmLabel: "OK",
dismissLabel: "Cancel",
}
}
2024-03-30 17:45:07 +01:00
func (d *FormDialog) SetButtonLabels(
confirm string,
dismiss string,
) {
d.confirmLabel = confirm
d.dismissLabel = dismiss
}
2024-04-03 16:07:24 +02:00
func (d *FormDialog) Initialize() fyne.CanvasObject {
if d.itemsFn == nil {
2024-08-06 21:30:11 +02:00
form := d.BaseDialog.Initialize().(*fyne.Container)
2024-04-03 16:07:24 +02:00
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
}
2024-03-30 17:45:07 +01:00
func (d *FormDialog) Show(parent ...fyne.Window) {
2024-04-03 16:07:24 +02:00
d.concreteDialog.Initialize()
2024-03-30 17:45:07 +01:00
d.items = d.itemsFn()
d.concreteDialog.OnShow()
2024-04-11 14:52:32 +02:00
frm := dialog.NewForm(
2024-03-30 17:45:07 +01:00
d.concreteDialog.Title(),
d.confirmLabel,
d.dismissLabel,
d.items,
d.concreteDialog.OnClose,
d.window,
)
2024-04-11 14:52:32 +02:00
d.dlg = frm
2024-03-30 17:45:07 +01:00
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(
2024-04-03 16:07:24 +02:00
name DialogName,
kind DialogKind,
2024-03-30 17:45:07 +01:00
parent fyne.Window,
confirm OnConfirmFn,
) Dialog {
if dialogCatalog == nil {
return nil
}
if kindCatalog, ok := dialogCatalog[name]; ok {
if fn, ok := kindCatalog[kind]; ok {
2024-04-03 16:07:24 +02:00
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)
2024-03-30 17:45:07 +01:00
}
}
return nil
}
func NewDialogWithData(
2024-04-03 16:07:24 +02:00
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,
2024-03-30 17:45:07 +01:00
parent fyne.Window,
confirm OnConfirmFn,
data binding.DataItem,
2024-04-03 16:07:24 +02:00
filesystem embed.FS,
screenName string,
localizer *i18n.Localizer,
2024-03-30 17:45:07 +01:00
) Dialog {
if dialogCatalog == nil {
return nil
}
if kindCatalog, ok := dialogCatalog[name]; ok {
if fn, ok := kindCatalog[kind]; ok {
2024-04-03 16:07:24 +02:00
return fn(
name, kind, parent, confirm, data,
&filesystem, screenName, localizer)
2024-03-30 17:45:07 +01:00
}
}
return nil
}
2024-04-19 16:44:36 +02:00
func ShowConfirmDialog(
title string,
message string,
lblOk string,
lblCancel string,
callback func(bool),
parent fyne.Window,
) {
label := widget.NewLabel(message)
label.Wrapping = fyne.TextWrapBreak
dlg := dialog.NewCustomConfirm(
title, lblOk, lblCancel,
label, callback, parent)
dlg.Resize(fyne.NewSize(480, 100))
dlg.Show()
}