[gtk/matthiasc/for-master: 4/4] gtk-demo: Bring back the applauncher demo



commit 87d5e533c5d995b1acdf1e7a7673abd23c65fa4b
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Jun 4 15:44:39 2020 -0400

    gtk-demo: Bring back the applauncher demo
    
    Just without the coverflow. It was a well-documented
    demo, so it is useful to keep around.

 demos/gtk-demo/demo.gresource.xml     |   1 +
 demos/gtk-demo/listview_applauncher.c | 203 ++++++++++++++++++++++++++++++++++
 demos/gtk-demo/meson.build            |   1 +
 3 files changed, 205 insertions(+)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index fc0c1465a4..400ecbf208 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -215,6 +215,7 @@
     <file>infobar.c</file>
     <file>links.c</file>
     <file>listbox.c</file>
+    <file>listview_applauncher.c</file>
     <file>listview_colors.c</file>
     <file>listview_clocks.c</file>
     <file>listview_filebrowser.c</file>
diff --git a/demos/gtk-demo/listview_applauncher.c b/demos/gtk-demo/listview_applauncher.c
new file mode 100644
index 0000000000..3391b467f9
--- /dev/null
+++ b/demos/gtk-demo/listview_applauncher.c
@@ -0,0 +1,203 @@
+/* Lists/Application launcher
+ *
+ * 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 #GtkKabel 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 *box;
+  GtkWidget *image;
+  GtkWidget *label;
+
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+  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, box);
+}
+
+/* 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 = gtk_widget_get_first_child (gtk_list_item_get_child (list_item));
+  label = gtk_widget_get_next_sibling (image);
+  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_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 (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;
+      GListModel *selection;
+      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);
+
+      /* Create the list widget here.
+       */
+      list = gtk_list_view_new_with_factory (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);
+
+      /* 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 ();
+      gtk_list_view_set_model (GTK_LIST_VIEW (list), model);
+      g_object_unref (model);
+
+      /* 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 (NULL, NULL);
+      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;
+}
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index 1a9ee4aee0..eb6513591b 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -42,6 +42,7 @@ demos = files([
   'listbox.c',
   'flowbox.c',
   'list_store.c',
+  'listview_applauncher.c',
   'listview_clocks.c',
   'listview_colors.c',
   'listview_filebrowser.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]