Start of file dialogs

This commit is contained in:
Lea Anthony 2022-12-18 23:06:31 +11:00
commit b1e8f2f887
No known key found for this signature in database
GPG key ID: 33DAF7BB90A58405
7 changed files with 252 additions and 35 deletions

View file

@ -135,6 +135,63 @@ func main() {
dialog.Show()
})
openMenu := menu.AddSubmenu("Open")
openMenu.Add("Open File").OnClick(func(ctx *application.Context) {
result, _ := app.NewOpenFileDialog().
CanChooseFiles(true).
Show()
if result != "" {
app.NewInfoDialog().SetMessage(result).Show()
} else {
app.NewInfoDialog().SetMessage("No file selected").Show()
}
})
openMenu.Add("Open File (Show Hidden Files)").OnClick(func(ctx *application.Context) {
result, _ := app.NewOpenFileDialog().
CanChooseFiles(true).
CanCreateDirectories(true).
ShowHiddenFiles(true).
Show()
if result != "" {
app.NewInfoDialog().SetMessage(result).Show()
} else {
app.NewInfoDialog().SetMessage("No file selected").Show()
}
})
//openMenu.Add("Open Multiple Files (Show Hidden Files)").OnClick(func(ctx *application.Context) {
// result, _ := app.NewOpenMultipleFilesDialog().
// CanChooseFiles(true).
// CanCreateDirectories(true).
// ShowHiddenFiles(true).
// Show()
// if len(result) > 0 {
// app.NewInfoDialog().SetMessage(strings.Join(result, ",")).Show()
// } else {
// app.NewInfoDialog().SetMessage("No file selected").Show()
// }
//})
openMenu.Add("Open Directory").OnClick(func(ctx *application.Context) {
result, _ := app.NewOpenFileDialog().
CanChooseDirectories(true).
Show()
if result != "" {
app.NewInfoDialog().SetMessage(result).Show()
} else {
app.NewInfoDialog().SetMessage("No directory selected").Show()
}
})
openMenu.Add("Open Directory (Create Directories)").OnClick(func(ctx *application.Context) {
result, _ := app.NewOpenFileDialog().
CanChooseDirectories(true).
CanCreateDirectories(true).
Show()
if result != "" {
app.NewInfoDialog().SetMessage(result).Show()
} else {
app.NewInfoDialog().SetMessage("No directory selected").Show()
}
})
app.SetMenu(menu)
app.NewWindow()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -11,12 +11,6 @@ import (
"github.com/wailsapp/wails/exp/pkg/options"
)
//go:embed icon.png
var icon []byte
//go:embed macos_icon.png
var macosIcon []byte
func main() {
app := application.NewWithOptions(&options.Application{
Mac: &options.Mac{
@ -67,9 +61,9 @@ func main() {
mySystray := app.NewSystemTray()
mySystray.SetLabel("Wails")
if runtime.GOOS == "darwin" {
mySystray.SetTemplateIcon(macosIcon)
mySystray.SetTemplateIcon(application.DefaultMacTemplateIcon)
} else {
mySystray.SetIcon(icon)
mySystray.SetIcon(application.DefaultApplicationIcon)
}
myMenu := app.NewMenu()
myMenu.Add("Item 1")
@ -107,9 +101,9 @@ func main() {
mySystray := app.NewSystemTray()
mySystray.SetLabel("Wails is awesome")
if runtime.GOOS == "darwin" {
mySystray.SetTemplateIcon(macosIcon)
mySystray.SetTemplateIcon(application.DefaultMacTemplateIcon)
} else {
mySystray.SetIcon(icon)
mySystray.SetIcon(application.DefaultApplicationIcon)
}
mySystray.SetMenu(myMenu)
mySystray.SetIconPosition(application.NSImageLeading)

View file

@ -87,7 +87,7 @@ type App struct {
// The main application menu
ApplicationMenu *Menu
// About Dialog
// About MessageDialog
name string
description string
icon []byte
@ -321,18 +321,26 @@ func (a *App) ShowAboutDialog() {
}
}
func (a *App) NewInfoDialog() *Dialog {
return newDialog(InfoDialog)
func (a *App) NewInfoDialog() *MessageDialog {
return newMessageDialog(InfoDialog)
}
func (a *App) NewQuestionDialog() *Dialog {
return newDialog(QuestionDialog)
func (a *App) NewQuestionDialog() *MessageDialog {
return newMessageDialog(QuestionDialog)
}
func (a *App) NewWarningDialog() *Dialog {
return newDialog(WarningDialog)
func (a *App) NewWarningDialog() *MessageDialog {
return newMessageDialog(WarningDialog)
}
func (a *App) NewErrorDialog() *Dialog {
return newDialog(ErrorDialog)
func (a *App) NewErrorDialog() *MessageDialog {
return newMessageDialog(ErrorDialog)
}
func (a *App) NewOpenDirectoryDialog() *MessageDialog {
return newMessageDialog(OpenDirectoryDialog)
}
func (a *App) NewOpenFileDialog() *OpenFileDialog {
return newOpenFileDialog()
}

View file

@ -1,12 +1,30 @@
package application
import "C"
import (
"sync"
)
type DialogType int
var dialogID uint
var dialogIDLock sync.RWMutex
func getDialogID() uint {
dialogIDLock.Lock()
defer dialogIDLock.Unlock()
dialogID++
return dialogID
}
var openFileResponses = make(map[uint]chan string)
const (
InfoDialog DialogType = iota
QuestionDialog
WarningDialog
ErrorDialog
OpenDirectoryDialog
)
type Button struct {
@ -20,19 +38,19 @@ func (b *Button) OnClick(callback func()) {
b.callback = callback
}
type dialogImpl interface {
type messageDialogImpl interface {
show()
}
type Dialog struct {
type MessageDialog struct {
dialogType DialogType
title string
message string
buttons []*Button
icon []byte
// platform independent
impl dialogImpl
icon []byte
impl messageDialogImpl
}
var defaultTitles = map[DialogType]string{
@ -42,36 +60,36 @@ var defaultTitles = map[DialogType]string{
ErrorDialog: "Error",
}
func newDialog(dialogType DialogType) *Dialog {
return &Dialog{
func newMessageDialog(dialogType DialogType) *MessageDialog {
return &MessageDialog{
dialogType: dialogType,
title: defaultTitles[dialogType],
}
}
func (d *Dialog) SetTitle(title string) *Dialog {
func (d *MessageDialog) SetTitle(title string) *MessageDialog {
d.title = title
return d
}
func (d *Dialog) SetMessage(message string) *Dialog {
func (d *MessageDialog) SetMessage(message string) *MessageDialog {
d.message = message
return d
}
func (d *Dialog) Show() {
func (d *MessageDialog) Show() {
if d.impl == nil {
d.impl = newDialogImpl(d)
}
d.impl.show()
}
func (d *Dialog) SetIcon(icon []byte) *Dialog {
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog {
d.icon = icon
return d
}
func (d *Dialog) AddButton(s string) *Button {
func (d *MessageDialog) AddButton(s string) *Button {
result := &Button{
label: s,
}
@ -79,7 +97,7 @@ func (d *Dialog) AddButton(s string) *Button {
return result
}
func (d *Dialog) SetDefaultButton(button *Button) *Dialog {
func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog {
for _, b := range d.buttons {
b.isDefault = false
}
@ -87,10 +105,62 @@ func (d *Dialog) SetDefaultButton(button *Button) *Dialog {
return d
}
func (d *Dialog) SetCancelButton(button *Button) *Dialog {
func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog {
for _, b := range d.buttons {
b.isCancel = false
}
button.isCancel = true
return d
}
type openFileDialogImpl interface {
show() ([]string, error)
}
type OpenFileDialog struct {
id uint
canChooseDirectories bool
canChooseFiles bool
canCreateDirectories bool
showHiddenFiles bool
allowsMultipleSelection bool
impl openFileDialogImpl
}
func (d *OpenFileDialog) CanChooseFiles(canChooseFiles bool) *OpenFileDialog {
d.canChooseFiles = canChooseFiles
return d
}
func (d *OpenFileDialog) CanChooseDirectories(canChooseDirectories bool) *OpenFileDialog {
d.canChooseDirectories = canChooseDirectories
return d
}
func (d *OpenFileDialog) CanCreateDirectories(canCreateDirectories bool) *OpenFileDialog {
d.canCreateDirectories = canCreateDirectories
return d
}
func (d *OpenFileDialog) ShowHiddenFiles(showHiddenFiles bool) *OpenFileDialog {
d.showHiddenFiles = showHiddenFiles
return d
}
func (d *OpenFileDialog) Show() (string, error) {
if d.impl == nil {
d.impl = newOpenFileDialogImpl(d)
}
result, err := d.impl.show()
return result[0], err
}
func newOpenFileDialog() *OpenFileDialog {
return &OpenFileDialog{
id: getDialogID(),
canChooseDirectories: false,
canChooseFiles: true,
canCreateDirectories: false,
}
}

View file

@ -8,6 +8,9 @@ package application
#import <Cocoa/Cocoa.h>
extern void openFileDialogCallback(uint id, char* path);
extern void openFileDialogCallbackEnd(uint id);
static void showAboutBox(char* title, char *message, void *icon, int length) {
// run on main thread
@ -99,9 +102,46 @@ static void alertAddButton(void *dialog, char *label, bool isDefault, bool isCan
}
}
static void showOpenFileDialog(unsigned int dialogID, bool canChooseFiles, bool canChooseDirectories, bool canCreateDirectories, bool showHiddenFiles, bool allowsMultipleSelection) {
// run on main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:canChooseFiles];
[panel setCanChooseDirectories:canChooseDirectories];
[panel setCanCreateDirectories:canCreateDirectories];
[panel setShowsHiddenFiles:showHiddenFiles];
[panel setAllowsMultipleSelection:allowsMultipleSelection];
// Show panel
[panel beginWithCompletionHandler:^(NSInteger result) {
const char *path = NULL;
if (result == NSModalResponseOK) {
if (allowsMultipleSelection) {
NSArray *urls = [panel URLs];
for (NSURL *url in urls) {
path = [[url path] UTF8String];
openFileDialogCallback(dialogID, (char *)path);
}
} else {
NSURL *url = [panel URL];
path = [[url path] UTF8String];
openFileDialogCallback(dialogID, (char *)path);
}
}
openFileDialogCallbackEnd(dialogID);
}];
});
}
*/
import "C"
import "unsafe"
import (
"unsafe"
)
const NSAlertStyleWarning = C.int(0)
const NSAlertStyleInformational = C.int(1)
@ -123,7 +163,7 @@ func (m *macosApp) showAboutDialog(title string, message string, icon []byte) {
}
type macosDialog struct {
dialog *Dialog
dialog *MessageDialog
nsDialog unsafe.Pointer
}
@ -188,8 +228,56 @@ func (m *macosDialog) show() {
}
func newDialogImpl(d *Dialog) *macosDialog {
func newDialogImpl(d *MessageDialog) *macosDialog {
return &macosDialog{
dialog: d,
}
}
type macosOpenFileDialog struct {
dialog *OpenFileDialog
}
func newOpenFileDialogImpl(d *OpenFileDialog) *macosOpenFileDialog {
return &macosOpenFileDialog{
dialog: d,
}
}
func (m *macosOpenFileDialog) show() ([]string, error) {
openFileResponses[dialogID] = make(chan string)
C.showOpenFileDialog(C.uint(m.dialog.id),
C.bool(m.dialog.canChooseFiles),
C.bool(m.dialog.canChooseDirectories),
C.bool(m.dialog.canCreateDirectories),
C.bool(m.dialog.showHiddenFiles),
C.bool(m.dialog.allowsMultipleSelection))
var result []string
for filename := range openFileResponses[m.dialog.id] {
result = append(result, filename)
}
return result, nil
}
//export openFileDialogCallback
func openFileDialogCallback(id C.uint, path *C.char) {
// Covert the path to a string
filePath := C.GoString(path)
// put response on channel
channel, ok := openFileResponses[uint(id)]
if ok {
channel <- filePath
} else {
panic("No channel found for open file dialog")
}
}
//export openFileDialogCallbackEnd
func openFileDialogCallbackEnd(id C.uint) {
channel, ok := openFileResponses[uint(id)]
if ok {
close(channel)
} else {
panic("No channel found for open file dialog")
}
}