130 lines
3.7 KiB
Go
130 lines
3.7 KiB
Go
package ui
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
)
|
|
|
|
// A FileSelectorDialog is a WindowContainer with an input and buttons for selecting files.
|
|
// It can be used to open zero or more existing files, or select one non-existant file (for saving).
|
|
type FileSelectorDialog struct {
|
|
Title string
|
|
MustExist bool // Whether the dialog should have a user select an existing file.
|
|
FilesChosenCallback func([]string) // Returns slice of filenames selected. nil if user canceled.
|
|
|
|
tabOrder []Component
|
|
tabOrderIdx int
|
|
|
|
inputField *InputField
|
|
confirmButton *Button
|
|
cancelButton *Button
|
|
|
|
baseComponent
|
|
}
|
|
|
|
func NewFileSelectorDialog(screen *tcell.Screen, title string, mustExist bool, theme *Theme, filesChosenCallback func([]string), cancelCallback func()) *FileSelectorDialog {
|
|
dialog := &FileSelectorDialog{
|
|
Title: title,
|
|
MustExist: mustExist,
|
|
FilesChosenCallback: filesChosenCallback,
|
|
|
|
baseComponent: baseComponent{theme: theme},
|
|
}
|
|
|
|
dialog.inputField = NewInputField(screen, []byte{}, theme.GetOrDefault("Window")) // Use window's theme for InputField
|
|
dialog.confirmButton = NewButton("Confirm", theme, dialog.onConfirm)
|
|
dialog.cancelButton = NewButton("Cancel", theme, cancelCallback)
|
|
dialog.tabOrder = []Component{dialog.inputField, dialog.cancelButton, dialog.confirmButton}
|
|
|
|
return dialog
|
|
}
|
|
|
|
// onConfirm is a callback called by the confirm button.
|
|
func (d *FileSelectorDialog) onConfirm() {
|
|
if d.FilesChosenCallback != nil {
|
|
files := strings.Split(string(d.inputField.Buffer), ",") // Split input by commas
|
|
for i := range files {
|
|
files[i] = strings.TrimSpace(files[i]) // Trim all strings in slice
|
|
}
|
|
d.FilesChosenCallback(files)
|
|
}
|
|
}
|
|
|
|
func (d *FileSelectorDialog) SetCancelCallback(callback func()) {
|
|
d.cancelButton.Callback = callback
|
|
}
|
|
|
|
func (d *FileSelectorDialog) Draw(s tcell.Screen) {
|
|
DrawWindow(s, d.x, d.y, d.width, d.height, d.Title, d.theme)
|
|
|
|
// Update positions of child components (dependent on size information that may not be available at SetPos() )
|
|
btnWidth, _ := d.confirmButton.GetSize()
|
|
d.confirmButton.SetPos(d.x+d.width-btnWidth-1, d.y+4) // Place "Ok" button on right, bottom
|
|
|
|
d.inputField.Draw(s)
|
|
d.confirmButton.Draw(s)
|
|
d.cancelButton.Draw(s)
|
|
}
|
|
|
|
func (d *FileSelectorDialog) SetFocused(v bool) {
|
|
d.focused = v
|
|
d.tabOrder[d.tabOrderIdx].SetFocused(v)
|
|
}
|
|
|
|
func (d *FileSelectorDialog) SetTheme(theme *Theme) {
|
|
d.theme = theme
|
|
d.inputField.SetStyle(theme.GetOrDefault("Window"))
|
|
d.confirmButton.SetTheme(theme)
|
|
d.cancelButton.SetTheme(theme)
|
|
}
|
|
|
|
func (d *FileSelectorDialog) SetPos(x, y int) {
|
|
d.x, d.y = x, y
|
|
d.inputField.SetPos(d.x+1, d.y+2) // Center input field
|
|
d.cancelButton.SetPos(d.x+1, d.y+4) // Place "Cancel" button on left, bottom
|
|
}
|
|
|
|
func (d *FileSelectorDialog) GetMinSize() (int, int) {
|
|
return Max(len(d.Title), 8) + 2, 6
|
|
}
|
|
|
|
func (d *FileSelectorDialog) SetSize(width, height int) {
|
|
minX, minY := d.GetMinSize()
|
|
d.width, d.height = Max(width, minX), Max(height, minY)
|
|
|
|
d.inputField.SetSize(d.width-2, 1)
|
|
d.cancelButton.SetSize(d.cancelButton.GetMinSize())
|
|
d.confirmButton.SetSize(d.confirmButton.GetMinSize())
|
|
}
|
|
|
|
func (d *FileSelectorDialog) HandleEvent(event tcell.Event) bool {
|
|
switch ev := event.(type) {
|
|
case *tcell.EventKey:
|
|
switch ev.Key() {
|
|
case tcell.KeyTab:
|
|
d.tabOrder[d.tabOrderIdx].SetFocused(false)
|
|
|
|
d.tabOrderIdx++
|
|
if d.tabOrderIdx >= len(d.tabOrder) {
|
|
d.tabOrderIdx = 0
|
|
}
|
|
|
|
d.tabOrder[d.tabOrderIdx].SetFocused(true)
|
|
|
|
return true
|
|
case tcell.KeyEsc:
|
|
if d.cancelButton.Callback != nil {
|
|
d.cancelButton.Callback()
|
|
}
|
|
return true
|
|
case tcell.KeyEnter:
|
|
if d.tabOrder[d.tabOrderIdx] == d.inputField {
|
|
d.onConfirm()
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return d.tabOrder[d.tabOrderIdx].HandleEvent(event)
|
|
}
|