diff --git a/ffi/filechooser.lua b/ffi/filechooser.lua new file mode 100644 index 000000000..29207fd49 --- /dev/null +++ b/ffi/filechooser.lua @@ -0,0 +1,14 @@ +local ffi = require("ffi") +local util = require("ffi/util") + +if util.isSDL() then + if util.haveSDL2() then + local ok, gtk = pcall(ffi.load, "gtk-3") + if not ok then + ok, gtk = pcall(ffi.load, "gtk-3.so.0") + end + if ok then + return require("ffi/filechooser_gtk-3") + end + end +end diff --git a/ffi/filechooser_gtk-3.lua b/ffi/filechooser_gtk-3.lua new file mode 100644 index 000000000..f8dbb9f5a --- /dev/null +++ b/ffi/filechooser_gtk-3.lua @@ -0,0 +1,56 @@ +local ffi = require "ffi" + +local dummy = require("ffi/gtk-3_h") + +local ok, gtk = pcall(ffi.load, "gtk-3") +if not ok then + ok, gtk = pcall(ffi.load, "gtk-3.so.0") +end +if not ok then return end + +local FileChooser = { + type = "gtk-3", +} + +--[[ +Mostly taken from https://love2d.org/forums/viewtopic.php?f=4&t=82442 + +Proof of concept, https://github.com/Alloyed/nativefiledialog might be better. +--]] + +function FileChooser:show(action, button, title) + gtk.gtk_init(nil, nil) + + local d = gtk.gtk_file_chooser_dialog_new( + title, + nil, + action, + "_Cancel", ffi.cast("const gchar *", gtk.GTK_RESPONSE_CANCEL), + button, ffi.cast("const gchar *", gtk.GTK_RESPONSE_OK), + nil) + + local response = gtk.gtk_dialog_run(d) + local filename = gtk.gtk_file_chooser_get_filename(d) + + gtk.gtk_widget_destroy(d) + + while gtk.gtk_events_pending() do + gtk.gtk_main_iteration() + end + + if response == gtk.GTK_RESPONSE_OK then + return filename ~= nil and ffi.string(filename) or nil + end +end + +function FileChooser:save(title) + return self:show(gtk.GTK_FILE_CHOOSER_ACTION_SAVE, + "_Save", title or "Save As") +end + +function FileChooser:open(title) + return self:show(gtk.GTK_FILE_CHOOSER_ACTION_OPEN, + "_Open", title or "Open") +end + +return FileChooser diff --git a/ffi/gtk-3_h.lua b/ffi/gtk-3_h.lua new file mode 100644 index 000000000..46c0ff524 --- /dev/null +++ b/ffi/gtk-3_h.lua @@ -0,0 +1,70 @@ +local ffi = require "ffi" + +ffi.cdef [[ + +typedef void GtkDialog; +typedef void GtkWidget; +typedef void GtkWindow; +typedef void GtkFileChooser; + +typedef int gint; +typedef char gchar; +typedef bool gboolean; + +typedef enum +{ + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER +} GtkFileChooserAction; + +typedef enum +{ + GTK_RESPONSE_NONE = -1, + GTK_RESPONSE_REJECT = -2, + GTK_RESPONSE_ACCEPT = -3, + GTK_RESPONSE_DELETE_EVENT = -4, + GTK_RESPONSE_OK = -5, + GTK_RESPONSE_CANCEL = -6, + GTK_RESPONSE_CLOSE = -7, + GTK_RESPONSE_YES = -8, + GTK_RESPONSE_NO = -9, + GTK_RESPONSE_APPLY = -10, + GTK_RESPONSE_HELP = -11 +} GtkResponseType; + +void gtk_init ( + int *argc, + char ***argv +); + +gboolean gtk_events_pending ( + void +); + +gboolean gtk_main_iteration ( + void +); + +GtkWidget * gtk_file_chooser_dialog_new ( + const gchar *title, + GtkWindow *parent, + GtkFileChooserAction action, + const gchar *first_button_text, + ... +); + +gint gtk_dialog_run ( + GtkDialog *dialog +); + +void gtk_widget_destroy ( + GtkWidget *widget +); + +gchar * gtk_file_chooser_get_filename ( + GtkFileChooser *chooser +); + +]] diff --git a/ffi/input_SDL2_0.lua b/ffi/input_SDL2_0.lua index 0bf6f5cbe..f68f2fd1b 100644 --- a/ffi/input_SDL2_0.lua +++ b/ffi/input_SDL2_0.lua @@ -1,3 +1,4 @@ +local FileChooser = require("ffi/filechooser") -- load common SDL input/video library local SDL = require("ffi/SDL2_0") @@ -11,4 +12,5 @@ return { hasClipboardText = SDL.hasClipboardText, getClipboardText = SDL.getClipboardText, setClipboardText = SDL.setClipboardText, + file_chooser = FileChooser, }