Files
gtk/demos/gtk-demo/listview_applauncher.c
Alexander Mikhaylenko 74002c680a tmp
2022-05-11 18:31:07 +04:00

212 lines
8.0 KiB
C

/* Lists/Application launcher
* #Keywords: GtkListItemFactory, GListModel
*
* This demo uses the GtkListView widget as a fancy application launcher.
*
* It is also a very small introduction to listviews.
*/
#include <gtk/gtk.h>
/* This is the function that creates the GListModel that we need.
* GTK list widgets need a GListModel to display, as models support change
* notifications.
* Unfortunately various older APIs do not provide list models, so we create
* our own.
*/
static GListModel *
create_application_list (void)
{
GListStore *store;
GList *apps, *l;
/* We use a GListStore here, which is a simple array-like list implementation
* for manual management.
* List models need to know what type of data they provide, so we need to
* provide the type here. As we want to do a list of applications, GAppInfo
* is the object we provide.
*/
store = g_list_store_new (G_TYPE_APP_INFO);
apps = g_app_info_get_all ();
for (l = apps; l; l = l->next)
g_list_store_append (store, l->data);
g_list_free_full (apps, g_object_unref);
return G_LIST_MODEL (store);
}
/* This is the function we use for setting up new listitems to display.
* We add just an GtkImage and a GtkLabel here to display the application's
* icon and name, as this is just a simple demo.
*/
static void
setup_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *swindow;
GtkWidget *box;
GtkWidget *image;
GtkWidget *label;
swindow = gtk_scrolled_window_new ();
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
GTK_POLICY_EXTERNAL, GTK_POLICY_NEVER);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (swindow), box);
gtk_widget_set_size_request (box, 1000, 200);
image = gtk_image_new ();
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
gtk_box_append (GTK_BOX (box), image);
label = gtk_label_new ("");
gtk_box_append (GTK_BOX (box), label);
gtk_list_item_set_child (list_item, swindow);
g_object_set_data (G_OBJECT (list_item), "image", image);
g_object_set_data (G_OBJECT (list_item), "label", label);
}
/* Here we need to prepare the listitem for displaying its item. We get the
* listitem already set up from the previous function, so we can reuse the
* GtkImage widget we set up above.
* We get the item - which we know is a GAppInfo because it comes out of
* the model we set up above, grab its icon and display it.
*/
static void
bind_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *image;
GtkWidget *label;
GAppInfo *app_info;
image = g_object_get_data (G_OBJECT (list_item), "image");
label = g_object_get_data (G_OBJECT (list_item), "label");
app_info = gtk_list_item_get_item (list_item);
gtk_image_set_from_gicon (GTK_IMAGE (image), g_app_info_get_icon (app_info));
gtk_label_set_label (GTK_LABEL (label), g_strdup_printf ("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magnam aliquam quaerat voluptatem. ut enim ad sapientiam perveniri potest, non paranda nobis solum ea, sed fruenda etiam sapientia est; sive hoc difficile est, tamen nec modus ullus nec finis inveniri potest. Quodsi. %s", g_app_info_get_display_name (app_info)));
}
/* In more complex code, we would also need functions to unbind and teardown
* the listitem, but this is simple code, so the default implementations are
* enough. If we had connected signals, this step would have been necessary.
*
* The GtkSignalListItemFactory documentation contains more information about
* this step.
*/
/* This function is called whenever an item in the list is activated. This is
* the simple way to allow reacting to the Enter key or double-clicking on a
* listitem.
* Of course, it is possible to use far more complex interactions by turning
* off activation and adding buttons or other widgets in the setup function
* above, but this is a simple demo, so we'll use the simple way.
*/
static void
activate_cb (GtkListView *list,
guint position,
gpointer unused)
{
GAppInfo *app_info;
GdkAppLaunchContext *context;
GError *error = NULL;
app_info = g_list_model_get_item (G_LIST_MODEL (gtk_list_view_get_model (list)), position);
/* Prepare the context for launching the application and launch it. This
* code is explained in detail in the documentation for GdkAppLaunchContext
* and GAppInfo.
*/
context = gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (list)));
if (!g_app_info_launch (app_info,
NULL,
G_APP_LAUNCH_CONTEXT (context),
&error))
{
GtkWidget *dialog;
/* And because error handling is important, even a simple demo has it:
* We display an error dialog that something went wrong.
*/
dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (list))),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"Could not launch %s", g_app_info_get_display_name (app_info));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
g_clear_error (&error);
gtk_widget_show (dialog);
}
g_object_unref (context);
g_object_unref (app_info);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_applauncher (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *list, *sw;
GListModel *model;
GtkListItemFactory *factory;
/* Create a window and set a few defaults */
window = gtk_window_new ();
gtk_window_set_default_size (GTK_WINDOW (window), 640, 320);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Application Launcher");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
/* The GtkListitemFactory is what is used to create GtkListItems
* to display the data from the model. So it is absolutely necessary
* to create one.
* We will use a GtkSignalListItemFactory because it is the simplest
* one to use. Different ones are available for different use cases.
* The most powerful one is GtkBuilderListItemFactory which uses
* GtkBuilder .ui files, so it requires little code.
*/
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
g_signal_connect (factory, "bind", G_CALLBACK (bind_listitem_cb), NULL);
/* And of course we need to set the data model. Here we call the function
* we wrote above that gives us the list of applications. Then we set
* it on the list widget.
* The list will now take items from the model and use the factory
* to create as many listitems as it needs to show itself to the user.
*/
model = create_application_list ();
/* Create the list widget here.
*/
list = gtk_list_view_new (GTK_SELECTION_MODEL (gtk_single_selection_new (model)), factory);
/* We connect the activate signal here. It's the function we defined
* above for launching the selected application.
*/
g_signal_connect (list, "activate", G_CALLBACK (activate_cb), NULL);
/* List widgets should always be contained in a GtkScrolledWindow,
* because otherwise they might get too large or they might not
* be scrollable.
*/
sw = gtk_scrolled_window_new ();
gtk_window_set_child (GTK_WINDOW (window), sw);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), list);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}