From 1920a545f43f099b6e175298e1b192b174cd2b9b Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 29 Dec 2021 09:47:31 +1100 Subject: [PATCH 01/16] [linux] Dialogs WIP --- v2/internal/frontend/desktop/linux/calloc.go | 32 +++++++++++++ v2/internal/frontend/desktop/linux/dialog.go | 25 +++++++++-- .../frontend/desktop/linux/frontend.go | 24 ++++++++-- v2/internal/frontend/desktop/linux/window.go | 45 ++++++++++++++++++- 4 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 v2/internal/frontend/desktop/linux/calloc.go diff --git a/v2/internal/frontend/desktop/linux/calloc.go b/v2/internal/frontend/desktop/linux/calloc.go new file mode 100644 index 000000000..8a8158ba8 --- /dev/null +++ b/v2/internal/frontend/desktop/linux/calloc.go @@ -0,0 +1,32 @@ +package linux + +/* +#include +*/ +import "C" +import "unsafe" + +// Calloc handles alloc/dealloc of C data +type Calloc struct { + pool []unsafe.Pointer +} + +// NewCalloc creates a new allocator +func NewCalloc() Calloc { + return Calloc{} +} + +// String creates a new C string and retains a reference to it +func (c Calloc) String(in string) *C.char { + result := C.CString(in) + c.pool = append(c.pool, unsafe.Pointer(result)) + return result +} + +// Free frees all allocated C memory +func (c Calloc) Free() { + for _, str := range c.pool { + C.free(str) + } + c.pool = []unsafe.Pointer{} +} diff --git a/v2/internal/frontend/desktop/linux/dialog.go b/v2/internal/frontend/desktop/linux/dialog.go index 4e3f1e05b..7227410b4 100644 --- a/v2/internal/frontend/desktop/linux/dialog.go +++ b/v2/internal/frontend/desktop/linux/dialog.go @@ -3,10 +3,24 @@ package linux -import "github.com/wailsapp/wails/v2/internal/frontend" +import ( + "github.com/wailsapp/wails/v2/internal/frontend" +) +import "C" -func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (string, error) { - panic("implement me") +var openFileResults = make(chan string) + +func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (result string, err error) { + + f.dispatch(func() { + println("Before OpenFileDialog") + f.mainWindow.OpenFileDialog(dialogOptions) + println("After OpenFileDialog") + }) + println("Waiting for result") + result = <-openFileResults + println("Got result") + return } func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) { @@ -24,3 +38,8 @@ func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (str func (f *Frontend) MessageDialog(dialogOptions frontend.MessageDialogOptions) (string, error) { panic("implement me") } + +//export processOpenFileResult +func processOpenFileResult(result *C.char) { + openFileResults <- C.GoString(result) +} diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index abcfcd447..0161c87f3 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -24,6 +24,7 @@ import "C" import ( "context" "encoding/json" + "fmt" "log" "os" "strconv" @@ -62,6 +63,7 @@ type Frontend struct { func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend { + println("[NewFrontend] PID:", os.Getpid()) // Set GDK_BACKEND=x11 to prevent warnings os.Setenv("GDK_BACKEND", "x11") @@ -301,10 +303,11 @@ var dispatchCallbackLock sync.Mutex //export callDispatchedMethod func callDispatchedMethod(cid C.int) { + println("[callDispatchedMethod] PID:", os.Getpid()) id := int(cid) fn := dispatchCallbacks[id] if fn != nil { - go fn() + fn() dispatchCallbackLock.Lock() delete(dispatchCallbacks, id) dispatchCallbackLock.Unlock() @@ -342,11 +345,24 @@ func (f *Frontend) processRequest(request unsafe.Pointer) { // Load file from asset store content, mimeType, err := f.assets.Load(file) - if err != nil { - return - } // TODO How to return 404/500 errors to webkit? + if err != nil { + if os.IsNotExist(err) { + f.dispatch(func() { + message := C.CString("not found") + defer C.free(unsafe.Pointer(message)) + C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(404), message)) + }) + } else { + err = fmt.Errorf("Error processing request %s: %w", uri, err) + f.logger.Error(err.Error()) + message := C.CString("internal server error") + defer C.free(unsafe.Pointer(message)) + C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(500), message)) + } + return + } cContent := C.CString(string(content)) defer C.free(unsafe.Pointer(cContent)) diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 9eca7af50..11e81ff09 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -4,10 +4,11 @@ package linux /* -#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 x11 #include "gtk/gtk.h" #include "webkit2/webkit2.h" +#include #include #include @@ -105,6 +106,11 @@ ulong setupInvokeSignal(void* contentManager) { return g_signal_connect((WebKitUserContentManager*)contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), NULL); } +void initThreads() { + printf("init threads\n"); + XInitThreads(); +} + // These are the x,y & time of the last mouse down event // It's used for window dragging float xroot = 0.0f; @@ -199,10 +205,37 @@ int executeJS(gpointer data) { void ExecuteOnMainThread(JSCallback* jscallback) { g_idle_add((GSourceFunc)executeJS, (gpointer)jscallback); } + +void extern processOpenFileResult(char*); + +static void OpenDialog(GtkWindow* window, char *title) { + printf("Here\n"); + GtkWidget *dlg = gtk_file_chooser_dialog_new(title, window, GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL); + printf("Here3\n"); + + gint response = gtk_dialog_run(GTK_DIALOG(dlg)); + printf("Here 4\n"); + + if (response == GTK_RESPONSE_ACCEPT) + { + printf("Here 5\n"); + + gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); + processOpenFileResult(filename); + g_free(filename); + } + gtk_widget_destroy(dlg); +} + */ import "C" import ( + "github.com/wailsapp/wails/v2/internal/frontend" "github.com/wailsapp/wails/v2/pkg/options" + "os" "unsafe" ) @@ -384,6 +417,8 @@ func (w *Window) Run() { case options.Maximised: w.Maximise() } + + C.initThreads() C.gtk_main() w.Destroy() } @@ -425,3 +460,11 @@ func (w *Window) StartDrag() { func (w *Window) Quit() { C.gtk_main_quit() } + +func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) { + println("OpenFileDialog PID:", os.Getpid()) + mem := NewCalloc() + title := mem.String(dialogOptions.Title) + C.OpenDialog(w.asGTKWindow(), title) + mem.Free() +} From b11964f0eb4a47f110e1dbd377ce5d6bb3cbfbc9 Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Wed, 19 Jan 2022 13:34:53 -0600 Subject: [PATCH 02/16] remove xlib thread initialization --- v2/internal/frontend/desktop/linux/window.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 11e81ff09..bfba803ad 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -4,11 +4,10 @@ package linux /* -#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 x11 +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 #include "gtk/gtk.h" #include "webkit2/webkit2.h" -#include #include #include @@ -106,11 +105,6 @@ ulong setupInvokeSignal(void* contentManager) { return g_signal_connect((WebKitUserContentManager*)contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), NULL); } -void initThreads() { - printf("init threads\n"); - XInitThreads(); -} - // These are the x,y & time of the last mouse down event // It's used for window dragging float xroot = 0.0f; @@ -418,7 +412,6 @@ func (w *Window) Run() { w.Maximise() } - C.initThreads() C.gtk_main() w.Destroy() } From 3bcddd5b4cb7fc0f62788912a0f454fbd1e104fd Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Wed, 19 Jan 2022 13:36:44 -0600 Subject: [PATCH 03/16] add function argument to ExecuteOnMainThread --- v2/internal/frontend/desktop/linux/window.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index bfba803ad..906af4f78 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -196,8 +196,8 @@ int executeJS(gpointer data) { return G_SOURCE_REMOVE; } -void ExecuteOnMainThread(JSCallback* jscallback) { - g_idle_add((GSourceFunc)executeJS, (gpointer)jscallback); +void ExecuteOnMainThread(void* f, JSCallback* jscallback) { + g_idle_add((GSourceFunc)f, (gpointer)jscallback); } void extern processOpenFileResult(char*); @@ -443,7 +443,7 @@ func (w *Window) ExecJS(js string) { webview: w.webview, script: C.CString(js), } - C.ExecuteOnMainThread(&jscallback) + C.ExecuteOnMainThread(C.executeJS, &jscallback) } func (w *Window) StartDrag() { From a2d447aecf4797e14042b7bf57f9c937af287c96 Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Wed, 19 Jan 2022 13:37:44 -0600 Subject: [PATCH 04/16] implement opendialog as callback function --- v2/internal/frontend/desktop/linux/window.go | 31 +++++++++----------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 906af4f78..463f04435 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -202,26 +202,23 @@ void ExecuteOnMainThread(void* f, JSCallback* jscallback) { void extern processOpenFileResult(char*); -static void OpenDialog(GtkWindow* window, char *title) { - printf("Here\n"); - GtkWidget *dlg = gtk_file_chooser_dialog_new(title, window, GTK_FILE_CHOOSER_ACTION_OPEN, +int opendialog(gpointer data) { + struct JSCallback *js = data; + GtkWidget *dlg = gtk_file_chooser_dialog_new(js->script, js->webview, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); - printf("Here3\n"); gint response = gtk_dialog_run(GTK_DIALOG(dlg)); - printf("Here 4\n"); - - if (response == GTK_RESPONSE_ACCEPT) - { - printf("Here 5\n"); + if (response == GTK_RESPONSE_ACCEPT) { gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); processOpenFileResult(filename); g_free(filename); - } - gtk_widget_destroy(dlg); + } + gtk_widget_destroy(dlg); + free(js->script); + return G_SOURCE_REMOVE; } */ @@ -229,7 +226,6 @@ import "C" import ( "github.com/wailsapp/wails/v2/internal/frontend" "github.com/wailsapp/wails/v2/pkg/options" - "os" "unsafe" ) @@ -455,9 +451,10 @@ func (w *Window) Quit() { } func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) { - println("OpenFileDialog PID:", os.Getpid()) - mem := NewCalloc() - title := mem.String(dialogOptions.Title) - C.OpenDialog(w.asGTKWindow(), title) - mem.Free() + data := C.JSCallback{ + webview: w.webview, + script: C.CString(dialogOptions.Title), + } + C.ExecuteOnMainThread(C.opendialog, &data) } + From 6e30c6770ba9d4993a3e885fc513620459b79e35 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Thu, 20 Jan 2022 21:46:19 +1100 Subject: [PATCH 05/16] Tidy up and slight refactor --- v2/internal/frontend/desktop/linux/dialog.go | 9 +----- .../frontend/desktop/linux/frontend.go | 4 +-- v2/internal/frontend/desktop/linux/window.go | 32 ++++++++++++------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/v2/internal/frontend/desktop/linux/dialog.go b/v2/internal/frontend/desktop/linux/dialog.go index 7227410b4..b53d1dddc 100644 --- a/v2/internal/frontend/desktop/linux/dialog.go +++ b/v2/internal/frontend/desktop/linux/dialog.go @@ -11,15 +11,8 @@ import "C" var openFileResults = make(chan string) func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (result string, err error) { - - f.dispatch(func() { - println("Before OpenFileDialog") - f.mainWindow.OpenFileDialog(dialogOptions) - println("After OpenFileDialog") - }) - println("Waiting for result") + f.mainWindow.OpenFileDialog(dialogOptions) result = <-openFileResults - println("Got result") return } diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index 0161c87f3..02050f2c9 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -16,7 +16,7 @@ static inline void processDispatchID(gpointer id) { } static void gtkDispatch(int id) { - gdk_threads_add_idle((GSourceFunc)processDispatchID, GINT_TO_POINTER(id)); + g_idle_add((GSourceFunc)processDispatchID, GINT_TO_POINTER(id)); } */ @@ -63,7 +63,6 @@ type Frontend struct { func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend { - println("[NewFrontend] PID:", os.Getpid()) // Set GDK_BACKEND=x11 to prevent warnings os.Setenv("GDK_BACKEND", "x11") @@ -303,7 +302,6 @@ var dispatchCallbackLock sync.Mutex //export callDispatchedMethod func callDispatchedMethod(cid C.int) { - println("[callDispatchedMethod] PID:", os.Getpid()) id := int(cid) fn := dispatchCallbacks[id] if fn != nil { diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 463f04435..31e063202 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -196,15 +196,23 @@ int executeJS(gpointer data) { return G_SOURCE_REMOVE; } -void ExecuteOnMainThread(void* f, JSCallback* jscallback) { +void ExecuteOnMainThread(void* f, gpointer jscallback) { g_idle_add((GSourceFunc)f, (gpointer)jscallback); } void extern processOpenFileResult(char*); + +typedef struct OpenFileDialogOptions { + void* webview; + char* title; + char** filters; +} OpenFileDialogOptions; + + int opendialog(gpointer data) { - struct JSCallback *js = data; - GtkWidget *dlg = gtk_file_chooser_dialog_new(js->script, js->webview, GTK_FILE_CHOOSER_ACTION_OPEN, + struct OpenFileDialogOptions *options = data; + GtkWidget *dlg = gtk_file_chooser_dialog_new(options->title, options->webview, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); @@ -215,9 +223,11 @@ int opendialog(gpointer data) { gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); processOpenFileResult(filename); g_free(filename); - } + } else { + processOpenFileResult(""); + } gtk_widget_destroy(dlg); - free(js->script); + free(options->title); return G_SOURCE_REMOVE; } @@ -437,9 +447,9 @@ func (w *Window) SetTitle(title string) { func (w *Window) ExecJS(js string) { jscallback := C.JSCallback{ webview: w.webview, - script: C.CString(js), + script: C.CString(js), } - C.ExecuteOnMainThread(C.executeJS, &jscallback) + C.ExecuteOnMainThread(C.executeJS, C.gpointer(&jscallback)) } func (w *Window) StartDrag() { @@ -451,10 +461,10 @@ func (w *Window) Quit() { } func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) { - data := C.JSCallback{ + data := C.OpenFileDialogOptions{ webview: w.webview, - script: C.CString(dialogOptions.Title), + title: C.CString(dialogOptions.Title), } - C.ExecuteOnMainThread(C.opendialog, &data) + // TODO: Filter + C.ExecuteOnMainThread(C.opendialog, C.gpointer(&data)) } - From 6c68e591138bc3585f91a7578b3c76b30696bd92 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Fri, 21 Jan 2022 20:25:04 +1100 Subject: [PATCH 06/16] Support OpenFile / OpenMultipleFiles --- v2/internal/frontend/desktop/linux/dialog.go | 40 ++++- v2/internal/frontend/desktop/linux/window.go | 150 +++++++++++++++++-- 2 files changed, 168 insertions(+), 22 deletions(-) diff --git a/v2/internal/frontend/desktop/linux/dialog.go b/v2/internal/frontend/desktop/linux/dialog.go index b53d1dddc..1641c7f41 100644 --- a/v2/internal/frontend/desktop/linux/dialog.go +++ b/v2/internal/frontend/desktop/linux/dialog.go @@ -5,19 +5,36 @@ package linux import ( "github.com/wailsapp/wails/v2/internal/frontend" + "unsafe" ) + +/* +#include +#include "gtk/gtk.h" +*/ import "C" -var openFileResults = make(chan string) +const ( + GTK_FILE_CHOOSER_ACTION_OPEN C.GtkFileChooserAction = C.GTK_FILE_CHOOSER_ACTION_OPEN + GTK_FILE_CHOOSER_ACTION_SAVE C.GtkFileChooserAction = C.GTK_FILE_CHOOSER_ACTION_SAVE + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER C.GtkFileChooserAction = C.GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER +) + +var openFileResults = make(chan []string) func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (result string, err error) { - f.mainWindow.OpenFileDialog(dialogOptions) - result = <-openFileResults - return + f.mainWindow.OpenFileDialog(dialogOptions, 0, GTK_FILE_CHOOSER_ACTION_OPEN) + results := <-openFileResults + if len(results) == 1 { + return results[0], nil + } + return "", nil } func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) { - panic("implement me") + f.mainWindow.OpenFileDialog(dialogOptions, 1, GTK_FILE_CHOOSER_ACTION_OPEN) + result := <-openFileResults + return result, nil } func (f *Frontend) OpenDirectoryDialog(dialogOptions frontend.OpenDialogOptions) (string, error) { @@ -33,6 +50,15 @@ func (f *Frontend) MessageDialog(dialogOptions frontend.MessageDialogOptions) (s } //export processOpenFileResult -func processOpenFileResult(result *C.char) { - openFileResults <- C.GoString(result) +func processOpenFileResult(carray **C.char) { + // Create a Go slice from the C array + var result []string + goArray := (*[1024]*C.char)(unsafe.Pointer(carray))[:1024:1024] + for _, s := range goArray { + if s == nil { + break + } + result = append(result, C.GoString(s)) + } + openFileResults <- result } diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 31e063202..34e06c8dd 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -142,7 +142,7 @@ void connectButtons(void* webview) { g_signal_connect(WEBKIT_WEB_VIEW(webview), "button-release-event", G_CALLBACK(buttonRelease), NULL); } -extern void processURLRequest(WebKitURISchemeRequest *request); +extern void processURLRequest(void *request); // This is called when the close button on the window is pressed gboolean close_button_pressed(GtkWidget *widget, GdkEvent *event, void* data) @@ -200,42 +200,121 @@ void ExecuteOnMainThread(void* f, gpointer jscallback) { g_idle_add((GSourceFunc)f, (gpointer)jscallback); } -void extern processOpenFileResult(char*); - +void extern processOpenFileResult(void*); typedef struct OpenFileDialogOptions { void* webview; char* title; - char** filters; + char* defaultFilename; + int createDirectories; + int multipleFiles; + int showHiddenFiles; + GtkFileChooserAction action; + GtkFileFilter** filters; } OpenFileDialogOptions; +GtkFileFilter** allocFileFilterArray(size_t ln) { + return (GtkFileFilter**) malloc(ln * sizeof(GtkFileFilter*)); +} + +void freeFileFilterArray(GtkFileFilter** filters) { + free(filters); +} int opendialog(gpointer data) { struct OpenFileDialogOptions *options = data; - GtkWidget *dlg = gtk_file_chooser_dialog_new(options->title, options->webview, GTK_FILE_CHOOSER_ACTION_OPEN, + GtkWidget *dlgWidget = gtk_file_chooser_dialog_new(options->title, options->webview, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); - gint response = gtk_dialog_run(GTK_DIALOG(dlg)); + GtkFileChooser *fc = GTK_FILE_CHOOSER(dlgWidget); + // filters + if (options->filters != 0) { + int index = 0; + GtkFileFilter* thisFilter; + while(options->filters[index] != NULL) { + thisFilter = options->filters[index]; + gtk_file_chooser_add_filter(fc, thisFilter); + index++; + } + } + + gtk_file_chooser_set_local_only(fc, FALSE); + + if (options->multipleFiles == 1) { + gtk_file_chooser_set_select_multiple(fc, TRUE); + } + gtk_file_chooser_set_do_overwrite_confirmation(fc, TRUE); + if (options->createDirectories == 1) { + gtk_file_chooser_set_create_folders(fc, TRUE); + } + if (options->showHiddenFiles == 1) { + gtk_file_chooser_set_show_hidden(fc, TRUE); + } + + if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) { + if (options->defaultFilename != NULL) { + gtk_file_chooser_set_current_name(fc, options->defaultFilename); + free(options->defaultFilename); + } + } + + gint response = gtk_dialog_run(GTK_DIALOG(dlgWidget)); + + // Max 1024 files to select + char** result = calloc(1024, sizeof(char*)); + int resultIndex = 0; if (response == GTK_RESPONSE_ACCEPT) { - gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); - processOpenFileResult(filename); - g_free(filename); + GSList* filenames = gtk_file_chooser_get_filenames(fc); + GSList *iter = filenames; + while(iter) { + result[resultIndex++] = (char *)iter->data; + iter = g_slist_next(iter); + if (resultIndex == 1024) { + break; + } + } + processOpenFileResult(result); + iter = filenames; + while(iter) { + g_free(iter->data); + iter = g_slist_next(iter); + } } else { - processOpenFileResult(""); + processOpenFileResult(result); } - gtk_widget_destroy(dlg); + free(result); + + // Release filters + if (options->filters != NULL) { + int index = 0; + GtkFileFilter* thisFilter; + while(options->filters[index] != 0) { + thisFilter = options->filters[index]; + g_object_unref(thisFilter); + index++; + } + freeFileFilterArray(options->filters); + } + gtk_widget_destroy(dlgWidget); free(options->title); return G_SOURCE_REMOVE; } +GtkFileFilter* newFileFilter() { + GtkFileFilter* result = gtk_file_filter_new(); + g_object_ref(result); + return result; +} + */ import "C" import ( "github.com/wailsapp/wails/v2/internal/frontend" "github.com/wailsapp/wails/v2/pkg/options" + "strings" "unsafe" ) @@ -286,6 +365,7 @@ func NewWindow(appoptions *options.App, debug bool) *Window { if debug { C.devtoolsEnabled(unsafe.Pointer(webview), C.int(1)) + } // Setup window @@ -460,11 +540,51 @@ func (w *Window) Quit() { C.gtk_main_quit() } -func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) { +func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multipleFiles int, action C.GtkFileChooserAction) { + data := C.OpenFileDialogOptions{ - webview: w.webview, - title: C.CString(dialogOptions.Title), + webview: w.webview, + title: C.CString(dialogOptions.Title), + multipleFiles: C.int(multipleFiles), + action: action, + } + + if len(dialogOptions.Filters) > 0 { + // Create filter array + mem := NewCalloc() + arraySize := len(dialogOptions.Filters) + 1 + data.filters = C.allocFileFilterArray((C.ulong)(arraySize)) + filters := (*[1 << 30]*C.struct__GtkFileFilter)(unsafe.Pointer(data.filters)) + for index, filter := range dialogOptions.Filters { + thisFilter := C.gtk_file_filter_new() + C.g_object_ref(C.gpointer(thisFilter)) + if filter.DisplayName != "" { + cName := mem.String(filter.DisplayName) + C.gtk_file_filter_set_name(thisFilter, cName) + } + if filter.Pattern != "" { + for _, thisPattern := range strings.Split(filter.Pattern, ";") { + cThisPattern := mem.String(thisPattern) + C.gtk_file_filter_add_pattern(thisFilter, cThisPattern) + } + } + // Add filter to array + filters[index] = thisFilter + } + mem.Free() + filters[arraySize-1] = (*C.struct__GtkFileFilter)(unsafe.Pointer(uintptr(0))) + } + + if dialogOptions.CanCreateDirectories { + data.createDirectories = C.int(1) + } + + if dialogOptions.ShowHiddenFiles { + data.showHiddenFiles = C.int(1) + } + + if dialogOptions.DefaultFilename != "" { + data.defaultFilename = C.CString(dialogOptions.DefaultFilename) } - // TODO: Filter C.ExecuteOnMainThread(C.opendialog, C.gpointer(&data)) } From 4f3c14f25b55c58bdf77e3fdbf2b8c554f102de7 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Fri, 21 Jan 2022 20:25:33 +1100 Subject: [PATCH 07/16] Fix context issue --- v2/internal/appng/app_production.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/v2/internal/appng/app_production.go b/v2/internal/appng/app_production.go index c4a67e996..4161d2405 100644 --- a/v2/internal/appng/app_production.go +++ b/v2/internal/appng/app_production.go @@ -78,12 +78,14 @@ func CreateApp(appoptions *options.App) (*App, error) { ctx = context.WithValue(ctx, "events", eventHandler) messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings, eventHandler) - appFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher) - eventHandler.AddFrontend(appFrontend) - + debug := IsDebug() + ctx = context.WithValue(ctx, "debug", debug) // Attach logger to context ctx = context.WithValue(ctx, "logger", myLogger) + appFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher) + eventHandler.AddFrontend(appFrontend) + result := &App{ ctx: ctx, frontend: appFrontend, @@ -91,13 +93,10 @@ func CreateApp(appoptions *options.App) (*App, error) { menuManager: menuManager, startupCallback: appoptions.OnStartup, shutdownCallback: appoptions.OnShutdown, - debug: IsDebug(), + debug: debug, + options: appoptions, } - result.options = appoptions - - result.ctx = context.WithValue(result.ctx, "debug", result.debug) - return result, nil } From 1e5f8e03cb1c18489fe78bd9d4074017e32572c0 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Fri, 21 Jan 2022 20:25:47 +1100 Subject: [PATCH 08/16] Tidy Up --- v2/internal/appng/app_default_darwin.go | 13 +------------ v2/internal/appng/app_default_linux.go | 13 +------------ 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/v2/internal/appng/app_default_darwin.go b/v2/internal/appng/app_default_darwin.go index f0971f864..4cde85212 100644 --- a/v2/internal/appng/app_default_darwin.go +++ b/v2/internal/appng/app_default_darwin.go @@ -17,16 +17,5 @@ func (a *App) Run() error { // CreateApp creates the app! func CreateApp(_ *options.App) (*App, error) { - // result := w32.MessageBox(0, - // `Wails applications will not build without the correct build tags. - //Please use "wails build" or press "OK" to open the documentation on how to use "go build"`, - // "Error", - // w32.MB_ICONERROR|w32.MB_OKCANCEL) - // if result == 1 { - // exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://wails.io").Start() - // } - - err := fmt.Errorf(`Wails applications will not build without the correct build tags.`) - - return nil, err + return nil, fmt.Errorf(`Wails applications will not build without the correct build tags.`) } diff --git a/v2/internal/appng/app_default_linux.go b/v2/internal/appng/app_default_linux.go index 3356debae..ec54dd107 100644 --- a/v2/internal/appng/app_default_linux.go +++ b/v2/internal/appng/app_default_linux.go @@ -17,16 +17,5 @@ func (a *App) Run() error { // CreateApp creates the app! func CreateApp(_ *options.App) (*App, error) { - // result := w32.MessageBox(0, - // `Wails applications will not build without the correct build tags. - //Please use "wails build" or press "OK" to open the documentation on how to use "go build"`, - // "Error", - // w32.MB_ICONERROR|w32.MB_OKCANCEL) - // if result == 1 { - // exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://wails.io").Start() - // } - - err := fmt.Errorf(`Wails applications will not build without the correct build tags.`) - - return nil, err + return nil, fmt.Errorf(`Wails applications will not build without the correct build tags.`) } From b7647b3992504e448a0039de3e767e30f379457f Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Fri, 21 Jan 2022 10:16:20 -0600 Subject: [PATCH 09/16] allow `action` to be overridden by caller --- v2/internal/frontend/desktop/linux/window.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 34e06c8dd..8ffc16257 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -223,7 +223,7 @@ void freeFileFilterArray(GtkFileFilter** filters) { int opendialog(gpointer data) { struct OpenFileDialogOptions *options = data; - GtkWidget *dlgWidget = gtk_file_chooser_dialog_new(options->title, options->webview, GTK_FILE_CHOOSER_ACTION_OPEN, + GtkWidget *dlgWidget = gtk_file_chooser_dialog_new(options->title, options->webview, options->action, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); From 2c2e053bd1b88478db282149a7403c397f2f1c92 Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Fri, 21 Jan 2022 10:16:57 -0600 Subject: [PATCH 10/16] [linux] implement OpenDirectoryDialog --- v2/internal/frontend/desktop/linux/dialog.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/v2/internal/frontend/desktop/linux/dialog.go b/v2/internal/frontend/desktop/linux/dialog.go index 1641c7f41..afbf50239 100644 --- a/v2/internal/frontend/desktop/linux/dialog.go +++ b/v2/internal/frontend/desktop/linux/dialog.go @@ -38,7 +38,12 @@ func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOpti } func (f *Frontend) OpenDirectoryDialog(dialogOptions frontend.OpenDialogOptions) (string, error) { - panic("implement me") + f.mainWindow.OpenFileDialog(dialogOptions, 0, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + result := <-openFileResults + if len(result) == 1 { + return result[0], nil + } + return "", nil } func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) { From d0feeb938ccaa0b1c7c25617c372692497a9b7bf Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Fri, 21 Jan 2022 10:20:59 -0600 Subject: [PATCH 11/16] implement SaveFileDialog --- v2/internal/frontend/desktop/linux/dialog.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/v2/internal/frontend/desktop/linux/dialog.go b/v2/internal/frontend/desktop/linux/dialog.go index afbf50239..ede5b1f5e 100644 --- a/v2/internal/frontend/desktop/linux/dialog.go +++ b/v2/internal/frontend/desktop/linux/dialog.go @@ -47,7 +47,12 @@ func (f *Frontend) OpenDirectoryDialog(dialogOptions frontend.OpenDialogOptions) } func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) { - panic("implement me") + f.mainWindow.OpenFileDialog(dialogOptions, 0, GTK_FILE_CHOOSER_ACTION_SAVE) + results := <-openFileResults + if len(results) == 1 { + return results[0], nil + } + return "", nil } func (f *Frontend) MessageDialog(dialogOptions frontend.MessageDialogOptions) (string, error) { From a7e516a43d5a6ea6f96416014fc88502017ace76 Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Fri, 21 Jan 2022 10:55:21 -0600 Subject: [PATCH 12/16] [linux] implement SaveFileDialog --- v2/internal/frontend/desktop/linux/dialog.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/v2/internal/frontend/desktop/linux/dialog.go b/v2/internal/frontend/desktop/linux/dialog.go index ede5b1f5e..106b9f1bd 100644 --- a/v2/internal/frontend/desktop/linux/dialog.go +++ b/v2/internal/frontend/desktop/linux/dialog.go @@ -47,7 +47,16 @@ func (f *Frontend) OpenDirectoryDialog(dialogOptions frontend.OpenDialogOptions) } func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) { - f.mainWindow.OpenFileDialog(dialogOptions, 0, GTK_FILE_CHOOSER_ACTION_SAVE) + options := frontend.OpenDialogOptions{ + DefaultDirectory: dialogOptions.DefaultDirectory, + DefaultFilename: dialogOptions.DefaultFilename, + Title: dialogOptions.Title, + Filters: dialogOptions.Filters, + ShowHiddenFiles: dialogOptions.ShowHiddenFiles, + CanCreateDirectories: dialogOptions.CanCreateDirectories, + TreatPackagesAsDirectories: dialogOptions.TreatPackagesAsDirectories, + } + f.mainWindow.OpenFileDialog(options, 0, GTK_FILE_CHOOSER_ACTION_SAVE) results := <-openFileResults if len(results) == 1 { return results[0], nil From c58252386ff6b5c07c4ce4fcb7105ef899821062 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 22 Jan 2022 06:46:35 +1100 Subject: [PATCH 13/16] Support default directory, Fixed label, removed TreatPackagesAsDirectories --- v2/internal/frontend/desktop/linux/dialog.go | 11 +++++------ v2/internal/frontend/desktop/linux/window.go | 19 +++++++++++++++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/v2/internal/frontend/desktop/linux/dialog.go b/v2/internal/frontend/desktop/linux/dialog.go index 106b9f1bd..91c9122f7 100644 --- a/v2/internal/frontend/desktop/linux/dialog.go +++ b/v2/internal/frontend/desktop/linux/dialog.go @@ -48,13 +48,12 @@ func (f *Frontend) OpenDirectoryDialog(dialogOptions frontend.OpenDialogOptions) func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) { options := frontend.OpenDialogOptions{ - DefaultDirectory: dialogOptions.DefaultDirectory, - DefaultFilename: dialogOptions.DefaultFilename, - Title: dialogOptions.Title, - Filters: dialogOptions.Filters, - ShowHiddenFiles: dialogOptions.ShowHiddenFiles, + DefaultDirectory: dialogOptions.DefaultDirectory, + DefaultFilename: dialogOptions.DefaultFilename, + Title: dialogOptions.Title, + Filters: dialogOptions.Filters, + ShowHiddenFiles: dialogOptions.ShowHiddenFiles, CanCreateDirectories: dialogOptions.CanCreateDirectories, - TreatPackagesAsDirectories: dialogOptions.TreatPackagesAsDirectories, } f.mainWindow.OpenFileDialog(options, 0, GTK_FILE_CHOOSER_ACTION_SAVE) results := <-openFileResults diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 8ffc16257..817815ee2 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -206,6 +206,7 @@ typedef struct OpenFileDialogOptions { void* webview; char* title; char* defaultFilename; + char* defaultDirectory; int createDirectories; int multipleFiles; int showHiddenFiles; @@ -223,9 +224,13 @@ void freeFileFilterArray(GtkFileFilter** filters) { int opendialog(gpointer data) { struct OpenFileDialogOptions *options = data; + char *label = "_Open"; + if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) { + label = "_Save"; + } GtkWidget *dlgWidget = gtk_file_chooser_dialog_new(options->title, options->webview, options->action, "_Cancel", GTK_RESPONSE_CANCEL, - "_Open", GTK_RESPONSE_ACCEPT, + label, GTK_RESPONSE_ACCEPT, NULL); GtkFileChooser *fc = GTK_FILE_CHOOSER(dlgWidget); @@ -253,6 +258,11 @@ int opendialog(gpointer data) { gtk_file_chooser_set_show_hidden(fc, TRUE); } + if (options->defaultDirectory != NULL) { + gtk_file_chooser_set_current_folder (fc, options->defaultDirectory); + free(options->defaultDirectory); + } + if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) { if (options->defaultFilename != NULL) { gtk_file_chooser_set_current_name(fc, options->defaultFilename); @@ -572,7 +582,7 @@ func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multip filters[index] = thisFilter } mem.Free() - filters[arraySize-1] = (*C.struct__GtkFileFilter)(unsafe.Pointer(uintptr(0))) + filters[arraySize-1] = nil } if dialogOptions.CanCreateDirectories { @@ -586,5 +596,10 @@ func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multip if dialogOptions.DefaultFilename != "" { data.defaultFilename = C.CString(dialogOptions.DefaultFilename) } + + if dialogOptions.DefaultDirectory != "" { + data.defaultDirectory = C.CString(dialogOptions.DefaultDirectory) + } + C.ExecuteOnMainThread(C.opendialog, C.gpointer(&data)) } From f3cc4b5ebdf42efcdbe25442fc4d562603a32f03 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Tue, 25 Jan 2022 20:58:29 +1100 Subject: [PATCH 14/16] Support message dialog --- v2/internal/frontend/desktop/linux/dialog.go | 9 ++- v2/internal/frontend/desktop/linux/window.go | 73 ++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/v2/internal/frontend/desktop/linux/dialog.go b/v2/internal/frontend/desktop/linux/dialog.go index 91c9122f7..c0d9158e5 100644 --- a/v2/internal/frontend/desktop/linux/dialog.go +++ b/v2/internal/frontend/desktop/linux/dialog.go @@ -21,6 +21,7 @@ const ( ) var openFileResults = make(chan []string) +var messageDialogResult = make(chan string) func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (result string, err error) { f.mainWindow.OpenFileDialog(dialogOptions, 0, GTK_FILE_CHOOSER_ACTION_OPEN) @@ -64,7 +65,8 @@ func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (str } func (f *Frontend) MessageDialog(dialogOptions frontend.MessageDialogOptions) (string, error) { - panic("implement me") + f.mainWindow.MessageDialog(dialogOptions) + return <-messageDialogResult, nil } //export processOpenFileResult @@ -80,3 +82,8 @@ func processOpenFileResult(carray **C.char) { } openFileResults <- result } + +//export processMessageDialogResult +func processMessageDialogResult(result *C.char) { + messageDialogResult <- C.GoString(result) +} diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 817815ee2..f27733f75 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -200,6 +200,59 @@ void ExecuteOnMainThread(void* f, gpointer jscallback) { g_idle_add((GSourceFunc)f, (gpointer)jscallback); } +void extern processMessageDialogResult(char*); + +typedef struct MessageDialogOptions { + void* window; + char* title; + char* message; + int messageType; +} MessageDialogOptions; + +void messageDialog(gpointer data) { + + GtkDialogFlags flags; + GtkMessageType messageType; + MessageDialogOptions *options = (MessageDialogOptions*) data; + if( options->messageType == 0 ) { + messageType = GTK_MESSAGE_INFO; + flags = GTK_BUTTONS_OK; + } else if( options->messageType == 1 ) { + messageType = GTK_MESSAGE_ERROR; + flags = GTK_BUTTONS_OK; + } else if( options->messageType == 2 ) { + messageType = GTK_MESSAGE_QUESTION; + flags = GTK_BUTTONS_YES_NO; + } else { + messageType = GTK_MESSAGE_WARNING; + flags = GTK_BUTTONS_OK; + } + + GtkWidget *dialog; + dialog = gtk_message_dialog_new(GTK_WINDOW(options->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + messageType, + flags, + options->message, NULL); + gtk_window_set_title(GTK_WINDOW(dialog), options->title); + GtkResponseType result = gtk_dialog_run(GTK_DIALOG(dialog)); + if ( result == GTK_RESPONSE_YES ) { + processMessageDialogResult("Yes"); + } else if ( result == GTK_RESPONSE_NO ) { + processMessageDialogResult("No"); + } else if ( result == GTK_RESPONSE_OK ) { + processMessageDialogResult("OK"); + } else if ( result == GTK_RESPONSE_CANCEL ) { + processMessageDialogResult("Cancel"); + } else { + processMessageDialogResult(""); + } + + gtk_widget_destroy(dialog); + free(options->title); + free(options->message); +} + void extern processOpenFileResult(void*); typedef struct OpenFileDialogOptions { @@ -603,3 +656,23 @@ func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multip C.ExecuteOnMainThread(C.opendialog, C.gpointer(&data)) } + +func (w *Window) MessageDialog(dialogOptions frontend.MessageDialogOptions) { + + data := C.MessageDialogOptions{ + window: w.gtkWindow, + title: C.CString(dialogOptions.Title), + message: C.CString(dialogOptions.Message), + } + switch dialogOptions.Type { + case frontend.InfoDialog: + data.messageType = C.int(0) + case frontend.ErrorDialog: + data.messageType = C.int(1) + case frontend.QuestionDialog: + data.messageType = C.int(2) + case frontend.WarningDialog: + data.messageType = C.int(3) + } + C.ExecuteOnMainThread(C.messageDialog, C.gpointer(&data)) +} From bb867832e15ba6ec48bf6e8bd934cf9d6c329b35 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 26 Jan 2022 09:55:12 +1100 Subject: [PATCH 15/16] Disable 404 handling for now --- .../frontend/desktop/linux/frontend.go | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index 02050f2c9..d9dc36028 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -24,7 +24,6 @@ import "C" import ( "context" "encoding/json" - "fmt" "log" "os" "strconv" @@ -345,22 +344,22 @@ func (f *Frontend) processRequest(request unsafe.Pointer) { content, mimeType, err := f.assets.Load(file) // TODO How to return 404/500 errors to webkit? - if err != nil { - if os.IsNotExist(err) { - f.dispatch(func() { - message := C.CString("not found") - defer C.free(unsafe.Pointer(message)) - C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(404), message)) - }) - } else { - err = fmt.Errorf("Error processing request %s: %w", uri, err) - f.logger.Error(err.Error()) - message := C.CString("internal server error") - defer C.free(unsafe.Pointer(message)) - C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(500), message)) - } - return - } + //if err != nil { + //if os.IsNotExist(err) { + // f.dispatch(func() { + // message := C.CString("not found") + // defer C.free(unsafe.Pointer(message)) + // C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(404), message)) + // }) + //} else { + // err = fmt.Errorf("Error processing request %s: %w", uri, err) + // f.logger.Error(err.Error()) + // message := C.CString("internal server error") + // defer C.free(unsafe.Pointer(message)) + // C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(500), message)) + //} + //return + //} cContent := C.CString(string(content)) defer C.free(unsafe.Pointer(cContent)) From 736d8b6b04b42e1251995977020313e11fd32c97 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 26 Jan 2022 14:16:36 +1100 Subject: [PATCH 16/16] Update docs --- website/docs/reference/runtime/dialog.mdx | 72 ++++++++++++++--------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/website/docs/reference/runtime/dialog.mdx b/website/docs/reference/runtime/dialog.mdx index fb47d34cb..88fc5ea40 100644 --- a/website/docs/reference/runtime/dialog.mdx +++ b/website/docs/reference/runtime/dialog.mdx @@ -75,16 +75,16 @@ type OpenDialogOptions struct { TreatPackagesAsDirectories bool } ``` -| Field | Description | Win | Mac | -| -------------------------- | ---------------------------------------------- | --- | --- | -| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ | -| DefaultFilename | The default filename | ✅ | ✅ | -| Title | Title for the dialog | ✅ | ✅ | -| [Filters](#filefilter) | A list of file filters | ✅ | ✅ | -| ShowHiddenFiles | Show files hidden by the system | | ✅ | -| CanCreateDirectories | Allow user to create directories | | ✅ | -| ResolvesAliases | If true, returns the file not the alias | | ✅ | -| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ | +| Field | Description | Win | Mac | Lin | +| -------------------------- | ---------------------------------------------- | --- | --- | --- | +| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ | ✅ | +| DefaultFilename | The default filename | ✅ | ✅ | ✅ | +| Title | Title for the dialog | ✅ | ✅ | ✅ | +| [Filters](#filefilter) | A list of file filters | ✅ | ✅ | ✅ | +| ShowHiddenFiles | Show files hidden by the system | | ✅ | ✅ | +| CanCreateDirectories | Allow user to create directories | | ✅ | | +| ResolvesAliases | If true, returns the file not the alias | | ✅ | | +| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ | | ### SaveDialogOptions @@ -101,15 +101,15 @@ type SaveDialogOptions struct { } ``` -| Field | Description | Win | Mac | -| -------------------------- | ---------------------------------------------- | --- | --- | -| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ | -| DefaultFilename | The default filename | ✅ | ✅ | -| Title | Title for the dialog | ✅ | ✅ | -| [Filters](#filefilter) | A list of file filters | ✅ | ✅ | -| ShowHiddenFiles | Show files hidden by the system | | ✅ | -| CanCreateDirectories | Allow user to create directories | | ✅ | -| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ | +| Field | Description | Win | Mac | Lin | +| -------------------------- | ---------------------------------------------- | --- | --- | --- | +| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ | ✅ | +| DefaultFilename | The default filename | ✅ | ✅ | ✅ | +| Title | Title for the dialog | ✅ | ✅ | ✅ | +| [Filters](#filefilter) | A list of file filters | ✅ | ✅ | ✅ | +| ShowHiddenFiles | Show files hidden by the system | | ✅ | ✅ | +| CanCreateDirectories | Allow user to create directories | | ✅ | | +| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ | | ### MessageDialogOptions @@ -123,20 +123,25 @@ type MessageDialogOptions struct { CancelButton string } ``` -| Field | Description | Win | Mac | -| ------------- | ------------------------------------------------------------------------- | --- | --- | -| Type | The type of message dialog, eg question, info... | ✅ | ✅ | -| Title | Title for the dialog | ✅ | ✅ | -| Message | The message to show the user | ✅ | ✅ | -| Buttons | A list of button titles | | ✅ | -| DefaultButton | The button with this text should be treated as default. Bound to `return` | | ✅ | -| CancelButton | The button with this text should be treated as cancel. Bound to `escape` | | ✅ | +| Field | Description | Win | Mac | Lin | +| ------------- | ------------------------------------------------------------------------- | --- | --- | --- | +| Type | The type of message dialog, eg question, info... | ✅ | ✅ | ✅ | +| Title | Title for the dialog | ✅ | ✅ | ✅ | +| Message | The message to show the user | ✅ | ✅ | ✅ | +| Buttons | A list of button titles | | ✅ | | +| DefaultButton | The button with this text should be treated as default. Bound to `return` | | ✅ | | +| CancelButton | The button with this text should be treated as cancel. Bound to `escape` | | ✅ | | #### Windows Windows has standard dialog types in which the buttons are not customisable. The value returned will be one of: "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "Try Again" or "Continue" +#### Linux + +Linux has standard dialog types in which the buttons are not customisable. +The value returned will be one of: "Ok", "Cancel", "Yes", "No" + #### Mac A message dialog on Mac may specify up to 4 buttons. If no `DefaultButton` or `CancelButton` is given, the first button @@ -222,6 +227,19 @@ dialog:

+#### Linux + +Linux allows you to use multiple file filters in dialog boxes. Each FileFilter will show up as a separate entry in the +dialog: + +
+ +
+
+
+
+ + #### Mac Mac dialogs only have the concept of a single set of patterns to filter files. If multiple FileFilters are provided,