Dialogs [src]
Dialogs in GTK are asynchronous APIs to obtain certain objects, such as files or fonts, or to initiate certain actions such as printing or to provide information to the user. The commonality behind these is that they are all high-level tasks that usually require user interaction.
API
All dialogs follow the async/finish pattern with a GAsyncResult
object. This pattern is extensively used in GIO and in other places.
The dialog object itself functions as the source object of the async operation, and keeps state that is needed during the operation. Each async operation is provided as a pair of functions, one to begin the async operation, and one to obtain the results. The second function is by convention named finish(), and it must be called from a callback that the caller provides to the first function.
Other pieces that are by convention passed to the begin function include an optional parent window (to attach dialog windows to) and a cancellable object that can be used to programmatically cancel an ongoing async operation.
The callback that is passed to the begin function must have the signature
of a GAsyncReadyCallback
:
void callback (GObject *source,
GAsyncResult *result,
gpointer user_data)
Example
Here is an example of the dialog API flow, for opening a file:
dialog = gtk_file_dialog_new ();
/* Set up dialog here ... */
gtk_file_dialog_open (dialog, window, NULL, file_selected, data);
g_object_unref (dialog);
/* Return to the mainloop to give the async op a chance to run */
The callback will look like this:
static void
file_selected (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GtkFileDialog *dialog = GTK_FILE_DIALOG (source);
GError *error = NULL;
GFile *file;
file = gtk_file_dialog_open_finish (dialog, result, &error);
if (!file)
{
/* Check if the user chose not to select a file */
if (!g_error_matches (error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_DISMISSED))
{
GtkAlertDialog *alert;
alert = gtk_alert_dialog_new ("Something went wrong");
gtk_alert_dialog_set_detail (alert, error->message);
gtk_alert_dialog_show (alert, window);
g_object_unref (alert);
}
g_error_free (error);
return;
}
/* Do something with the file here ... */
g_object_unref (file);
}
Errors
There are many possible causes for async operations to fail. The ones
that are specifically related to this being a dialog API are captured
by the GtkDialogError
enumeration.
- A dialog can be cancelled programmatically (using the cancellable).
In this case, the
GTK_DIALOG_ERROR_CANCELLED
error is raised - A dialog can be dismissed by the user without selecting the requested
object. In this case, the
GTK_DIALOG_ERROR_DISMISSED
error is raised
It is appropriate to handle the first with an explanation for why the cancellation happened (e.g. “This took too long (timeout reached)”), and it isn’t necessary to show an error dialog for the latter (since the user explicitly chose to dismiss the dialog).
Details
There are some fine points in the async/finish pattern that are worth drawing attention to:
- The dialog object is kept alive for the duration of the asynchronous operation, so it is safe to drop your reference after initiating the operation (unless you want to keep using the dialog object for multiple operations).
- The finish functions are not nullable—they only return
NULL
if an error occurred (which is why the ‘dismissed by the user’ case is handled as an error). This is relevant for language bindings.
Existing dialogs
Here is a list of existing dialogs
Object/Task | GTK Dialog | Main API |
---|---|---|
Files | GtkFileDialog |
gtk_file_dialog_open() |
Text Files | GtkFileDialog |
gtk_file_dialog_open_text_file() |
Folders | GtkFileDialog |
gtk_file_dialog_select_folder() |
Fonts | GtkFontDialog |
gtk_font_dialog_choose_font() |
Colors | GtkColorDialog |
gtk_color_dialog_choose_rgba() |
Printing | GtkPrintDialog |
gtk_print_dialog_print() |
Alerts | GtkAlertDialog |
gtk_alert_dialog_choose() |
URIs | GtkUriLauncher |
gtk_uri_launcher_launch() |
Applications | GtkFileLauncher |
gtk_file_launcher_launch() |
Note that many of the dialogs have other entry points, for example the file dialog can open multiple files, or save to a file, and the font dialog can choose font face or font family objects.
Language Bindings
A big motivation for strictly following the async/finish pattern for dialogs is that bindings for languages with support for promises can make this pattern work seamlessly with their languages native async support.
Here is how the example above might look in JavaScript:
async someFunction() {
const dialog = new Gtk.FileDialog();
// Set up dialog here...
try {
const file = await dialog.open(window, null);
// Do something with the file here ...
} catch (e) {
logError(e, "Error opening file dialog:");
}
}
History
In past, dialogs in GTK were widgets that were derived from GtkDialog
(and ultimatively, from GtkWindow
), such as GtkFileChooserDialog
or
GtkColorChooserDialog
. This turned out to be limiting and inconvenient
when wrapping platform APIs that are often out-of-process.
Most of these APIs have been deprecated by now, and will be removed in the next major version of GTK.