5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-13 23:49:35 +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)
@ -131,6 +138,13 @@ var (
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)
gtkFileChooserDialogNew func(string, pointer, int, string, int, string, int, pointer) pointer
gtkFileChooserGetFilenames func(pointer) *GSList
gtkFileChooserSetCreateFolders func(pointer, int)
gtkFileChooserSetCurrentFolder func(pointer, string)
gtkFileChooserSetSelectMultiple func(pointer, int)
gtkFileChooserSetShowHidden func(pointer, int)
gtkImageNewFromPixbuf func(pointer) pointer gtkImageNewFromPixbuf func(pointer) pointer
gtkMenuBarNew func() pointer gtkMenuBarNew func() pointer
gtkMenuItemNewWithLabel func(string) pointer gtkMenuItemNewWithLabel func(string) 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
uris := unsafe.Slice(
(**C.char)(unsafe.Pointer(extracted)),
int(length))
var filenames []string
for _, uri := range uris {
if uri == nil {
break break
} }
filenames = append(filenames, strings.TrimPrefix(C.GoString(uri), "file://")) bytes = append(bytes, val)
p = unsafe.Add(p, 1)
} }
windowDragAndDropBuffer <- &dragAndDropMessage{ gFree(s) // so we don't have to iterate a second time
windowId: uint(*((*C.uint)(data))), return string(bytes)
filenames: filenames,
}
C.gtk_drag_finish(context, C.true, C.false, time)
} }
func onProcessRequest(request unsafe.Pointer, data unsafe.Pointer) { response := gtkDialogRun(fc)
windowId := uint(*((*C.uint)(data))) selections := []string{}
webviewRequests <- &webViewAssetRequest{ if response == GtkResponseAccept {
Request: webview.NewRequest(request), filenames := gtkFileChooserGetFilenames(fc)
windowId: windowId, iter := filenames
windowName: globalApplication.getWindowForID(windowId).Name(), count := 0
for {
selection := buildStringAndFree(iter.data)
selections = append(selections, selection)
iter = iter.next
if iter == nil || count == 1024 {
break
}
count++
} }
} }
*/ defer gtkWidgetDestroy(fc)
return selections, nil
}
// dialog related
func runOpenFileDialog(dialog *OpenFileDialog) ([]string, error) {
GtkFileChooserActionOpen := 0
// GtkFileChooserActionSave := 1
// GtkFileChooserActionSelectFolder := 2
// 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
}