487 lines
9.6 KiB
Go
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
|
|
|
|
Ctrl DialogController
|
|
*ScreenHandler
|
|
}
|
|
|
|
func NewBaseDialog(
|
|
concreteDialog Dialog,
|
|
name DialogName,
|
|
kind DialogKind,
|
|
controller DialogController,
|
|
parent fyne.Window,
|
|
confirm OnConfirmFn,
|
|
) *BaseDialog {
|
|
return &BaseDialog{
|
|
id: uuid.NewString(),
|
|
concreteDialog: concreteDialog,
|
|
dlgName: name,
|
|
dlgKind: kind,
|
|
window: parent,
|
|
onConfirm: confirm,
|
|
Ctrl: controller,
|
|
}
|
|
}
|
|
|
|
func NewBaseDialogWithScreen(
|
|
concreteDialog Dialog,
|
|
name DialogName,
|
|
kind DialogKind,
|
|
controller DialogController,
|
|
parent fyne.Window,
|
|
confirm OnConfirmFn,
|
|
filesystem embed.FS,
|
|
screenName string,
|
|
localizer *i18n.Localizer,
|
|
) *BaseDialog {
|
|
d := NewBaseDialog(
|
|
concreteDialog, name, kind, controller,
|
|
parent, confirm)
|
|
|
|
var err error
|
|
d.ScreenHandler, err = NewScreenHandler(
|
|
filesystem, screenName, controller, localizer)
|
|
if err != nil {
|
|
LogWindowError(d, LoadScreen, err)
|
|
return nil
|
|
}
|
|
return d
|
|
}
|
|
|
|
func NewBaseDialogWithData(
|
|
concreteDialog Dialog,
|
|
name DialogName,
|
|
kind DialogKind,
|
|
controller DialogController,
|
|
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,
|
|
Ctrl: controller,
|
|
}
|
|
}
|
|
|
|
func NewBaseDialogWithDataAndScreen(
|
|
concreteDialog Dialog,
|
|
name DialogName,
|
|
kind DialogKind,
|
|
controller DialogController,
|
|
parent fyne.Window,
|
|
confirm OnConfirmFn,
|
|
data binding.DataItem,
|
|
filesystem embed.FS,
|
|
screenName string,
|
|
localizer *i18n.Localizer,
|
|
) *BaseDialog {
|
|
d := NewBaseDialogWithData(
|
|
concreteDialog, name, kind, controller,
|
|
parent, confirm, data)
|
|
|
|
var err error
|
|
d.ScreenHandler, err = NewScreenHandler(
|
|
filesystem, screenName, controller, 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.Ctrl.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,
|
|
controller DialogController,
|
|
parent fyne.Window,
|
|
confirm OnConfirmFn,
|
|
itemsFn FormDialogItemsFn,
|
|
) *FormDialog {
|
|
return &FormDialog{
|
|
BaseDialog: NewBaseDialog(
|
|
concreteDialog, name, kind, controller, parent, confirm),
|
|
itemsFn: itemsFn,
|
|
confirmLabel: "OK",
|
|
dismissLabel: "Cancel",
|
|
}
|
|
}
|
|
|
|
func NewFormDialogWithScreen(
|
|
concreteDialog Dialog,
|
|
name DialogName,
|
|
kind DialogKind,
|
|
controller DialogController,
|
|
parent fyne.Window,
|
|
confirm OnConfirmFn,
|
|
filesystem embed.FS,
|
|
screenName string,
|
|
localizer *i18n.Localizer,
|
|
) *FormDialog {
|
|
return &FormDialog{
|
|
BaseDialog: NewBaseDialogWithScreen(
|
|
concreteDialog, name, kind, controller, parent, confirm,
|
|
filesystem, screenName, localizer),
|
|
confirmLabel: "OK",
|
|
dismissLabel: "Cancel",
|
|
}
|
|
}
|
|
|
|
func NewformDialogWithData(
|
|
concreteDialog Dialog,
|
|
name DialogName,
|
|
kind DialogKind,
|
|
controller DialogController,
|
|
parent fyne.Window,
|
|
confirm OnConfirmFn,
|
|
itemsFn FormDialogItemsFn,
|
|
data binding.DataItem,
|
|
) *FormDialog {
|
|
return &FormDialog{
|
|
BaseDialog: NewBaseDialogWithData(
|
|
concreteDialog, name, kind, controller, parent, confirm, data),
|
|
itemsFn: itemsFn,
|
|
confirmLabel: "OK",
|
|
dismissLabel: "Cancel",
|
|
}
|
|
}
|
|
|
|
func NewFormDialogWithDataAndScreen(
|
|
concreteDialog Dialog,
|
|
name DialogName,
|
|
kind DialogKind,
|
|
controller DialogController,
|
|
parent fyne.Window,
|
|
confirm OnConfirmFn,
|
|
data binding.DataItem,
|
|
filesystem embed.FS,
|
|
screenName string,
|
|
localizer *i18n.Localizer,
|
|
) *FormDialog {
|
|
return &FormDialog{
|
|
BaseDialog: NewBaseDialogWithDataAndScreen(
|
|
concreteDialog, name, kind, controller, 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
|
|
}
|