Files

496 lines
12 KiB
Go
Raw Permalink Normal View History

2023-01-18 21:42:49 +11:00
package application
import (
2023-09-29 23:54:57 -05:00
"fmt"
2023-01-18 21:42:49 +11:00
"strings"
"sync"
)
type DialogType int
var dialogMapID = make(map[uint]struct{})
var dialogIDLock sync.RWMutex
func getDialogID() uint {
dialogIDLock.Lock()
defer dialogIDLock.Unlock()
var dialogID uint
for {
if _, ok := dialogMapID[dialogID]; !ok {
dialogMapID[dialogID] = struct{}{}
break
}
dialogID++
if dialogID == 0 {
panic("no more dialog IDs")
}
}
return dialogID
}
func freeDialogID(id uint) {
dialogIDLock.Lock()
defer dialogIDLock.Unlock()
delete(dialogMapID, id)
}
var openFileResponses = make(map[uint]chan string)
var saveFileResponses = make(map[uint]chan string)
const (
2023-06-24 13:57:51 +10:00
InfoDialogType DialogType = iota
QuestionDialogType
WarningDialogType
ErrorDialogType
OpenDirectoryDialogType
2023-01-18 21:42:49 +11:00
)
type Button struct {
2023-02-06 20:50:11 +11:00
Label string
IsCancel bool
IsDefault bool
Callback func()
2023-01-18 21:42:49 +11:00
}
func (b *Button) OnClick(callback func()) *Button {
b.Callback = callback
return b
}
func (b *Button) SetAsDefault() *Button {
b.IsDefault = true
return b
}
func (b *Button) SetAsCancel() *Button {
b.IsCancel = true
return b
2023-01-18 21:42:49 +11:00
}
type messageDialogImpl interface {
show()
}
2023-02-06 20:50:11 +11:00
type MessageDialogOptions struct {
DialogType DialogType
Title string
Message string
Buttons []*Button
Icon []byte
2023-09-28 17:10:17 -05:00
window *WebviewWindow
2023-02-06 20:50:11 +11:00
}
2023-01-18 21:42:49 +11:00
type MessageDialog struct {
2023-02-06 20:50:11 +11:00
MessageDialogOptions
2023-01-18 21:42:49 +11:00
// platform independent
impl messageDialogImpl
}
var defaultTitles = map[DialogType]string{
2023-06-24 13:57:51 +10:00
InfoDialogType: "Information",
QuestionDialogType: "Question",
WarningDialogType: "Warning",
ErrorDialogType: "Error",
2023-01-18 21:42:49 +11:00
}
func newMessageDialog(dialogType DialogType) *MessageDialog {
return &MessageDialog{
2023-02-06 20:50:11 +11:00
MessageDialogOptions: MessageDialogOptions{
DialogType: dialogType,
},
impl: nil,
2023-01-18 21:42:49 +11:00
}
}
func (d *MessageDialog) SetTitle(title string) *MessageDialog {
2023-02-06 20:50:11 +11:00
d.Title = title
2023-01-18 21:42:49 +11:00
return d
}
func (d *MessageDialog) Show() {
if d.impl == nil {
d.impl = newDialogImpl(d)
}
2023-09-24 08:57:22 +10:00
InvokeSync(d.impl.show)
2023-01-18 21:42:49 +11:00
}
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog {
2023-02-06 20:50:11 +11:00
d.Icon = icon
2023-01-18 21:42:49 +11:00
return d
}
func (d *MessageDialog) AddButton(s string) *Button {
result := &Button{
2023-02-06 20:50:11 +11:00
Label: s,
2023-01-18 21:42:49 +11:00
}
2023-02-06 20:50:11 +11:00
d.Buttons = append(d.Buttons, result)
2023-01-18 21:42:49 +11:00
return result
}
2023-02-06 20:50:11 +11:00
func (d *MessageDialog) AddButtons(buttons []*Button) *MessageDialog {
d.Buttons = buttons
return d
}
2023-09-28 11:38:59 -05:00
func (d *MessageDialog) AttachToWindow(window Window) *MessageDialog {
2023-09-28 17:10:17 -05:00
d.window = window.(*WebviewWindow)
return d
}
2023-01-18 21:42:49 +11:00
func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog {
2023-02-06 20:50:11 +11:00
for _, b := range d.Buttons {
b.IsDefault = false
2023-01-18 21:42:49 +11:00
}
2023-02-06 20:50:11 +11:00
button.IsDefault = true
2023-01-18 21:42:49 +11:00
return d
}
func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog {
2023-02-06 20:50:11 +11:00
for _, b := range d.Buttons {
b.IsCancel = false
2023-01-18 21:42:49 +11:00
}
2023-02-06 20:50:11 +11:00
button.IsCancel = true
2023-01-18 21:42:49 +11:00
return d
}
2023-02-06 20:50:11 +11:00
func (d *MessageDialog) SetMessage(message string) *MessageDialog {
d.Message = message
2023-01-18 21:42:49 +11:00
return d
}
type openFileDialogImpl interface {
2023-09-29 23:54:57 -05:00
show() (chan string, error)
2023-01-18 21:42:49 +11:00
}
2023-02-06 20:50:11 +11:00
type FileFilter struct {
DisplayName string // Filter information EG: "Image Files (*.jpg, *.png)"
Pattern string // semicolon separated list of extensions, EG: "*.jpg;*.png"
}
type OpenFileDialogOptions struct {
CanChooseDirectories bool
CanChooseFiles bool
CanCreateDirectories bool
ShowHiddenFiles bool
ResolvesAliases bool
AllowsMultipleSelection bool
HideExtension bool
CanSelectHiddenExtension bool
TreatsFilePackagesAsDirectories bool
AllowsOtherFileTypes bool
Filters []FileFilter
2023-09-29 11:29:31 -05:00
Window *WebviewWindow
2023-02-06 20:50:11 +11:00
Title string
Message string
ButtonText string
Directory string
2023-01-18 21:42:49 +11:00
}
2023-06-24 13:57:51 +10:00
type OpenFileDialogStruct struct {
2023-01-18 21:42:49 +11:00
id uint
canChooseDirectories bool
canChooseFiles bool
canCreateDirectories bool
showHiddenFiles bool
resolvesAliases bool
allowsMultipleSelection bool
hideExtension bool
canSelectHiddenExtension bool
treatsFilePackagesAsDirectories bool
allowsOtherFileTypes bool
2023-02-06 20:50:11 +11:00
filters []FileFilter
2023-01-18 21:42:49 +11:00
title string
message string
buttonText string
directory string
2023-09-29 11:29:31 -05:00
window *WebviewWindow
2023-01-18 21:42:49 +11:00
impl openFileDialogImpl
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) CanChooseFiles(canChooseFiles bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.canChooseFiles = canChooseFiles
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) CanChooseDirectories(canChooseDirectories bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.canChooseDirectories = canChooseDirectories
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) CanCreateDirectories(canCreateDirectories bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.canCreateDirectories = canCreateDirectories
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) AllowsOtherFileTypes(allowsOtherFileTypes bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.allowsOtherFileTypes = allowsOtherFileTypes
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) ShowHiddenFiles(showHiddenFiles bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.showHiddenFiles = showHiddenFiles
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) HideExtension(hideExtension bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.hideExtension = hideExtension
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) TreatsFilePackagesAsDirectories(treatsFilePackagesAsDirectories bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.treatsFilePackagesAsDirectories = treatsFilePackagesAsDirectories
return d
}
2023-09-28 11:38:59 -05:00
func (d *OpenFileDialogStruct) AttachToWindow(window Window) *OpenFileDialogStruct {
2023-09-29 11:29:31 -05:00
d.window = window.(*WebviewWindow)
2023-01-18 21:42:49 +11:00
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) ResolvesAliases(resolvesAliases bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.resolvesAliases = resolvesAliases
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) SetTitle(title string) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.title = title
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) PromptForSingleSelection() (string, error) {
2023-01-18 21:42:49 +11:00
d.allowsMultipleSelection = false
if d.impl == nil {
d.impl = newOpenFileDialogImpl(d)
}
2023-09-29 23:54:57 -05:00
2023-01-18 21:42:49 +11:00
var result string
2023-09-29 23:54:57 -05:00
selections, err := InvokeSyncWithResultAndError(d.impl.show)
if err == nil {
result = <-selections
2023-01-18 21:42:49 +11:00
}
return result, err
}
// AddFilter adds a filter to the dialog. The filter is a display name and a semicolon separated list of extensions.
// EG: AddFilter("Image Files", "*.jpg;*.png")
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) AddFilter(displayName, pattern string) *OpenFileDialogStruct {
2023-02-06 20:50:11 +11:00
d.filters = append(d.filters, FileFilter{
DisplayName: strings.TrimSpace(displayName),
Pattern: strings.TrimSpace(pattern),
2023-01-18 21:42:49 +11:00
})
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) PromptForMultipleSelection() ([]string, error) {
2023-01-18 21:42:49 +11:00
d.allowsMultipleSelection = true
if d.impl == nil {
d.impl = newOpenFileDialogImpl(d)
}
2023-09-29 23:54:57 -05:00
selections, err := InvokeSyncWithResultAndError(d.impl.show)
var result []string
fmt.Println("Waiting for results:")
for filename := range selections {
fmt.Println(filename)
result = append(result, filename)
}
return result, err
2023-01-18 21:42:49 +11:00
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) SetMessage(message string) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.message = message
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) SetButtonText(text string) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.buttonText = text
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) SetDirectory(directory string) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.directory = directory
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) CanSelectHiddenExtension(canSelectHiddenExtension bool) *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.canSelectHiddenExtension = canSelectHiddenExtension
return d
}
2023-06-24 13:57:51 +10:00
func (d *OpenFileDialogStruct) SetOptions(options *OpenFileDialogOptions) {
2023-02-06 20:50:11 +11:00
d.title = options.Title
d.message = options.Message
d.buttonText = options.ButtonText
d.directory = options.Directory
d.canChooseDirectories = options.CanChooseDirectories
d.canChooseFiles = options.CanChooseFiles
d.canCreateDirectories = options.CanCreateDirectories
d.showHiddenFiles = options.ShowHiddenFiles
d.resolvesAliases = options.ResolvesAliases
d.allowsMultipleSelection = options.AllowsMultipleSelection
d.hideExtension = options.HideExtension
d.canSelectHiddenExtension = options.CanSelectHiddenExtension
d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories
d.allowsOtherFileTypes = options.AllowsOtherFileTypes
d.filters = options.Filters
d.window = options.Window
2023-02-06 20:50:11 +11:00
}
2023-06-24 13:57:51 +10:00
func newOpenFileDialog() *OpenFileDialogStruct {
return &OpenFileDialogStruct{
2023-01-18 21:42:49 +11:00
id: getDialogID(),
canChooseDirectories: false,
canChooseFiles: true,
canCreateDirectories: true,
resolvesAliases: false,
}
}
2023-06-24 13:57:51 +10:00
func newSaveFileDialog() *SaveFileDialogStruct {
return &SaveFileDialogStruct{
2023-01-18 21:42:49 +11:00
id: getDialogID(),
canCreateDirectories: true,
}
}
2023-02-06 20:50:11 +11:00
type SaveFileDialogOptions struct {
CanCreateDirectories bool
ShowHiddenFiles bool
CanSelectHiddenExtension bool
AllowOtherFileTypes bool
HideExtension bool
TreatsFilePackagesAsDirectories bool
Title string
2023-02-06 20:50:11 +11:00
Message string
Directory string
Filename string
ButtonText string
Filters []FileFilter
2023-09-29 12:13:51 -05:00
Window *WebviewWindow
2023-02-06 20:50:11 +11:00
}
2023-06-24 13:57:51 +10:00
type SaveFileDialogStruct struct {
2023-01-18 21:42:49 +11:00
id uint
canCreateDirectories bool
showHiddenFiles bool
canSelectHiddenExtension bool
allowOtherFileTypes bool
hideExtension bool
treatsFilePackagesAsDirectories bool
message string
directory string
filename string
buttonText string
filters []FileFilter
2023-01-18 21:42:49 +11:00
2023-09-29 12:13:51 -05:00
window *WebviewWindow
2023-01-18 21:42:49 +11:00
impl saveFileDialogImpl
title string
2023-01-18 21:42:49 +11:00
}
type saveFileDialogImpl interface {
2023-09-29 23:54:57 -05:00
show() (chan string, error)
2023-01-18 21:42:49 +11:00
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) SetOptions(options *SaveFileDialogOptions) {
d.title = options.Title
2023-02-06 20:50:11 +11:00
d.canCreateDirectories = options.CanCreateDirectories
d.showHiddenFiles = options.ShowHiddenFiles
d.canSelectHiddenExtension = options.CanSelectHiddenExtension
d.allowOtherFileTypes = options.AllowOtherFileTypes
d.hideExtension = options.HideExtension
d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories
d.message = options.Message
d.directory = options.Directory
d.filename = options.Filename
d.buttonText = options.ButtonText
d.filters = options.Filters
d.window = options.Window
2023-02-06 20:50:11 +11:00
}
// AddFilter adds a filter to the dialog. The filter is a display name and a semicolon separated list of extensions.
// EG: AddFilter("Image Files", "*.jpg;*.png")
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) AddFilter(displayName, pattern string) *SaveFileDialogStruct {
d.filters = append(d.filters, FileFilter{
DisplayName: strings.TrimSpace(displayName),
Pattern: strings.TrimSpace(pattern),
})
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) CanCreateDirectories(canCreateDirectories bool) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.canCreateDirectories = canCreateDirectories
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) CanSelectHiddenExtension(canSelectHiddenExtension bool) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.canSelectHiddenExtension = canSelectHiddenExtension
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) ShowHiddenFiles(showHiddenFiles bool) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.showHiddenFiles = showHiddenFiles
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) SetMessage(message string) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.message = message
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) SetDirectory(directory string) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.directory = directory
return d
}
2023-09-28 11:38:59 -05:00
func (d *SaveFileDialogStruct) AttachToWindow(window Window) *SaveFileDialogStruct {
2023-09-29 12:13:51 -05:00
d.window = window.(*WebviewWindow)
2023-01-18 21:42:49 +11:00
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) PromptForSingleSelection() (string, error) {
2023-01-18 21:42:49 +11:00
if d.impl == nil {
d.impl = newSaveFileDialogImpl(d)
}
2023-09-29 23:54:57 -05:00
var result string
selections, err := InvokeSyncWithResultAndError(d.impl.show)
if err == nil {
result = <-selections
}
return result, err
2023-01-18 21:42:49 +11:00
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) SetButtonText(text string) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.buttonText = text
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) SetFilename(filename string) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.filename = filename
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) AllowsOtherFileTypes(allowOtherFileTypes bool) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.allowOtherFileTypes = allowOtherFileTypes
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) HideExtension(hideExtension bool) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.hideExtension = hideExtension
return d
}
2023-06-24 13:57:51 +10:00
func (d *SaveFileDialogStruct) TreatsFilePackagesAsDirectories(treatsFilePackagesAsDirectories bool) *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
d.treatsFilePackagesAsDirectories = treatsFilePackagesAsDirectories
return d
}