github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/darwin/dialog.go (about)

     1  //go:build darwin
     2  // +build darwin
     3  
     4  package darwin
     5  
     6  /*
     7  #cgo CFLAGS: -x objective-c
     8  #cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
     9  #import <Foundation/Foundation.h>
    10  #import "Application.h"
    11  #import "WailsContext.h"
    12  */
    13  import "C"
    14  
    15  import (
    16  	"encoding/json"
    17  	"fmt"
    18  	"strings"
    19  	"sync"
    20  	"unsafe"
    21  
    22  	"github.com/leaanthony/slicer"
    23  	"github.com/secoba/wails/v2/internal/frontend"
    24  )
    25  
    26  // Obj-C dialog methods send the response to this channel
    27  var (
    28  	messageDialogResponse  = make(chan int)
    29  	openFileDialogResponse = make(chan string)
    30  	saveFileDialogResponse = make(chan string)
    31  	dialogLock             sync.Mutex
    32  )
    33  
    34  // OpenDirectoryDialog prompts the user to select a directory
    35  func (f *Frontend) OpenDirectoryDialog(options frontend.OpenDialogOptions) (string, error) {
    36  	results, err := f.openDialog(&options, false, false, true)
    37  	if err != nil {
    38  		return "", err
    39  	}
    40  	var selected string
    41  	if len(results) > 0 {
    42  		selected = results[0]
    43  	}
    44  	return selected, nil
    45  }
    46  
    47  func (f *Frontend) openDialog(options *frontend.OpenDialogOptions, multiple bool, allowfiles bool, allowdirectories bool) ([]string, error) {
    48  	dialogLock.Lock()
    49  	defer dialogLock.Unlock()
    50  
    51  	c := NewCalloc()
    52  	defer c.Free()
    53  	title := c.String(options.Title)
    54  	defaultFilename := c.String(options.DefaultFilename)
    55  	defaultDirectory := c.String(options.DefaultDirectory)
    56  	allowDirectories := bool2Cint(allowdirectories)
    57  	allowFiles := bool2Cint(allowfiles)
    58  	canCreateDirectories := bool2Cint(options.CanCreateDirectories)
    59  	treatPackagesAsDirectories := bool2Cint(options.TreatPackagesAsDirectories)
    60  	resolveAliases := bool2Cint(options.ResolvesAliases)
    61  	showHiddenFiles := bool2Cint(options.ShowHiddenFiles)
    62  	allowMultipleFileSelection := bool2Cint(multiple)
    63  
    64  	var filterStrings slicer.StringSlicer
    65  	if options.Filters != nil {
    66  		for _, filter := range options.Filters {
    67  			thesePatterns := strings.Split(filter.Pattern, ";")
    68  			for _, pattern := range thesePatterns {
    69  				pattern = strings.TrimSpace(pattern)
    70  				if pattern != "" {
    71  					filterStrings.Add(pattern)
    72  				}
    73  			}
    74  		}
    75  		filterStrings.Deduplicate()
    76  	}
    77  	filters := filterStrings.Join(";")
    78  	C.OpenFileDialog(f.mainWindow.context, title, defaultFilename, defaultDirectory, allowDirectories, allowFiles, canCreateDirectories, treatPackagesAsDirectories, resolveAliases, showHiddenFiles, allowMultipleFileSelection, c.String(filters))
    79  
    80  	result := <-openFileDialogResponse
    81  
    82  	var parsedResults []string
    83  	err := json.Unmarshal([]byte(result), &parsedResults)
    84  
    85  	return parsedResults, err
    86  }
    87  
    88  // OpenFileDialog prompts the user to select a file
    89  func (f *Frontend) OpenFileDialog(options frontend.OpenDialogOptions) (string, error) {
    90  	results, err := f.openDialog(&options, false, true, false)
    91  	if err != nil {
    92  		return "", err
    93  	}
    94  	var selected string
    95  	if len(results) > 0 {
    96  		selected = results[0]
    97  	}
    98  	return selected, nil
    99  }
   100  
   101  // OpenMultipleFilesDialog prompts the user to select a file
   102  func (f *Frontend) OpenMultipleFilesDialog(options frontend.OpenDialogOptions) ([]string, error) {
   103  	return f.openDialog(&options, true, true, false)
   104  }
   105  
   106  // SaveFileDialog prompts the user to select a file
   107  func (f *Frontend) SaveFileDialog(options frontend.SaveDialogOptions) (string, error) {
   108  	dialogLock.Lock()
   109  	defer dialogLock.Unlock()
   110  
   111  	c := NewCalloc()
   112  	defer c.Free()
   113  	title := c.String(options.Title)
   114  	defaultFilename := c.String(options.DefaultFilename)
   115  	defaultDirectory := c.String(options.DefaultDirectory)
   116  	canCreateDirectories := bool2Cint(options.CanCreateDirectories)
   117  	treatPackagesAsDirectories := bool2Cint(options.TreatPackagesAsDirectories)
   118  	showHiddenFiles := bool2Cint(options.ShowHiddenFiles)
   119  
   120  	var filterStrings slicer.StringSlicer
   121  	if options.Filters != nil {
   122  		for _, filter := range options.Filters {
   123  			thesePatterns := strings.Split(filter.Pattern, ";")
   124  			for _, pattern := range thesePatterns {
   125  				pattern = strings.TrimSpace(pattern)
   126  				if pattern != "" {
   127  					filterStrings.Add(pattern)
   128  				}
   129  			}
   130  		}
   131  		filterStrings.Deduplicate()
   132  	}
   133  	filters := filterStrings.Join(";")
   134  	C.SaveFileDialog(f.mainWindow.context, title, defaultFilename, defaultDirectory, canCreateDirectories, treatPackagesAsDirectories, showHiddenFiles, c.String(filters))
   135  
   136  	result := <-saveFileDialogResponse
   137  
   138  	return result, nil
   139  }
   140  
   141  // MessageDialog show a message dialog to the user
   142  func (f *Frontend) MessageDialog(options frontend.MessageDialogOptions) (string, error) {
   143  	dialogLock.Lock()
   144  	defer dialogLock.Unlock()
   145  
   146  	c := NewCalloc()
   147  	defer c.Free()
   148  	dialogType := c.String(string(options.Type))
   149  	title := c.String(options.Title)
   150  	message := c.String(options.Message)
   151  	defaultButton := c.String(options.DefaultButton)
   152  	cancelButton := c.String(options.CancelButton)
   153  	const MaxButtons = 4
   154  	var buttons [MaxButtons]*C.char
   155  	for index, buttonText := range options.Buttons {
   156  		if index == MaxButtons {
   157  			return "", fmt.Errorf("max %d buttons supported (%d given)", MaxButtons, len(options.Buttons))
   158  		}
   159  		buttons[index] = c.String(buttonText)
   160  	}
   161  
   162  	var iconData unsafe.Pointer
   163  	var iconDataLength C.int
   164  	if options.Icon != nil {
   165  		iconData = unsafe.Pointer(&options.Icon[0])
   166  		iconDataLength = C.int(len(options.Icon))
   167  	}
   168  
   169  	C.MessageDialog(f.mainWindow.context, dialogType, title, message, buttons[0], buttons[1], buttons[2], buttons[3], defaultButton, cancelButton, iconData, iconDataLength)
   170  
   171  	result := <-messageDialogResponse
   172  
   173  	selectedC := buttons[result]
   174  	var selected string
   175  	if selectedC != nil {
   176  		selected = options.Buttons[result]
   177  	}
   178  	return selected, nil
   179  }
   180  
   181  //export processMessageDialogResponse
   182  func processMessageDialogResponse(selection int) {
   183  	messageDialogResponse <- selection
   184  }
   185  
   186  //export processOpenFileDialogResponse
   187  func processOpenFileDialogResponse(cselection *C.char) {
   188  	selection := C.GoString(cselection)
   189  	openFileDialogResponse <- selection
   190  }
   191  
   192  //export processSaveFileDialogResponse
   193  func processSaveFileDialogResponse(cselection *C.char) {
   194  	selection := C.GoString(cselection)
   195  	saveFileDialogResponse <- selection
   196  }