From 25625e9a995c4726781aeee4a0c7cb18c2f1eac8 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Sat, 18 May 2024 13:25:06 -0400 Subject: [PATCH] Add dialog boxes --- dialog.go | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 dialog.go diff --git a/dialog.go b/dialog.go new file mode 100644 index 0000000..1cd3928 --- /dev/null +++ b/dialog.go @@ -0,0 +1,101 @@ +package objects + +import "image" +import "git.tebibyte.media/tomo/tomo" +import "git.tebibyte.media/tomo/tomo/event" +import "git.tebibyte.media/tomo/tomo/theme" +import "git.tebibyte.media/tomo/objects/layouts" + +// DialogKind defines the semantic role of a dialog window. +type DialogKind int; const ( + DialogInformation DialogKind = iota + DialogQuestion + DialogWarning + DialogError +) + +// Dialog is a modal dialog window. +type Dialog struct { + tomo.Window + controlRow tomo.ContainerBox +} + +type clickable interface { + OnClick (func ()) event.Cookie +} + +// New creates a new dialog window given a dialog kind. +func (kind DialogKind) New (parent tomo.Window, title, message string, options ...tomo.Object) (*Dialog, error) { + if title == "" { + switch kind { + case DialogInformation: title = "Information" + case DialogQuestion: title = "Question" + case DialogWarning: title = "Warning" + case DialogError: title = "Error" + } + } + + dialog := &Dialog { } + if parent == nil { + window, err := tomo.NewWindow(image.Rectangle { }) + if err != nil { return nil, err } + dialog.Window = window + } else { + window, err := parent.NewModal(image.Rectangle { }) + if err != nil { return nil, err } + dialog.Window = window + } + + var iconId theme.Icon + switch kind { + case DialogInformation: iconId = theme.IconStatusInformation + case DialogQuestion: iconId = theme.IconStatusQuestion + case DialogWarning: iconId = theme.IconStatusWarning + case DialogError: iconId = theme.IconStatusError + } + dialog.SetTitle(title) + icon := NewIcon(iconId, theme.IconSizeLarge) + messageText := NewLabel(message) + messageText.SetAlign(tomo.AlignStart, tomo.AlignMiddle) + + for _, option := range options { + if option, ok := option.(clickable); ok { + option.OnClick(dialog.Close) + } + } + dialog.controlRow = NewInnerContainer(layouts.ContractHorizontal, options...) + dialog.controlRow.SetAlign(tomo.AlignEnd, tomo.AlignEnd) + + dialog.SetRoot(NewOuterContainer ( + layouts.NewGrid([]bool { true }, []bool { true, false }), + NewInnerContainer(layouts.ContractHorizontal, icon, messageText), + dialog.controlRow)) + return dialog, nil +} + +// NewOk creates a new dialog window with an OK option. +func (kind DialogKind) NewOk (parent tomo.Window, title, message string, onOk func ()) (*Dialog, error) { + okButton := NewButton("OK") + okButton.SetIcon(theme.IconStatusOkay) + okButton.OnClick(func () { + if onOk != nil { onOk() } + }) + okButton.SetFocused(true) + + return kind.New(parent, title, message, okButton) +} + +// NewOkCancel creates a new dialog window with OK and Cancel options. +func (kind DialogKind) NewOkCancel (parent tomo.Window, title, message string, onOk func ()) (*Dialog, error) { + cancelButton := NewButton("Cancel") + cancelButton.SetIcon(theme.IconStatusCancel) + + okButton := NewButton("OK") + okButton.SetIcon(theme.IconStatusOkay) + okButton.OnClick(func () { + if onOk != nil { onOk() } + }) + okButton.SetFocused(true) + + return kind.New(parent, title, message, cancelButton, okButton) +}