mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-21 19:39:29 +08:00
Merge pull request #1089 from wailsapp/feature/fix-linux-dialogs
[linux] Fix dialogs
This commit is contained in:
commit
160b650833
@ -17,16 +17,5 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
// CreateApp creates the app!
|
// CreateApp creates the app!
|
||||||
func CreateApp(_ *options.App) (*App, error) {
|
func CreateApp(_ *options.App) (*App, error) {
|
||||||
// result := w32.MessageBox(0,
|
return nil, fmt.Errorf(`Wails applications will not build without the correct build tags.`)
|
||||||
// `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
|
|
||||||
}
|
}
|
||||||
|
@ -17,16 +17,5 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
// CreateApp creates the app!
|
// CreateApp creates the app!
|
||||||
func CreateApp(_ *options.App) (*App, error) {
|
func CreateApp(_ *options.App) (*App, error) {
|
||||||
// result := w32.MessageBox(0,
|
return nil, fmt.Errorf(`Wails applications will not build without the correct build tags.`)
|
||||||
// `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
|
|
||||||
}
|
}
|
||||||
|
@ -78,12 +78,14 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
ctx = context.WithValue(ctx, "events", eventHandler)
|
ctx = context.WithValue(ctx, "events", eventHandler)
|
||||||
messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings, eventHandler)
|
messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings, eventHandler)
|
||||||
|
|
||||||
appFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher)
|
debug := IsDebug()
|
||||||
eventHandler.AddFrontend(appFrontend)
|
ctx = context.WithValue(ctx, "debug", debug)
|
||||||
|
|
||||||
// Attach logger to context
|
// Attach logger to context
|
||||||
ctx = context.WithValue(ctx, "logger", myLogger)
|
ctx = context.WithValue(ctx, "logger", myLogger)
|
||||||
|
|
||||||
|
appFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher)
|
||||||
|
eventHandler.AddFrontend(appFrontend)
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
frontend: appFrontend,
|
frontend: appFrontend,
|
||||||
@ -91,13 +93,10 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
menuManager: menuManager,
|
menuManager: menuManager,
|
||||||
startupCallback: appoptions.OnStartup,
|
startupCallback: appoptions.OnStartup,
|
||||||
shutdownCallback: appoptions.OnShutdown,
|
shutdownCallback: appoptions.OnShutdown,
|
||||||
debug: IsDebug(),
|
debug: debug,
|
||||||
|
options: appoptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
result.options = appoptions
|
|
||||||
|
|
||||||
result.ctx = context.WithValue(result.ctx, "debug", result.debug)
|
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
32
v2/internal/frontend/desktop/linux/calloc.go
Normal file
32
v2/internal/frontend/desktop/linux/calloc.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
*/
|
||||||
|
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{}
|
||||||
|
}
|
@ -3,24 +3,87 @@
|
|||||||
|
|
||||||
package linux
|
package linux
|
||||||
|
|
||||||
import "github.com/wailsapp/wails/v2/internal/frontend"
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (string, error) {
|
/*
|
||||||
panic("implement me")
|
#include <stdlib.h>
|
||||||
|
#include "gtk/gtk.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
results := <-openFileResults
|
||||||
|
if len(results) == 1 {
|
||||||
|
return results[0], nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) {
|
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) {
|
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) {
|
func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) {
|
||||||
panic("implement me")
|
options := frontend.OpenDialogOptions{
|
||||||
|
DefaultDirectory: dialogOptions.DefaultDirectory,
|
||||||
|
DefaultFilename: dialogOptions.DefaultFilename,
|
||||||
|
Title: dialogOptions.Title,
|
||||||
|
Filters: dialogOptions.Filters,
|
||||||
|
ShowHiddenFiles: dialogOptions.ShowHiddenFiles,
|
||||||
|
CanCreateDirectories: dialogOptions.CanCreateDirectories,
|
||||||
|
}
|
||||||
|
f.mainWindow.OpenFileDialog(options, 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) {
|
func (f *Frontend) MessageDialog(dialogOptions frontend.MessageDialogOptions) (string, error) {
|
||||||
panic("implement me")
|
f.mainWindow.MessageDialog(dialogOptions)
|
||||||
|
return <-messageDialogResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//export processOpenFileResult
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
//export processMessageDialogResult
|
||||||
|
func processMessageDialogResult(result *C.char) {
|
||||||
|
messageDialogResult <- C.GoString(result)
|
||||||
}
|
}
|
||||||
|
@ -345,24 +345,22 @@ func (f *Frontend) processRequest(request unsafe.Pointer) {
|
|||||||
content, mimeType, err := f.assets.Load(file)
|
content, mimeType, err := f.assets.Load(file)
|
||||||
|
|
||||||
// TODO How to return 404/500 errors to webkit?
|
// TODO How to return 404/500 errors to webkit?
|
||||||
if err != nil {
|
//if err != nil {
|
||||||
var gerr *C.GError
|
//if os.IsNotExist(err) {
|
||||||
if os.IsNotExist(err) {
|
// f.dispatch(func() {
|
||||||
message := C.CString("not found")
|
// message := C.CString("not found")
|
||||||
defer C.free(unsafe.Pointer(message))
|
// defer C.free(unsafe.Pointer(message))
|
||||||
gerr = C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(404), message)
|
// C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(404), message))
|
||||||
C.webkit_uri_scheme_request_finish_error(req, gerr)
|
// })
|
||||||
} else {
|
//} else {
|
||||||
err = fmt.Errorf("Error processing request %s: %w", uri, err)
|
// err = fmt.Errorf("Error processing request %s: %w", uri, err)
|
||||||
f.logger.Error(err.Error())
|
// f.logger.Error(err.Error())
|
||||||
message := C.CString("internal server error")
|
// message := C.CString("internal server error")
|
||||||
defer C.free(unsafe.Pointer(message))
|
// defer C.free(unsafe.Pointer(message))
|
||||||
gerr = C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(500), message)
|
// C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(500), message))
|
||||||
C.webkit_uri_scheme_request_finish_error(req, gerr)
|
//}
|
||||||
}
|
//return
|
||||||
C.g_error_free(gerr)
|
//}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cContent := C.CString(string(content))
|
cContent := C.CString(string(content))
|
||||||
defer C.free(unsafe.Pointer(cContent))
|
defer C.free(unsafe.Pointer(cContent))
|
||||||
|
@ -142,7 +142,7 @@ void connectButtons(void* webview) {
|
|||||||
g_signal_connect(WEBKIT_WEB_VIEW(webview), "button-release-event", G_CALLBACK(buttonRelease), NULL);
|
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
|
// This is called when the close button on the window is pressed
|
||||||
gboolean close_button_pressed(GtkWidget *widget, GdkEvent *event, void* data)
|
gboolean close_button_pressed(GtkWidget *widget, GdkEvent *event, void* data)
|
||||||
@ -196,13 +196,188 @@ int executeJS(gpointer data) {
|
|||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecuteOnMainThread(JSCallback* jscallback) {
|
void ExecuteOnMainThread(void* f, gpointer jscallback) {
|
||||||
g_idle_add((GSourceFunc)executeJS, (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 {
|
||||||
|
void* webview;
|
||||||
|
char* title;
|
||||||
|
char* defaultFilename;
|
||||||
|
char* defaultDirectory;
|
||||||
|
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;
|
||||||
|
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,
|
||||||
|
label, GTK_RESPONSE_ACCEPT,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
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->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);
|
||||||
|
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) {
|
||||||
|
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(result);
|
||||||
|
}
|
||||||
|
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 "C"
|
||||||
import (
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -253,6 +428,7 @@ func NewWindow(appoptions *options.App, debug bool) *Window {
|
|||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
C.devtoolsEnabled(unsafe.Pointer(webview), C.int(1))
|
C.devtoolsEnabled(unsafe.Pointer(webview), C.int(1))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup window
|
// Setup window
|
||||||
@ -384,6 +560,7 @@ func (w *Window) Run() {
|
|||||||
case options.Maximised:
|
case options.Maximised:
|
||||||
w.Maximise()
|
w.Maximise()
|
||||||
}
|
}
|
||||||
|
|
||||||
C.gtk_main()
|
C.gtk_main()
|
||||||
w.Destroy()
|
w.Destroy()
|
||||||
}
|
}
|
||||||
@ -413,9 +590,9 @@ func (w *Window) SetTitle(title string) {
|
|||||||
func (w *Window) ExecJS(js string) {
|
func (w *Window) ExecJS(js string) {
|
||||||
jscallback := C.JSCallback{
|
jscallback := C.JSCallback{
|
||||||
webview: w.webview,
|
webview: w.webview,
|
||||||
script: C.CString(js),
|
script: C.CString(js),
|
||||||
}
|
}
|
||||||
C.ExecuteOnMainThread(&jscallback)
|
C.ExecuteOnMainThread(C.executeJS, C.gpointer(&jscallback))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) StartDrag() {
|
func (w *Window) StartDrag() {
|
||||||
@ -425,3 +602,77 @@ func (w *Window) StartDrag() {
|
|||||||
func (w *Window) Quit() {
|
func (w *Window) Quit() {
|
||||||
C.gtk_main_quit()
|
C.gtk_main_quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multipleFiles int, action C.GtkFileChooserAction) {
|
||||||
|
|
||||||
|
data := C.OpenFileDialogOptions{
|
||||||
|
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] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dialogOptions.DefaultDirectory != "" {
|
||||||
|
data.defaultDirectory = C.CString(dialogOptions.DefaultDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
@ -75,16 +75,16 @@ type OpenDialogOptions struct {
|
|||||||
TreatPackagesAsDirectories bool
|
TreatPackagesAsDirectories bool
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
| Field | Description | Win | Mac |
|
| Field | Description | Win | Mac | Lin |
|
||||||
| -------------------------- | ---------------------------------------------- | --- | --- |
|
| -------------------------- | ---------------------------------------------- | --- | --- | --- |
|
||||||
| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ |
|
| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ | ✅ |
|
||||||
| DefaultFilename | The default filename | ✅ | ✅ |
|
| DefaultFilename | The default filename | ✅ | ✅ | ✅ |
|
||||||
| Title | Title for the dialog | ✅ | ✅ |
|
| Title | Title for the dialog | ✅ | ✅ | ✅ |
|
||||||
| [Filters](#filefilter) | A list of file filters | ✅ | ✅ |
|
| [Filters](#filefilter) | A list of file filters | ✅ | ✅ | ✅ |
|
||||||
| ShowHiddenFiles | Show files hidden by the system | | ✅ |
|
| ShowHiddenFiles | Show files hidden by the system | | ✅ | ✅ |
|
||||||
| CanCreateDirectories | Allow user to create directories | | ✅ |
|
| CanCreateDirectories | Allow user to create directories | | ✅ | |
|
||||||
| ResolvesAliases | If true, returns the file not the alias | | ✅ |
|
| ResolvesAliases | If true, returns the file not the alias | | ✅ | |
|
||||||
| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ |
|
| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ | |
|
||||||
|
|
||||||
|
|
||||||
### SaveDialogOptions
|
### SaveDialogOptions
|
||||||
@ -101,15 +101,15 @@ type SaveDialogOptions struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
| Field | Description | Win | Mac |
|
| Field | Description | Win | Mac | Lin |
|
||||||
| -------------------------- | ---------------------------------------------- | --- | --- |
|
| -------------------------- | ---------------------------------------------- | --- | --- | --- |
|
||||||
| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ |
|
| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ | ✅ |
|
||||||
| DefaultFilename | The default filename | ✅ | ✅ |
|
| DefaultFilename | The default filename | ✅ | ✅ | ✅ |
|
||||||
| Title | Title for the dialog | ✅ | ✅ |
|
| Title | Title for the dialog | ✅ | ✅ | ✅ |
|
||||||
| [Filters](#filefilter) | A list of file filters | ✅ | ✅ |
|
| [Filters](#filefilter) | A list of file filters | ✅ | ✅ | ✅ |
|
||||||
| ShowHiddenFiles | Show files hidden by the system | | ✅ |
|
| ShowHiddenFiles | Show files hidden by the system | | ✅ | ✅ |
|
||||||
| CanCreateDirectories | Allow user to create directories | | ✅ |
|
| CanCreateDirectories | Allow user to create directories | | ✅ | |
|
||||||
| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ |
|
| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ | |
|
||||||
|
|
||||||
### MessageDialogOptions
|
### MessageDialogOptions
|
||||||
|
|
||||||
@ -123,20 +123,25 @@ type MessageDialogOptions struct {
|
|||||||
CancelButton string
|
CancelButton string
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
| Field | Description | Win | Mac |
|
| Field | Description | Win | Mac | Lin |
|
||||||
| ------------- | ------------------------------------------------------------------------- | --- | --- |
|
| ------------- | ------------------------------------------------------------------------- | --- | --- | --- |
|
||||||
| Type | The type of message dialog, eg question, info... | ✅ | ✅ |
|
| Type | The type of message dialog, eg question, info... | ✅ | ✅ | ✅ |
|
||||||
| Title | Title for the dialog | ✅ | ✅ |
|
| Title | Title for the dialog | ✅ | ✅ | ✅ |
|
||||||
| Message | The message to show the user | ✅ | ✅ |
|
| Message | The message to show the user | ✅ | ✅ | ✅ |
|
||||||
| Buttons | A list of button titles | | ✅ |
|
| Buttons | A list of button titles | | ✅ | |
|
||||||
| DefaultButton | The button with this text should be treated as default. Bound to `return` | | ✅ |
|
| 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` | | ✅ |
|
| CancelButton | The button with this text should be treated as cancel. Bound to `escape` | | ✅ | |
|
||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
|
|
||||||
Windows has standard dialog types in which the buttons are not customisable.
|
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"
|
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
|
#### Mac
|
||||||
|
|
||||||
A message dialog on Mac may specify up to 4 buttons. If no `DefaultButton` or `CancelButton` is given, the first button
|
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:
|
|||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
#### Linux
|
||||||
|
|
||||||
|
Linux allows you to use multiple file filters in dialog boxes. Each FileFilter will show up as a separate entry in the
|
||||||
|
dialog:
|
||||||
|
|
||||||
|
<div class="text--center">
|
||||||
|
<img src="/img/runtime/dialog_lin_filters.png" width="50%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
#### Mac
|
#### Mac
|
||||||
|
|
||||||
Mac dialogs only have the concept of a single set of patterns to filter files. If multiple FileFilters are provided,
|
Mac dialogs only have the concept of a single set of patterns to filter files. If multiple FileFilters are provided,
|
||||||
|
Loading…
Reference in New Issue
Block a user