5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-11 22:49:29 +08:00

[v3 linux/purego] initial file/directory dialogs impl

This commit is contained in:
Travis McLane 2023-06-21 16:31:26 -05:00
parent d52c26e82f
commit e92858f64d
2 changed files with 190 additions and 182 deletions

View File

@ -54,52 +54,7 @@ func newOpenFileDialogImpl(d *OpenFileDialog) *linuxOpenFileDialog {
} }
func (m *linuxOpenFileDialog) show() ([]string, error) { func (m *linuxOpenFileDialog) show() ([]string, error) {
openFileResponses[m.dialog.id] = make(chan string) return runOpenFileDialog(m.dialog)
// nsWindow := unsafe.Pointer(nil)
if m.dialog.window != nil {
// get NSWindow from window
//nsWindow = m.dialog.window.impl.(*macosWebviewWindow).nsWindow
}
// Massage filter patterns into macOS format
// We iterate all filter patterns, tidy them up and then join them with a semicolon
// This should produce a single string of extensions like "png;jpg;gif"
// var filterPatterns string
// if len(m.dialog.filters) > 0 {
// var allPatterns []string
// for _, filter := range m.dialog.filters {
// patternComponents := strings.Split(filter.Pattern, ";")
// for i, component := range patternComponents {
// filterPattern := strings.TrimSpace(component)
// filterPattern = strings.TrimPrefix(filterPattern, "*.")
// patternComponents[i] = filterPattern
// }
// allPatterns = append(allPatterns, strings.Join(patternComponents, ";"))
// }
// filterPatterns = strings.Join(allPatterns, ";")
// }
// 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),
// C.bool(m.dialog.resolvesAliases),
// C.bool(m.dialog.hideExtension),
// C.bool(m.dialog.treatsFilePackagesAsDirectories),
// C.bool(m.dialog.allowsOtherFileTypes),
// toCString(filterPatterns),
// C.uint(len(filterPatterns)),
// toCString(m.dialog.message),
// toCString(m.dialog.directory),
// toCString(m.dialog.buttonText),
// nsWindow)
var result []string
for filename := range openFileResponses[m.dialog.id] {
result = append(result, filename)
}
return result, nil
} }
type linuxSaveFileDialog struct { type linuxSaveFileDialog struct {
@ -113,24 +68,5 @@ func newSaveFileDialogImpl(d *SaveFileDialog) *linuxSaveFileDialog {
} }
func (m *linuxSaveFileDialog) show() (string, error) { func (m *linuxSaveFileDialog) show() (string, error) {
saveFileResponses[m.dialog.id] = make(chan string) return runSaveFileDialog(m.dialog)
// nsWindow := unsafe.Pointer(nil)
if m.dialog.window != nil {
// get NSWindow from window
// nsWindow = m.dialog.window.impl.(*linuxWebviewWindow).nsWindow
}
// C.showSaveFileDialog(C.uint(m.dialog.id),
// C.bool(m.dialog.canCreateDirectories),
// C.bool(m.dialog.showHiddenFiles),
// C.bool(m.dialog.canSelectHiddenExtension),
// C.bool(m.dialog.hideExtension),
// C.bool(m.dialog.treatsFilePackagesAsDirectories),
// C.bool(m.dialog.allowOtherFileTypes),
// toCString(m.dialog.message),
// toCString(m.dialog.directory),
// toCString(m.dialog.buttonText),
// toCString(m.dialog.filename),
// nsWindow)
return <-saveFileResponses[m.dialog.id], nil
} }

View File

@ -16,7 +16,13 @@ import (
type windowPointer uintptr type windowPointer uintptr
type identifier uint type identifier uint
type pointer uintptr type pointer uintptr
type GSList uintptr
// type GSList uintptr
type GSList struct {
data pointer
next *GSList
}
type GSListPointer *GSList type GSListPointer *GSList
const ( const (
@ -93,6 +99,7 @@ var (
gApplicationRun func(pointer, int, []string) int gApplicationRun func(pointer, int, []string) int
gBytesNewStatic func(uintptr, int) uintptr gBytesNewStatic func(uintptr, int) uintptr
gBytesUnref func(uintptr) gBytesUnref func(uintptr)
gFree func(pointer)
gIdleAdd func(uintptr) gIdleAdd func(uintptr)
gObjectRefSink func(pointer) gObjectRefSink func(pointer)
gObjectUnref func(pointer) gObjectUnref func(pointer)
@ -116,63 +123,70 @@ var (
gdkWindowGetDisplay func(pointer) pointer gdkWindowGetDisplay func(pointer) pointer
// gtk functions // gtk functions
gtkApplicationNew func(string, uint) pointer gtkApplicationNew func(string, uint) pointer
gtkApplicationGetActiveWindow func(pointer) pointer gtkApplicationGetActiveWindow func(pointer) pointer
gtkApplicationGetWindows func(pointer) *GList gtkApplicationGetWindows func(pointer) *GList
gtkApplicationWindowNew func(pointer) pointer gtkApplicationWindowNew func(pointer) pointer
gtkBoxNew func(int, int) pointer gtkBoxNew func(int, int) pointer
gtkBoxPackStart func(pointer, pointer, int, int, int) gtkBoxPackStart func(pointer, pointer, int, int, int)
gtkCheckMenuItemGetActive func(pointer) int gtkCheckMenuItemGetActive func(pointer) int
gtkCheckMenuItemNewWithLabel func(string) pointer gtkCheckMenuItemNewWithLabel func(string) pointer
gtkCheckMenuItemSetActive func(pointer, int) gtkCheckMenuItemSetActive func(pointer, int)
gtkContainerAdd func(pointer, pointer) gtkContainerAdd func(pointer, pointer)
gtkDialogAddButton func(pointer, string, int) gtkDialogAddButton func(pointer, string, int)
gtkDialogGetContentArea func(pointer) pointer gtkDialogGetContentArea func(pointer) pointer
gtkDialogRun func(pointer) int gtkDialogRun func(pointer) int
gtkDialogSetDefaultResponse func(pointer, int) gtkDialogSetDefaultResponse func(pointer, int)
gtkDragDestSet func(pointer, uint, pointer, uint, uint) gtkDragDestSet func(pointer, uint, pointer, uint, uint)
gtkImageNewFromPixbuf func(pointer) pointer gtkFileChooserDialogNew func(string, pointer, int, string, int, string, int, pointer) pointer
gtkMenuBarNew func() pointer gtkFileChooserGetFilenames func(pointer) *GSList
gtkMenuItemNewWithLabel func(string) pointer gtkFileChooserSetCreateFolders func(pointer, int)
gtkMenuItemSetLabel func(pointer, string) gtkFileChooserSetCurrentFolder func(pointer, string)
gtkMenuItemSetSubmenu func(pointer, pointer) gtkFileChooserSetSelectMultiple func(pointer, int)
gtkMenuNew func() pointer gtkFileChooserSetShowHidden func(pointer, int)
gtkMenuShellAppend func(pointer, pointer)
gtkMessageDialogNew func(pointer, int, int, int, string) pointer gtkImageNewFromPixbuf func(pointer) pointer
gtkRadioMenuItemGetGroup func(pointer) GSListPointer gtkMenuBarNew func() pointer
gtkRadioMenuItemNewWithLabel func(GSListPointer, string) pointer gtkMenuItemNewWithLabel func(string) pointer
gtkSeparatorMenuItemNew func() pointer gtkMenuItemSetLabel func(pointer, string)
gtkTargetEntryFree func(pointer) gtkMenuItemSetSubmenu func(pointer, pointer)
gtkTargetEntryNew func(string, int, uint) pointer gtkMenuNew func() pointer
gtkWidgetDestroy func(pointer) gtkMenuShellAppend func(pointer, pointer)
gtkWidgetGetDisplay func(pointer) pointer gtkMessageDialogNew func(pointer, int, int, int, string) pointer
gtkWidgetGetWindow func(pointer) pointer gtkRadioMenuItemGetGroup func(pointer) GSListPointer
gtkWidgetGetScreen func(pointer) pointer gtkRadioMenuItemNewWithLabel func(GSListPointer, string) pointer
gtkWidgetHide func(pointer) gtkSeparatorMenuItemNew func() pointer
gtkWidgetIsVisible func(pointer) bool gtkTargetEntryFree func(pointer)
gtkWidgetShow func(pointer) gtkTargetEntryNew func(string, int, uint) pointer
gtkWidgetShowAll func(pointer) gtkWidgetDestroy func(pointer)
gtkWidgetSetAppPaintable func(pointer, int) gtkWidgetGetDisplay func(pointer) pointer
gtkWidgetSetSensitive func(pointer, int) gtkWidgetGetWindow func(pointer) pointer
gtkWidgetSetToolTipText func(pointer, string) gtkWidgetGetScreen func(pointer) pointer
gtkWidgetSetVisual func(pointer, pointer) gtkWidgetHide func(pointer)
gtkWindowClose func(pointer) gtkWidgetIsVisible func(pointer) bool
gtkWindowFullScreen func(pointer) gtkWidgetShow func(pointer)
gtkWindowGetPosition func(pointer, *int, *int) bool gtkWidgetShowAll func(pointer)
gtkWindowGetSize func(pointer, *int, *int) gtkWidgetSetAppPaintable func(pointer, int)
gtkWindowKeepAbove func(pointer, bool) gtkWidgetSetSensitive func(pointer, int)
gtkWindowMaximize func(pointer) gtkWidgetSetToolTipText func(pointer, string)
gtkWindowMinimize func(pointer) gtkWidgetSetVisual func(pointer, pointer)
gtkWindowMove func(pointer, int, int) gtkWindowClose func(pointer)
gtkWindowPresent func(pointer) gtkWindowFullScreen func(pointer)
gtkWindowResize func(pointer, int, int) gtkWindowGetPosition func(pointer, *int, *int) bool
gtkWindowSetDecorated func(pointer, int) gtkWindowGetSize func(pointer, *int, *int)
gtkWindowSetGeometryHints func(pointer, pointer, pointer, int) gtkWindowKeepAbove func(pointer, bool)
gtkWindowSetKeepAbove func(pointer, bool) gtkWindowMaximize func(pointer)
gtkWindowSetResizable func(pointer, bool) gtkWindowMinimize func(pointer)
gtkWindowSetTitle func(pointer, string) gtkWindowMove func(pointer, int, int)
gtkWindowUnfullscreen func(pointer) gtkWindowPresent func(pointer)
gtkWindowUnmaximize func(pointer) gtkWindowResize func(pointer, int, int)
gtkWindowSetDecorated func(pointer, int)
gtkWindowSetGeometryHints func(pointer, pointer, pointer, int)
gtkWindowSetKeepAbove func(pointer, bool)
gtkWindowSetResizable func(pointer, bool)
gtkWindowSetTitle func(pointer, string)
gtkWindowUnfullscreen func(pointer)
gtkWindowUnmaximize func(pointer)
// webkit // webkit
webkitNewWithUserContentManager func(pointer) pointer webkitNewWithUserContentManager func(pointer) pointer
@ -227,6 +241,7 @@ func init() {
purego.RegisterLibFunc(&gApplicationRun, gtk, "g_application_run") purego.RegisterLibFunc(&gApplicationRun, gtk, "g_application_run")
purego.RegisterLibFunc(&gBytesNewStatic, gtk, "g_bytes_new_static") purego.RegisterLibFunc(&gBytesNewStatic, gtk, "g_bytes_new_static")
purego.RegisterLibFunc(&gBytesUnref, gtk, "g_bytes_unref") purego.RegisterLibFunc(&gBytesUnref, gtk, "g_bytes_unref")
purego.RegisterLibFunc(&gFree, gtk, "g_free")
purego.RegisterLibFunc(&gIdleAdd, gtk, "g_idle_add") purego.RegisterLibFunc(&gIdleAdd, gtk, "g_idle_add")
purego.RegisterLibFunc(&gObjectRefSink, gtk, "g_object_ref_sink") purego.RegisterLibFunc(&gObjectRefSink, gtk, "g_object_ref_sink")
purego.RegisterLibFunc(&gObjectUnref, gtk, "g_object_unref") purego.RegisterLibFunc(&gObjectUnref, gtk, "g_object_unref")
@ -265,6 +280,12 @@ func init() {
purego.RegisterLibFunc(&gtkDialogRun, gtk, "gtk_dialog_run") purego.RegisterLibFunc(&gtkDialogRun, gtk, "gtk_dialog_run")
purego.RegisterLibFunc(&gtkDialogSetDefaultResponse, gtk, "gtk_dialog_set_default_response") purego.RegisterLibFunc(&gtkDialogSetDefaultResponse, gtk, "gtk_dialog_set_default_response")
purego.RegisterLibFunc(&gtkDragDestSet, gtk, "gtk_drag_dest_set") purego.RegisterLibFunc(&gtkDragDestSet, gtk, "gtk_drag_dest_set")
purego.RegisterLibFunc(&gtkFileChooserDialogNew, gtk, "gtk_file_chooser_dialog_new")
purego.RegisterLibFunc(&gtkFileChooserGetFilenames, gtk, "gtk_file_chooser_get_filenames")
purego.RegisterLibFunc(&gtkFileChooserSetCreateFolders, gtk, "gtk_file_chooser_set_create_folders")
purego.RegisterLibFunc(&gtkFileChooserSetCurrentFolder, gtk, "gtk_file_chooser_set_current_folder")
purego.RegisterLibFunc(&gtkFileChooserSetSelectMultiple, gtk, "gtk_file_chooser_set_select_multiple")
purego.RegisterLibFunc(&gtkFileChooserSetShowHidden, gtk, "gtk_file_chooser_set_show_hidden")
purego.RegisterLibFunc(&gtkImageNewFromPixbuf, gtk, "gtk_image_new_from_pixbuf") purego.RegisterLibFunc(&gtkImageNewFromPixbuf, gtk, "gtk_image_new_from_pixbuf")
purego.RegisterLibFunc(&gtkMenuItemSetLabel, gtk, "gtk_menu_item_set_label") purego.RegisterLibFunc(&gtkMenuItemSetLabel, gtk, "gtk_menu_item_set_label")
purego.RegisterLibFunc(&gtkMenuBarNew, gtk, "gtk_menu_bar_new") purego.RegisterLibFunc(&gtkMenuBarNew, gtk, "gtk_menu_bar_new")
@ -919,79 +940,103 @@ func windowMove(window pointer, x, y int) {
gtkWindowMove(window, x, y) gtkWindowMove(window, x, y)
} }
/* func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden bool, currentFolder, title string, action int, acceptLabel string, filters []FileFilter) ([]string, error) {
func onButtonEvent(_ pointer, event *C.GdkEventButton, data unsafe.Pointer) C.gboolean { GtkResponseCancel := 0
// Constants (defined here to be easier to use with ) GtkResponseAccept := 1
GdkButtonPress := C.GDK_BUTTON_PRESS // 4
Gdk2ButtonPress := C.GDK_2BUTTON_PRESS // 5 for double-click
GdkButtonRelease := C.GDK_BUTTON_RELEASE // 7
windowId := uint(*((*C.uint)(data))) fc := gtkFileChooserDialogNew(
window := globalApplication.getWindowForID(windowId) title,
if window == nil { window,
return C.gboolean(0) action,
} "_Cancel",
lw, ok := (window.impl).(*linuxWebviewWindow) GtkResponseCancel,
if !ok { acceptLabel,
return C.gboolean(0) GtkResponseAccept,
0)
for _, filter := range filters {
// TODO: Process and add filters
// gtk_file_chooser_add_filter(fc, thisFilter)
fmt.Println("filter", filter)
} }
if event == nil { if allowMultiple {
return C.gboolean(0) gtkFileChooserSetSelectMultiple(fc, 1)
}
if event.button == 3 {
return C.gboolean(0)
} }
switch int(event._type) { if createFolders {
case GdkButtonPress: gtkFileChooserSetCreateFolders(fc, 1)
lw.startDrag(uint(event.button), int(event.x_root), int(event.y_root))
case Gdk2ButtonPress:
fmt.Printf("%d - button %d - double-clicked\n", windowId, int(event.button))
case GdkButtonRelease:
lw.endDrag(uint(event.button), int(event.x_root), int(event.y_root))
} }
return C.gboolean(0) if showHidden {
} gtkFileChooserSetShowHidden(fc, 1)
}
if currentFolder != "" {
gtkFileChooserSetCurrentFolder(fc, currentFolder)
}
func onDragNDrop(target unsafe.Pointer, context *C.GdkDragContext, x C.gint, y C.gint, seldata unsafe.Pointer, info C.guint, time C.guint, data unsafe.Pointer) { buildStringAndFree := func(s pointer) string {
fmt.Println("target", target, info) bytes := []byte{}
var length C.gint p := unsafe.Pointer(s)
selection := unsafe.Pointer(C.gtk_selection_data_get_data_with_length((*C.GtkSelectionData)(seldata), &length)) for {
extracted := C.g_uri_list_extract_uris((*C.char)(selection)) val := *(*byte)(p)
defer C.g_strfreev(extracted) if val == 0 { // this is the null terminator
break
uris := unsafe.Slice( }
(**C.char)(unsafe.Pointer(extracted)), bytes = append(bytes, val)
int(length)) p = unsafe.Add(p, 1)
var filenames []string
for _, uri := range uris {
if uri == nil {
break
} }
filenames = append(filenames, strings.TrimPrefix(C.GoString(uri), "file://")) gFree(s) // so we don't have to iterate a second time
return string(bytes)
} }
windowDragAndDropBuffer <- &dragAndDropMessage{
windowId: uint(*((*C.uint)(data))), response := gtkDialogRun(fc)
filenames: filenames, selections := []string{}
if response == GtkResponseAccept {
filenames := gtkFileChooserGetFilenames(fc)
iter := filenames
count := 0
for {
selection := buildStringAndFree(iter.data)
selections = append(selections, selection)
iter = iter.next
if iter == nil || count == 1024 {
break
}
count++
}
} }
C.gtk_drag_finish(context, C.true, C.false, time) defer gtkWidgetDestroy(fc)
return selections, nil
} }
func onProcessRequest(request unsafe.Pointer, data unsafe.Pointer) { // dialog related
windowId := uint(*((*C.uint)(data))) func runOpenFileDialog(dialog *OpenFileDialog) ([]string, error) {
webviewRequests <- &webViewAssetRequest{ GtkFileChooserActionOpen := 0
Request: webview.NewRequest(request), // GtkFileChooserActionSave := 1
windowId: windowId, // GtkFileChooserActionSelectFolder := 2
windowName: globalApplication.getWindowForID(windowId).Name(), // GtkFileChooserActionCreateFolder := 3
}
} // (dialog.window.impl).(*linuxWebviewWindow).window // FIXME: dialog.window == nil!
*/ window := pointer(0)
buttonText := dialog.buttonText
if buttonText == "" {
buttonText = "_Open"
}
return runChooserDialog(
window,
dialog.allowsMultipleSelection,
dialog.canCreateDirectories,
dialog.showHiddenFiles,
dialog.directory,
dialog.title,
GtkFileChooserActionOpen,
buttonText,
dialog.filters)
}
// dialog reloated
func runQuestionDialog(parent pointer, options *MessageDialog) int { func runQuestionDialog(parent pointer, options *MessageDialog) int {
dType, ok := map[DialogType]int{ dType, ok := map[DialogType]int{
InfoDialog: GtkMessageInfo, InfoDialog: GtkMessageInfo,
@ -1052,3 +1097,30 @@ func runQuestionDialog(parent pointer, options *MessageDialog) int {
defer gtkWidgetDestroy(dialog) defer gtkWidgetDestroy(dialog)
return gtkDialogRun(dialog) return gtkDialogRun(dialog)
} }
func runSaveFileDialog(dialog *SaveFileDialog) (string, error) {
GtkFileChooserActionSave := 1
// GtkFileChooserActionSelectFolder := 2
// GtkFileChooserActionCreateFolder := 3
window := pointer(0)
buttonText := dialog.buttonText
if buttonText == "" {
buttonText = "_Save"
}
results, err := runChooserDialog(
window,
false, // multiple selection
dialog.canCreateDirectories,
dialog.showHiddenFiles,
dialog.directory,
dialog.title,
GtkFileChooserActionSave,
buttonText,
dialog.filters)
if err != nil || len(results) == 0 {
return "", err
}
return results[0], nil
}