1 | /* Lists/Application launcher |
2 | * #Keywords: GtkListItemFactory, GListModel |
3 | * |
4 | * This demo uses the GtkListView widget as a fancy application launcher. |
5 | * |
6 | * It is also a very small introduction to listviews. |
7 | */ |
8 | |
9 | #include <gtk/gtk.h> |
10 | |
11 | /* This is the function that creates the GListModel that we need. |
12 | * GTK list widgets need a GListModel to display, as models support change |
13 | * notifications. |
14 | * Unfortunately various older APIs do not provide list models, so we create |
15 | * our own. |
16 | */ |
17 | static GListModel * |
18 | create_application_list (void) |
19 | { |
20 | GListStore *store; |
21 | GList *apps, *l; |
22 | |
23 | /* We use a GListStore here, which is a simple array-like list implementation |
24 | * for manual management. |
25 | * List models need to know what type of data they provide, so we need to |
26 | * provide the type here. As we want to do a list of applications, GAppInfo |
27 | * is the object we provide. |
28 | */ |
29 | store = g_list_store_new (G_TYPE_APP_INFO); |
30 | |
31 | apps = g_app_info_get_all (); |
32 | |
33 | for (l = apps; l; l = l->next) |
34 | g_list_store_append (store, item: l->data); |
35 | |
36 | g_list_free_full (list: apps, free_func: g_object_unref); |
37 | |
38 | return G_LIST_MODEL (ptr: store); |
39 | } |
40 | |
41 | /* This is the function we use for setting up new listitems to display. |
42 | * We add just an GtkImage and a GtkLabel here to display the application's |
43 | * icon and name, as this is just a simple demo. |
44 | */ |
45 | static void |
46 | setup_listitem_cb (GtkListItemFactory *factory, |
47 | GtkListItem *list_item) |
48 | { |
49 | GtkWidget *box; |
50 | GtkWidget *image; |
51 | GtkWidget *label; |
52 | |
53 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 12); |
54 | image = gtk_image_new (); |
55 | gtk_image_set_icon_size (GTK_IMAGE (image), icon_size: GTK_ICON_SIZE_LARGE); |
56 | gtk_box_append (GTK_BOX (box), child: image); |
57 | label = gtk_label_new (str: "" ); |
58 | gtk_box_append (GTK_BOX (box), child: label); |
59 | gtk_list_item_set_child (self: list_item, child: box); |
60 | } |
61 | |
62 | /* Here we need to prepare the listitem for displaying its item. We get the |
63 | * listitem already set up from the previous function, so we can reuse the |
64 | * GtkImage widget we set up above. |
65 | * We get the item - which we know is a GAppInfo because it comes out of |
66 | * the model we set up above, grab its icon and display it. |
67 | */ |
68 | static void |
69 | bind_listitem_cb (GtkListItemFactory *factory, |
70 | GtkListItem *list_item) |
71 | { |
72 | GtkWidget *image; |
73 | GtkWidget *label; |
74 | GAppInfo *app_info; |
75 | |
76 | image = gtk_widget_get_first_child (widget: gtk_list_item_get_child (self: list_item)); |
77 | label = gtk_widget_get_next_sibling (widget: image); |
78 | app_info = gtk_list_item_get_item (self: list_item); |
79 | |
80 | gtk_image_set_from_gicon (GTK_IMAGE (image), icon: g_app_info_get_icon (appinfo: app_info)); |
81 | gtk_label_set_label (GTK_LABEL (label), str: g_app_info_get_display_name (appinfo: app_info)); |
82 | } |
83 | |
84 | /* In more complex code, we would also need functions to unbind and teardown |
85 | * the listitem, but this is simple code, so the default implementations are |
86 | * enough. If we had connected signals, this step would have been necessary. |
87 | * |
88 | * The GtkSignalListItemFactory documentation contains more information about |
89 | * this step. |
90 | */ |
91 | |
92 | /* This function is called whenever an item in the list is activated. This is |
93 | * the simple way to allow reacting to the Enter key or double-clicking on a |
94 | * listitem. |
95 | * Of course, it is possible to use far more complex interactions by turning |
96 | * off activation and adding buttons or other widgets in the setup function |
97 | * above, but this is a simple demo, so we'll use the simple way. |
98 | */ |
99 | static void |
100 | activate_cb (GtkListView *list, |
101 | guint position, |
102 | gpointer unused) |
103 | { |
104 | GAppInfo *app_info; |
105 | GdkAppLaunchContext *context; |
106 | GError *error = NULL; |
107 | |
108 | app_info = g_list_model_get_item (list: G_LIST_MODEL (ptr: gtk_list_view_get_model (self: list)), position); |
109 | |
110 | /* Prepare the context for launching the application and launch it. This |
111 | * code is explained in detail in the documentation for GdkAppLaunchContext |
112 | * and GAppInfo. |
113 | */ |
114 | context = gdk_display_get_app_launch_context (display: gtk_widget_get_display (GTK_WIDGET (list))); |
115 | if (!g_app_info_launch (appinfo: app_info, |
116 | NULL, |
117 | G_APP_LAUNCH_CONTEXT (context), |
118 | error: &error)) |
119 | { |
120 | GtkWidget *dialog; |
121 | |
122 | /* And because error handling is important, even a simple demo has it: |
123 | * We display an error dialog that something went wrong. |
124 | */ |
125 | dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (list))), |
126 | flags: GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, |
127 | type: GTK_MESSAGE_ERROR, |
128 | buttons: GTK_BUTTONS_CLOSE, |
129 | message_format: "Could not launch %s" , g_app_info_get_display_name (appinfo: app_info)); |
130 | gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), message_format: "%s" , error->message); |
131 | g_clear_error (err: &error); |
132 | gtk_widget_show (widget: dialog); |
133 | } |
134 | |
135 | g_object_unref (object: context); |
136 | g_object_unref (object: app_info); |
137 | } |
138 | |
139 | static GtkWidget *window = NULL; |
140 | |
141 | GtkWidget * |
142 | do_listview_applauncher (GtkWidget *do_widget) |
143 | { |
144 | if (window == NULL) |
145 | { |
146 | GtkWidget *list, *sw; |
147 | GListModel *model; |
148 | GtkListItemFactory *factory; |
149 | |
150 | /* Create a window and set a few defaults */ |
151 | window = gtk_window_new (); |
152 | gtk_window_set_default_size (GTK_WINDOW (window), width: 640, height: 320); |
153 | gtk_window_set_display (GTK_WINDOW (window), |
154 | display: gtk_widget_get_display (widget: do_widget)); |
155 | gtk_window_set_title (GTK_WINDOW (window), title: "Application Launcher" ); |
156 | g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *) &window); |
157 | |
158 | /* The GtkListitemFactory is what is used to create GtkListItems |
159 | * to display the data from the model. So it is absolutely necessary |
160 | * to create one. |
161 | * We will use a GtkSignalListItemFactory because it is the simplest |
162 | * one to use. Different ones are available for different use cases. |
163 | * The most powerful one is GtkBuilderListItemFactory which uses |
164 | * GtkBuilder .ui files, so it requires little code. |
165 | */ |
166 | factory = gtk_signal_list_item_factory_new (); |
167 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_listitem_cb), NULL); |
168 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_listitem_cb), NULL); |
169 | |
170 | /* And of course we need to set the data model. Here we call the function |
171 | * we wrote above that gives us the list of applications. Then we set |
172 | * it on the list widget. |
173 | * The list will now take items from the model and use the factory |
174 | * to create as many listitems as it needs to show itself to the user. |
175 | */ |
176 | model = create_application_list (); |
177 | |
178 | /* Create the list widget here. |
179 | */ |
180 | list = gtk_list_view_new (model: GTK_SELECTION_MODEL (ptr: gtk_single_selection_new (model)), factory); |
181 | |
182 | /* We connect the activate signal here. It's the function we defined |
183 | * above for launching the selected application. |
184 | */ |
185 | g_signal_connect (list, "activate" , G_CALLBACK (activate_cb), NULL); |
186 | |
187 | /* List widgets should always be contained in a GtkScrolledWindow, |
188 | * because otherwise they might get too large or they might not |
189 | * be scrollable. |
190 | */ |
191 | sw = gtk_scrolled_window_new (); |
192 | gtk_window_set_child (GTK_WINDOW (window), child: sw); |
193 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: list); |
194 | } |
195 | |
196 | if (!gtk_widget_get_visible (widget: window)) |
197 | gtk_widget_show (widget: window); |
198 | else |
199 | gtk_window_destroy (GTK_WINDOW (window)); |
200 | |
201 | return window; |
202 | } |
203 | |