From 951773452a64dd0ae80ae26955d762dc25cdd8fd Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 22 Oct 2022 23:52:23 -0400 Subject: [PATCH] wip: Add async api to choose a file This is an experiment to replace explicit use of chooser dialogs with an async API. --- gtk/gtkfilechooser.h | 28 ++++++++ gtk/gtkfilechooserdialog.c | 137 +++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) diff --git a/gtk/gtkfilechooser.h b/gtk/gtkfilechooser.h index e5b68de3dd..adcdaf1e88 100644 --- a/gtk/gtkfilechooser.h +++ b/gtk/gtkfilechooser.h @@ -183,6 +183,34 @@ GDK_AVAILABLE_IN_ALL const char * gtk_file_chooser_get_choice (GtkFileChooser *chooser, const char *id); + +typedef void (*GtkFileChooserPrepareCallback) (GtkFileChooser *chooser, + gpointer user_data); + +GDK_AVAILABLE_IN_ALL +void gtk_choose_file (GtkWindow *parent, + const char *title, + GtkFileChooserAction action, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GDK_AVAILABLE_IN_ALL +void gtk_choose_file_full (GtkWindow *parent, + const char *title, + GtkFileChooserAction action, + GtkFileChooserPrepareCallback prepare, + gpointer prepare_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_choose_file_finish (GtkFileChooser *chooser, + GAsyncResult *result, + GError **error); + + G_END_DECLS #endif /* __GTK_FILE_CHOOSER_H__ */ diff --git a/gtk/gtkfilechooserdialog.c b/gtk/gtkfilechooserdialog.c index 853d304952..dc283c7272 100644 --- a/gtk/gtkfilechooserdialog.c +++ b/gtk/gtkfilechooserdialog.c @@ -735,3 +735,140 @@ gtk_file_chooser_dialog_new (const char *title, return result; } + + +static void +cancelled_cb (GCancellable *cancellable, + GtkDialog *dialog) +{ + gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL); +} + +static void +choose_response_cb (GtkDialog *dialog, + int response, + GTask *task) +{ + GCancellable *cancellable = g_task_get_cancellable (task); + + if (cancellable) + g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, dialog); + + if (response == GTK_RESPONSE_OK) + g_task_return_boolean (task, TRUE); + else + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled"); + + g_object_unref (task); + gtk_window_destroy (GTK_WINDOW (dialog)); +} + +/** + * gtk_choose_file: + * @parent: (nullable): parent window + * @title: title for the font chooser + * @action: the action for the file chooser + * @cancellable: (nullable): a `GCancellable` to cancel the operation + * @callback: (scope async): callback to call when the action is complete + * @user_data: (closure callback): data to pass to @callback + * + * This function presents a file chooser to let the user + * pick a file. + * + * The @callback will be called when the dialog is closed. + * It should call [function@Gtk.choose_file_finish] to + * find out whether the operation was completed successfully, + * and use [class@Gtk.FileChooser] API to obtain the results. + */ +void +gtk_choose_file (GtkWindow *parent, + const char *title, + GtkFileChooserAction action, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + gtk_choose_file_full (parent, title, action, NULL, NULL, cancellable, callback, user_data); +} + +/** + * gtk_choose_file_full: + * @parent: (nullable): parent window + * @title: title for the file chooser + * @action: the action for the file chooser + * @prepare: (nullable) (scope call): callback to set up the file chooser + * @prepare_data: (closure prepare): data to pass to @prepare + * @cancellable: (nullable): a `GCancellable` to cancel the operation + * @callback: (scope async): callback to call when the action is complete + * @user_data: (closure callback): data to pass to @callback + * + * This function presents a file chooser to let the user + * choose a file. + * + * In addition to [function@Gtk.choose_file], this function takes + * a @prepare callback that lets you set up the file chooser according + * to your needs. + * + * The @callback will be called when the dialog is closed. + * It should use [function@Gtk.choose_file_finish] to find + * out whether the operation was completed successfully, + * and use [class@Gtk.FileChooser] API to obtain the results. + */ +void +gtk_choose_file_full (GtkWindow *parent, + const char *title, + GtkFileChooserAction action, + GtkFileChooserPrepareCallback prepare, + gpointer prepare_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GtkWidget *dialog; + GTask *task; + const char *button[] = { + N_("_Open"), + N_("_Save"), + N_("_Select") + }; + + dialog = gtk_file_chooser_dialog_new (title, parent, action, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _(button[action]), GTK_RESPONSE_OK, + NULL); + + if (prepare) + prepare (GTK_FILE_CHOOSER (dialog), prepare); + + if (cancellable) + g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), dialog); + + task = g_task_new (dialog, cancellable, callback, user_data); + g_task_set_source_tag (task, gtk_choose_file_full); + + g_signal_connect (dialog, "response", G_CALLBACK (choose_response_cb), task); + + gtk_window_present (GTK_WINDOW (dialog)); +} + +/** + * gtk_choose_file_finish: + * @chooser: the `GtkFileChooser` + * @result: `GAsyncResult` that was passed to @callback + * @error: return location for an error + * + * Finishes a gtk_choose_file() or gtk_choose_file_full() call + * and returns whether the operation was successful. + * + * If this function returns `TRUE`, you can use + * [class@Gtk.FileChooser] API to get the results. + * + * Returns: `TRUE` if the operation was successful + */ +gboolean +gtk_choose_file_finish (GtkFileChooser *chooser, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +}