5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-04 07:29:56 +08:00
wails/v3/pkg/application/dialogs_windows.go
2025-01-16 22:08:18 +11:00

254 lines
5.9 KiB
Go

//go:build windows
package application
import (
"path/filepath"
"strings"
"github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd"
"github.com/wailsapp/wails/v3/pkg/w32"
"golang.org/x/sys/windows"
)
func (m *windowsApp) showAboutDialog(title string, message string, _ []byte) {
about := newDialogImpl(&MessageDialog{
MessageDialogOptions: MessageDialogOptions{
DialogType: InfoDialogType,
Title: title,
Message: message,
},
})
about.UseAppIcon = true
about.show()
}
type windowsDialog struct {
dialog *MessageDialog
//dialogImpl unsafe.Pointer
UseAppIcon bool
}
func (m *windowsDialog) show() {
title := w32.MustStringToUTF16Ptr(m.dialog.Title)
message := w32.MustStringToUTF16Ptr(m.dialog.Message)
flags := calculateMessageDialogFlags(m.dialog.MessageDialogOptions)
var button int32
var parentWindow uintptr
var err error
if m.dialog.window != nil {
parentWindow, err = m.dialog.window.NativeWindowHandle()
if err != nil {
globalApplication.fatal(err.Error())
}
}
if m.UseAppIcon || m.dialog.Icon != nil {
// 3 is the application icon
button, err = w32.MessageBoxWithIcon(parentWindow, message, title, 3, windows.MB_OK|windows.MB_USERICON)
if err != nil {
globalApplication.fatal(err.Error())
}
} else {
button, err = windows.MessageBox(windows.HWND(parentWindow), message, title, flags|windows.MB_SYSTEMMODAL)
if err != nil {
globalApplication.fatal(err.Error())
}
}
// This maps MessageBox return values to strings
responses := []string{"", "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "", "", "Try Again", "Continue"}
result := "Error"
if int(button) < len(responses) {
result = responses[button]
}
// Check if there's a callback for the button pressed
for _, buttonInDialog := range m.dialog.Buttons {
if buttonInDialog.Label == result {
if buttonInDialog.Callback != nil {
buttonInDialog.Callback()
}
}
}
}
func newDialogImpl(d *MessageDialog) *windowsDialog {
return &windowsDialog{
dialog: d,
}
}
type windowOpenFileDialog struct {
dialog *OpenFileDialogStruct
}
func newOpenFileDialogImpl(d *OpenFileDialogStruct) *windowOpenFileDialog {
return &windowOpenFileDialog{
dialog: d,
}
}
func getDefaultFolder(folder string) (string, error) {
if folder == "" {
return "", nil
}
return filepath.Abs(folder)
}
func (m *windowOpenFileDialog) show() (chan string, error) {
defaultFolder, err := getDefaultFolder(m.dialog.directory)
if err != nil {
return nil, err
}
config := cfd.DialogConfig{
Title: m.dialog.title,
Role: "PickFolder",
FileFilters: convertFilters(m.dialog.filters),
Folder: defaultFolder,
}
if m.dialog.window != nil {
config.ParentWindowHandle, err = m.dialog.window.NativeWindowHandle()
if err != nil {
globalApplication.fatal(err.Error())
}
}
var result []string
if m.dialog.allowsMultipleSelection && !m.dialog.canChooseDirectories {
temp, err := showCfdDialog(
func() (cfd.Dialog, error) {
return cfd.NewOpenMultipleFilesDialog(config)
}, true)
if err != nil {
return nil, err
}
result = temp.([]string)
} else {
if m.dialog.canChooseDirectories {
temp, err := showCfdDialog(
func() (cfd.Dialog, error) {
return cfd.NewSelectFolderDialog(config)
}, false)
if err != nil {
return nil, err
}
result = []string{temp.(string)}
} else {
temp, err := showCfdDialog(
func() (cfd.Dialog, error) {
return cfd.NewOpenFileDialog(config)
}, false)
if err != nil {
return nil, err
}
result = []string{temp.(string)}
}
}
files := make(chan string)
go func() {
defer handlePanic()
for _, file := range result {
files <- file
}
close(files)
}()
return files, nil
}
type windowSaveFileDialog struct {
dialog *SaveFileDialogStruct
}
func newSaveFileDialogImpl(d *SaveFileDialogStruct) *windowSaveFileDialog {
return &windowSaveFileDialog{
dialog: d,
}
}
func (m *windowSaveFileDialog) show() (chan string, error) {
files := make(chan string)
defaultFolder, err := getDefaultFolder(m.dialog.directory)
if err != nil {
close(files)
return files, err
}
config := cfd.DialogConfig{
Title: m.dialog.title,
Role: "SaveFile",
FileFilters: convertFilters(m.dialog.filters),
FileName: m.dialog.filename,
Folder: defaultFolder,
}
// Original PR for v2 by @almas1992: https://github.com/wailsapp/wails/pull/3205
if len(m.dialog.filters) > 0 {
config.DefaultExtension = strings.TrimPrefix(strings.Split(m.dialog.filters[0].Pattern, ";")[0], "*")
}
result, err := showCfdDialog(
func() (cfd.Dialog, error) {
return cfd.NewSaveFileDialog(config)
}, false)
go func() {
defer handlePanic()
files <- result.(string)
close(files)
}()
return files, err
}
func calculateMessageDialogFlags(options MessageDialogOptions) uint32 {
var flags uint32
switch options.DialogType {
case InfoDialogType:
flags = windows.MB_OK | windows.MB_ICONINFORMATION
case ErrorDialogType:
flags = windows.MB_ICONERROR | windows.MB_OK
case QuestionDialogType:
flags = windows.MB_YESNO
for _, button := range options.Buttons {
if strings.TrimSpace(strings.ToLower(button.Label)) == "no" && button.IsDefault {
flags |= windows.MB_DEFBUTTON2
}
}
case WarningDialogType:
flags = windows.MB_OK | windows.MB_ICONWARNING
}
return flags
}
func convertFilters(filters []FileFilter) []cfd.FileFilter {
var result []cfd.FileFilter
for _, filter := range filters {
result = append(result, cfd.FileFilter(filter))
}
return result
}
func showCfdDialog(newDlg func() (cfd.Dialog, error), isMultiSelect bool) (any, error) {
dlg, err := newDlg()
if err != nil {
return nil, err
}
defer func() {
err := dlg.Release()
if err != nil {
globalApplication.error("Unable to release dialog: " + err.Error())
}
}()
if multi, _ := dlg.(cfd.OpenMultipleFilesDialog); multi != nil && isMultiSelect {
return multi.ShowAndGetResults()
}
return dlg.ShowAndGetResult()
}