clui/dialog.go

324 lines
8.8 KiB
Go
Raw Normal View History

2015-10-21 17:04:57 -07:00
package clui
import (
term "github.com/nsf/termbox-go"
)
2015-10-28 14:17:49 -07:00
// 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
2015-10-22 13:37:38 -07:00
type ConfirmationDialog struct {
2018-03-16 17:48:53 +05:30
View *Window
result int
onClose func()
2015-10-21 17:04:57 -07:00
}
2015-10-28 14:17:49 -07:00
// 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
2015-10-22 14:44:19 -07:00
type SelectDialog struct {
2018-03-14 13:44:32 +05:30
View *Window
2018-03-16 17:48:53 +05:30
result int
2018-03-14 13:44:32 +05:30
value int
2018-03-16 17:48:53 +05:30
edtResult string
2018-03-14 13:44:32 +05:30
rg *RadioGroup
list *ListBox
2018-03-16 17:48:53 +05:30
edit *EditField
2018-03-14 13:44:32 +05:30
typ SelectDialogType
onClose func()
2015-10-22 14:44:19 -07:00
}
2018-01-24 19:21:05 +11:00
// CreateAlertDialog creates a new alert dialog.
// title is a dialog title
// message is a text inside dialog for user to be notified of a fact
// button is a title for button inside dialog.
func CreateAlertDialog(title, message string, button string) *ConfirmationDialog {
return CreateConfirmationDialog(title, message, []string{button}, 0)
}
// CreateConfirmationDialog creates new confirmation dialog.
2015-10-28 14:17:49 -07:00
// 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 {
2015-10-22 13:37:38 -07:00
dlg := new(ConfirmationDialog)
if len(buttons) == 0 {
buttons = []string{"OK"}
}
2015-10-21 17:04:57 -07:00
cw, ch := term.Size()
2018-01-24 19:23:00 +11:00
dlg.View = AddWindow(cw/2-12, ch/2-8, 30, 3, title)
2018-01-25 21:49:43 -08:00
WindowManager().BeginUpdate()
defer WindowManager().EndUpdate()
2018-01-24 19:23:00 +11:00
dlg.View.SetConstraints(30, 3)
dlg.View.SetModal(true)
dlg.View.SetPack(Vertical)
CreateFrame(dlg.View, 1, 1, BorderNone, Fixed)
2015-10-21 17:04:57 -07:00
2018-01-24 19:23:00 +11:00
fbtn := CreateFrame(dlg.View, 1, 1, BorderNone, 1)
CreateFrame(fbtn, 1, 1, BorderNone, Fixed)
lb := CreateLabel(fbtn, 10, 3, question, 1)
2015-10-21 17:04:57 -07:00
lb.SetMultiline(true)
CreateFrame(fbtn, 1, 1, BorderNone, Fixed)
2015-10-21 17:04:57 -07:00
2018-01-24 19:23:00 +11:00
CreateFrame(dlg.View, 1, 1, BorderNone, Fixed)
frm1 := CreateFrame(dlg.View, 16, 4, BorderNone, Fixed)
CreateFrame(frm1, 1, 1, BorderNone, 1)
2015-10-22 13:37:38 -07:00
bText := buttons[0]
btn1 := CreateButton(frm1, AutoSize, AutoSize, bText, Fixed)
2015-10-22 13:37:38 -07:00
btn1.OnClick(func(ev Event) {
2018-03-16 17:48:53 +05:30
dlg.result = DialogButton1
2018-01-24 19:23:00 +11:00
WindowManager().DestroyWindow(dlg.View)
2018-01-25 21:49:43 -08:00
WindowManager().BeginUpdate()
closeFunc := dlg.onClose
WindowManager().EndUpdate()
if closeFunc != nil {
closeFunc()
2015-10-22 13:37:38 -07:00
}
})
var btn2, btn3 *Button
if len(buttons) > 1 {
CreateFrame(frm1, 1, 1, BorderNone, 1)
btn2 = CreateButton(frm1, AutoSize, AutoSize, buttons[1], Fixed)
2015-10-22 13:37:38 -07:00
btn2.OnClick(func(ev Event) {
2018-03-16 17:48:53 +05:30
dlg.result = DialogButton2
2018-01-24 19:23:00 +11:00
WindowManager().DestroyWindow(dlg.View)
2015-10-22 13:37:38 -07:00
if dlg.onClose != nil {
dlg.onClose()
2015-10-22 13:37:38 -07:00
}
})
2015-10-21 17:04:57 -07:00
}
2015-10-22 13:37:38 -07:00
if len(buttons) > 2 {
CreateFrame(frm1, 1, 1, BorderNone, 1)
btn3 = CreateButton(frm1, AutoSize, AutoSize, buttons[2], Fixed)
2015-10-22 13:37:38 -07:00
btn3.OnClick(func(ev Event) {
2018-03-16 17:48:53 +05:30
dlg.result = DialogButton3
2018-01-24 19:23:00 +11:00
WindowManager().DestroyWindow(dlg.View)
2015-10-22 13:37:38 -07:00
if dlg.onClose != nil {
dlg.onClose()
2015-10-22 13:37:38 -07:00
}
})
}
CreateFrame(frm1, 1, 1, BorderNone, 1)
2015-10-21 17:04:57 -07:00
2018-03-16 17:48:53 +05:30
if defaultButton == DialogButton2 && len(buttons) > 1 {
2018-01-24 19:23:00 +11:00
ActivateControl(dlg.View, btn2)
} else if defaultButton == DialogButton3 && len(buttons) > 2 {
2018-01-24 19:23:00 +11:00
ActivateControl(dlg.View, btn3)
2018-01-25 21:49:43 -08:00
} else {
2018-01-24 19:23:00 +11:00
ActivateControl(dlg.View, btn1)
2018-01-25 21:49:43 -08:00
}
2018-01-24 19:23:00 +11:00
dlg.View.OnClose(func(ev Event) bool {
2018-03-16 17:48:53 +05:30
if dlg.result == DialogAlive {
dlg.result = DialogClosed
2015-10-22 14:44:19 -07:00
if ev.X != 1 {
2018-01-24 19:23:00 +11:00
WindowManager().DestroyWindow(dlg.View)
2015-10-22 14:44:19 -07:00
}
2015-10-21 17:04:57 -07:00
if dlg.onClose != nil {
dlg.onClose()
2015-10-21 17:04:57 -07:00
}
}
return true
2015-10-21 17:04:57 -07:00
})
return dlg
}
2015-10-28 14:17:49 -07:00
// OnClose sets the callback that is called when the
// dialog is closed
2015-10-22 13:37:38 -07:00
func (d *ConfirmationDialog) OnClose(fn func()) {
2018-01-25 21:49:43 -08:00
WindowManager().BeginUpdate()
defer WindowManager().EndUpdate()
2015-10-21 17:04:57 -07:00
d.onClose = fn
}
2015-10-28 14:17:49 -07:00
// 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
2015-10-22 13:37:38 -07:00
func (d *ConfirmationDialog) Result() int {
2018-03-16 17:48:53 +05:30
return d.result
2015-10-21 17:04:57 -07:00
}
2015-10-22 14:44:19 -07:00
// ------------------------ Selection Dialog ---------------------
2018-03-16 17:48:53 +05:30
func CreateEditDialog(title, message, initialText string) *SelectDialog {
return CreateSelectDialog(title, []string{message, initialText}, 0, SelectDialogEdit)
}
2015-10-28 14:17:49 -07:00
// 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 {
2015-10-22 14:44:19 -07:00
dlg := new(SelectDialog)
if len(items) == 0 {
// Item list must contain at least 1 item
return nil
2015-10-22 14:44:19 -07:00
}
cw, ch := term.Size()
dlg.typ = typ
2018-01-24 19:23:00 +11:00
dlg.View = AddWindow(cw/2-12, ch/2-8, 20, 10, title)
2018-01-25 21:49:43 -08:00
WindowManager().BeginUpdate()
defer WindowManager().EndUpdate()
2018-01-24 19:23:00 +11:00
dlg.View.SetModal(true)
dlg.View.SetPack(Vertical)
2015-10-22 14:44:19 -07:00
if typ == SelectDialogList {
2018-01-24 19:23:00 +11:00
fList := CreateFrame(dlg.View, 1, 1, BorderNone, 1)
fList.SetPaddings(1, 1)
fList.SetGaps(0, 0)
dlg.list = CreateListBox(fList, 15, 5, 1)
2015-10-22 14:44:19 -07:00
for _, item := range items {
dlg.list.AddItem(item)
}
if selectedItem >= 0 && selectedItem < len(items) {
dlg.list.SelectItem(selectedItem)
}
2018-03-16 17:48:53 +05:30
} else if typ == SelectDialogEdit {
CreateFrame(dlg.View, 1, 1, BorderNone, Fixed)
lb := CreateLabel(dlg.View, 10, 3, items[0], 1)
lb.SetMultiline(true)
fWidth, _ := dlg.View.Size()
dlg.edit = CreateEditField(dlg.View, fWidth-2, items[1], AutoSize)
CreateFrame(dlg.View, 1, 1, BorderNone, Fixed)
2018-04-07 14:23:36 -07:00
dlg.edit.OnKeyPress(func(key term.Key, r rune) bool {
2018-03-16 17:48:53 +05:30
var input string
if key == term.KeyEnter {
input = dlg.edit.Title()
dlg.edtResult = input
dlg.value = -1
dlg.result = DialogButton1
WindowManager().DestroyWindow(dlg.View)
if dlg.onClose != nil {
dlg.onClose()
}
}
// returning false so that other keypresses work as usual
return false
})
2015-10-22 14:44:19 -07:00
} else {
2018-01-24 19:23:00 +11:00
fRadio := CreateFrame(dlg.View, 1, 1, BorderNone, Fixed)
fRadio.SetPaddings(1, 1)
fRadio.SetGaps(0, 0)
2015-10-22 14:44:19 -07:00
fRadio.SetPack(Vertical)
dlg.rg = CreateRadioGroup()
2015-10-22 14:44:19 -07:00
for _, item := range items {
r := CreateRadio(fRadio, AutoSize, item, Fixed)
2015-10-22 14:44:19 -07:00
dlg.rg.AddItem(r)
}
if selectedItem >= 0 && selectedItem < len(items) {
dlg.rg.SetSelected(selectedItem)
}
}
2018-01-24 19:23:00 +11:00
frm1 := CreateFrame(dlg.View, 16, 4, BorderNone, Fixed)
CreateFrame(frm1, 1, 1, BorderNone, 1)
btn1 := CreateButton(frm1, AutoSize, AutoSize, "OK", Fixed)
2015-10-22 14:44:19 -07:00
btn1.OnClick(func(ev Event) {
2018-03-16 17:48:53 +05:30
dlg.result = DialogButton1
2015-10-22 14:44:19 -07:00
if dlg.typ == SelectDialogList {
dlg.value = dlg.list.SelectedItem()
2018-03-16 17:48:53 +05:30
} else if dlg.typ == SelectDialogEdit {
dlg.edtResult = dlg.edit.Title()
dlg.value = -1
2015-10-22 14:44:19 -07:00
} else {
dlg.value = dlg.rg.Selected()
}
2018-01-24 19:23:00 +11:00
WindowManager().DestroyWindow(dlg.View)
2015-10-22 14:44:19 -07:00
if dlg.onClose != nil {
dlg.onClose()
2015-10-22 14:44:19 -07:00
}
})
CreateFrame(frm1, 1, 1, BorderNone, 1)
btn2 := CreateButton(frm1, AutoSize, AutoSize, "Cancel", Fixed)
2015-10-22 14:44:19 -07:00
btn2.OnClick(func(ev Event) {
2018-03-16 17:48:53 +05:30
dlg.result = DialogButton2
dlg.edtResult = ""
2015-10-22 14:44:19 -07:00
dlg.value = -1
2018-01-24 19:23:00 +11:00
WindowManager().DestroyWindow(dlg.View)
2015-10-22 14:44:19 -07:00
if dlg.onClose != nil {
dlg.onClose()
2015-10-22 14:44:19 -07:00
}
})
2018-04-07 14:23:36 -07:00
if typ == SelectDialogEdit {
ActivateControl(dlg.View, dlg.edit)
} else {
ActivateControl(dlg.View, btn2)
}
CreateFrame(frm1, 1, 1, BorderNone, 1)
2015-10-22 14:44:19 -07:00
2018-01-24 19:23:00 +11:00
dlg.View.OnClose(func(ev Event) bool {
2018-03-16 17:48:53 +05:30
if dlg.result == DialogAlive {
dlg.result = DialogClosed
2015-10-22 14:44:19 -07:00
if ev.X != 1 {
2018-01-24 19:23:00 +11:00
WindowManager().DestroyWindow(dlg.View)
2015-10-22 14:44:19 -07:00
}
if dlg.onClose != nil {
dlg.onClose()
2015-10-22 14:44:19 -07:00
}
}
return true
2015-10-22 14:44:19 -07:00
})
return dlg
}
2015-10-28 14:17:49 -07:00
// OnClose sets the callback that is called when the
// dialog is closed
2015-10-22 14:44:19 -07:00
func (d *SelectDialog) OnClose(fn func()) {
2018-01-25 21:49:43 -08:00
WindowManager().BeginUpdate()
defer WindowManager().EndUpdate()
2015-10-22 14:44:19 -07:00
d.onClose = fn
}
2015-10-28 14:17:49 -07:00
// 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
2015-10-22 14:44:19 -07:00
func (d *SelectDialog) Result() int {
2018-03-16 17:48:53 +05:30
return d.result
2015-10-22 14:44:19 -07:00
}
2015-10-28 14:17:49 -07:00
// Value returns the number of the selected item or
// -1 if nothing is selected or the dialog is cancelled
2015-10-22 14:44:19 -07:00
func (d *SelectDialog) Value() int {
return d.value
}
2018-03-16 17:48:53 +05:30
// EditResult returns the text from editfield
func (d *SelectDialog) EditResult() string {
return d.edtResult
}