mirror of
https://github.com/VladimirMarkelov/clui.git
synced 2025-04-26 13:49:01 +08:00

1. TableView does not use 'go' for calling table events. It allows to create any new dialog on the fly inside an event handler. That can be useful to create simple confirmation dialogs 2. TableView demo: added an example of how to show confirmation dialogs inside TableView event handler 3. Fix setting default button for confirmation dialog: it was not possible to set the first button as default one
259 lines
6.9 KiB
Go
259 lines
6.9 KiB
Go
package clui
|
|
|
|
import (
|
|
term "github.com/nsf/termbox-go"
|
|
)
|
|
|
|
// ConfirmationDialog is a simple dialog to get a user
|
|
// choice or confirmation. The dialog can contain upto
|
|
// three button with custom titles. There are a few
|
|
// predefined button sets: see Buttons* constants.
|
|
// The dialog is modal, so a user cannot interact other
|
|
// Views until the user closes the dialog
|
|
type ConfirmationDialog struct {
|
|
view *Window
|
|
result int
|
|
onClose func()
|
|
}
|
|
|
|
// SelectDialog allows to user to select an item from
|
|
// the list. Items can be displayed in ListBox or in
|
|
// RadioGroup.
|
|
// The dialog is modal, so a user cannot interact other
|
|
// Views until the user closes the dialog
|
|
type SelectDialog struct {
|
|
view *Window
|
|
result int
|
|
value int
|
|
rg *RadioGroup
|
|
list *ListBox
|
|
typ SelectDialogType
|
|
onClose func()
|
|
}
|
|
|
|
// NewConfirmationDialog creates new confirmation dialog.
|
|
// c is a composer that manages the dialog
|
|
// title is a dialog title
|
|
// question is a text inside dialog for user to explain what happens
|
|
// buttons is a titles for button inside dialog. If the list is empty,
|
|
// the dialog will have only one button 'OK'. If the list has more
|
|
// than 3 button then only first three items will be used in the dialog
|
|
// defaultButton is the number of button that is active right after
|
|
// dialog is created. If the number is greater than the number of
|
|
// buttons, no button is active
|
|
func CreateConfirmationDialog(title, question string, buttons []string, defaultButton int) *ConfirmationDialog {
|
|
dlg := new(ConfirmationDialog)
|
|
|
|
if len(buttons) == 0 {
|
|
buttons = []string{"OK"}
|
|
}
|
|
|
|
cw, ch := term.Size()
|
|
|
|
dlg.view = AddWindow(cw/2-12, ch/2-8, 30, 3, title)
|
|
dlg.view.SetConstraints(30, 3)
|
|
dlg.view.SetModal(true)
|
|
dlg.view.SetPack(Vertical)
|
|
CreateFrame(dlg.view, 1, 1, BorderNone, Fixed)
|
|
|
|
fbtn := CreateFrame(dlg.view, 1, 1, BorderNone, 1)
|
|
CreateFrame(fbtn, 1, 1, BorderNone, Fixed)
|
|
lb := CreateLabel(fbtn, 10, 3, question, 1)
|
|
lb.SetMultiline(true)
|
|
CreateFrame(fbtn, 1, 1, BorderNone, Fixed)
|
|
|
|
CreateFrame(dlg.view, 1, 1, BorderNone, Fixed)
|
|
frm1 := CreateFrame(dlg.view, 16, 4, BorderNone, Fixed)
|
|
CreateFrame(frm1, 1, 1, BorderNone, 1)
|
|
|
|
bText := buttons[0]
|
|
btn1 := CreateButton(frm1, AutoSize, AutoSize, bText, Fixed)
|
|
btn1.OnClick(func(ev Event) {
|
|
dlg.result = DialogButton1
|
|
composer().DestroyWindow(dlg.view)
|
|
if dlg.onClose != nil {
|
|
go dlg.onClose()
|
|
}
|
|
})
|
|
var btn2, btn3 *Button
|
|
|
|
if len(buttons) > 1 {
|
|
CreateFrame(frm1, 1, 1, BorderNone, 1)
|
|
btn2 = CreateButton(frm1, AutoSize, AutoSize, buttons[1], Fixed)
|
|
btn2.OnClick(func(ev Event) {
|
|
dlg.result = DialogButton2
|
|
composer().DestroyWindow(dlg.view)
|
|
if dlg.onClose != nil {
|
|
go dlg.onClose()
|
|
}
|
|
})
|
|
}
|
|
if len(buttons) > 2 {
|
|
CreateFrame(frm1, 1, 1, BorderNone, 1)
|
|
btn3 = CreateButton(frm1, AutoSize, AutoSize, buttons[2], Fixed)
|
|
btn3.OnClick(func(ev Event) {
|
|
dlg.result = DialogButton3
|
|
composer().DestroyWindow(dlg.view)
|
|
if dlg.onClose != nil {
|
|
go dlg.onClose()
|
|
}
|
|
})
|
|
}
|
|
|
|
CreateFrame(frm1, 1, 1, BorderNone, 1)
|
|
|
|
if defaultButton == DialogButton2 && len(buttons) > 1 {
|
|
ActivateControl(dlg.view, btn2)
|
|
} else if defaultButton == DialogButton3 && len(buttons) > 2 {
|
|
ActivateControl(dlg.view, btn3)
|
|
} else {
|
|
ActivateControl(dlg.view, btn1)
|
|
}
|
|
|
|
dlg.view.OnClose(func(ev Event) bool {
|
|
if dlg.result == DialogAlive {
|
|
dlg.result = DialogClosed
|
|
if ev.X != 1 {
|
|
composer().DestroyWindow(dlg.view)
|
|
}
|
|
if dlg.onClose != nil {
|
|
go dlg.onClose()
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
|
|
return dlg
|
|
}
|
|
|
|
// OnClose sets the callback that is called when the
|
|
// dialog is closed
|
|
func (d *ConfirmationDialog) OnClose(fn func()) {
|
|
d.onClose = fn
|
|
}
|
|
|
|
// Result returns what button closed the dialog.
|
|
// See DialogButton constants. It can equal DialogAlive
|
|
// that means that the dialog is still visible and a
|
|
// user still does not click any button
|
|
func (d *ConfirmationDialog) Result() int {
|
|
return d.result
|
|
}
|
|
|
|
// ------------------------ Selection Dialog ---------------------
|
|
|
|
// NewSelectDialog creates new dialog to select an item from list.
|
|
// c is a composer that manages the dialog
|
|
// title is a dialog title
|
|
// items is a list of items to select from
|
|
// selectedItem is the index of the item that is selected after
|
|
// the dialog is created
|
|
// typ is a selection type: ListBox or RadioGroup
|
|
// Returns nil in case of creation process fails, e.g, if item list is empty
|
|
func CreateSelectDialog(title string, items []string, selectedItem int, typ SelectDialogType) *SelectDialog {
|
|
dlg := new(SelectDialog)
|
|
|
|
if len(items) == 0 {
|
|
// Item list must contain at least 1 item
|
|
return nil
|
|
}
|
|
|
|
cw, ch := term.Size()
|
|
|
|
dlg.typ = typ
|
|
dlg.view = AddWindow(cw/2-12, ch/2-8, 20, 10, title)
|
|
dlg.view.SetModal(true)
|
|
dlg.view.SetPack(Vertical)
|
|
|
|
if typ == SelectDialogList {
|
|
fList := CreateFrame(dlg.view, 1, 1, BorderNone, 1)
|
|
fList.SetPaddings(1, 1)
|
|
fList.SetGaps(0, 0)
|
|
dlg.list = CreateListBox(fList, 15, 5, 1)
|
|
for _, item := range items {
|
|
dlg.list.AddItem(item)
|
|
}
|
|
if selectedItem >= 0 && selectedItem < len(items) {
|
|
dlg.list.SelectItem(selectedItem)
|
|
}
|
|
} else {
|
|
fRadio := CreateFrame(dlg.view, 1, 1, BorderNone, Fixed)
|
|
fRadio.SetPaddings(1, 1)
|
|
fRadio.SetGaps(0, 0)
|
|
fRadio.SetPack(Vertical)
|
|
dlg.rg = CreateRadioGroup()
|
|
for _, item := range items {
|
|
r := CreateRadio(fRadio, AutoSize, item, Fixed)
|
|
dlg.rg.AddItem(r)
|
|
}
|
|
if selectedItem >= 0 && selectedItem < len(items) {
|
|
dlg.rg.SetSelected(selectedItem)
|
|
}
|
|
}
|
|
|
|
frm1 := CreateFrame(dlg.view, 16, 4, BorderNone, Fixed)
|
|
CreateFrame(frm1, 1, 1, BorderNone, 1)
|
|
btn1 := CreateButton(frm1, AutoSize, AutoSize, "OK", Fixed)
|
|
btn1.OnClick(func(ev Event) {
|
|
dlg.result = DialogButton1
|
|
if dlg.typ == SelectDialogList {
|
|
dlg.value = dlg.list.SelectedItem()
|
|
} else {
|
|
dlg.value = dlg.rg.Selected()
|
|
}
|
|
composer().DestroyWindow(dlg.view)
|
|
if dlg.onClose != nil {
|
|
go dlg.onClose()
|
|
}
|
|
})
|
|
|
|
CreateFrame(frm1, 1, 1, BorderNone, 1)
|
|
btn2 := CreateButton(frm1, AutoSize, AutoSize, "Cancel", Fixed)
|
|
btn2.OnClick(func(ev Event) {
|
|
dlg.result = DialogButton2
|
|
dlg.value = -1
|
|
composer().DestroyWindow(dlg.view)
|
|
if dlg.onClose != nil {
|
|
go dlg.onClose()
|
|
}
|
|
})
|
|
ActivateControl(dlg.view, btn2)
|
|
CreateFrame(frm1, 1, 1, BorderNone, 1)
|
|
|
|
dlg.view.OnClose(func(ev Event) bool {
|
|
if dlg.result == DialogAlive {
|
|
dlg.result = DialogClosed
|
|
if ev.X != 1 {
|
|
composer().DestroyWindow(dlg.view)
|
|
}
|
|
if dlg.onClose != nil {
|
|
go dlg.onClose()
|
|
}
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
return dlg
|
|
}
|
|
|
|
// OnClose sets the callback that is called when the
|
|
// dialog is closed
|
|
func (d *SelectDialog) OnClose(fn func()) {
|
|
d.onClose = fn
|
|
}
|
|
|
|
// Result returns what button closed the dialog.
|
|
// See DialogButton constants. It can equal DialogAlive
|
|
// that means that the dialog is still visible and a
|
|
// user still does not click any button
|
|
func (d *SelectDialog) Result() int {
|
|
return d.result
|
|
}
|
|
|
|
// Value returns the number of the selected item or
|
|
// -1 if nothing is selected or the dialog is cancelled
|
|
func (d *SelectDialog) Value() int {
|
|
return d.value
|
|
}
|