1/* Lists/Colors
2 * #Keywords: GtkSortListModel, GtkMultiSelection
3 *
4 * This demo displays a grid of colors.
5 *
6 * It is using a GtkGridView, and shows how to display
7 * and sort the data in various ways. The controls for
8 * this are implemented using GtkDropDown.
9 *
10 * The dataset used here has up to 16 777 216 items.
11 *
12 * Note that this demo also functions as a performance
13 * test for some of the list model machinery, and the
14 * biggest sizes here can lock up the application for
15 * extended times when used with sorting.
16 */
17
18#include <gtk/gtk.h>
19
20#include <stdlib.h>
21
22#define GTK_TYPE_COLOR (gtk_color_get_type ())
23G_DECLARE_FINAL_TYPE (GtkColor, gtk_color, GTK, COLOR, GObject)
24
25/* This is our object. It's just a color */
26typedef struct _GtkColor GtkColor;
27struct _GtkColor
28{
29 GObject parent_instance;
30
31 char *name;
32 GdkRGBA color;
33 int h, s, v;
34};
35
36enum {
37 PROP_0,
38 PROP_NAME,
39 PROP_COLOR,
40 PROP_RED,
41 PROP_GREEN,
42 PROP_BLUE,
43 PROP_HUE,
44 PROP_SATURATION,
45 PROP_VALUE,
46
47 N_COLOR_PROPS
48};
49
50static void
51gtk_color_snapshot (GdkPaintable *paintable,
52 GdkSnapshot *snapshot,
53 double width,
54 double height)
55{
56 GtkColor *self = GTK_COLOR (ptr: paintable);
57
58 gtk_snapshot_append_color (snapshot, color: &self->color, bounds: &GRAPHENE_RECT_INIT (0, 0, width, height));
59}
60
61static int
62gtk_color_get_intrinsic_width (GdkPaintable *paintable)
63{
64 return 32;
65}
66
67static int
68gtk_color_get_intrinsic_height (GdkPaintable *paintable)
69{
70 return 32;
71}
72
73static void
74gtk_color_paintable_init (GdkPaintableInterface *iface)
75{
76 iface->snapshot = gtk_color_snapshot;
77 iface->get_intrinsic_width = gtk_color_get_intrinsic_width;
78 iface->get_intrinsic_height = gtk_color_get_intrinsic_height;
79}
80
81/*
82 * Finally, we define the type. The important part is adding the paintable
83 * interface, so GTK knows that this object can indeed be drawn.
84 */
85G_DEFINE_TYPE_WITH_CODE (GtkColor, gtk_color, G_TYPE_OBJECT,
86 G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
87 gtk_color_paintable_init))
88
89static GParamSpec *color_properties[N_COLOR_PROPS] = { NULL, };
90
91static void
92rgb_to_hsv (GdkRGBA *rgba,
93 double *h_out,
94 double *s_out,
95 double *v_out)
96{
97 double red, green, blue;
98 double h, s, v;
99 double min, max;
100 double delta;
101
102 red = rgba->red;
103 green = rgba->green;
104 blue = rgba->blue;
105
106 h = 0.0;
107
108 if (red > green)
109 {
110 if (red > blue)
111 max = red;
112 else
113 max = blue;
114
115 if (green < blue)
116 min = green;
117 else
118 min = blue;
119 }
120 else
121 {
122 if (green > blue)
123 max = green;
124 else
125 max = blue;
126
127 if (red < blue)
128 min = red;
129 else
130 min = blue;
131 }
132
133 v = max;
134
135 if (max != 0.0)
136 s = (max - min) / max;
137 else
138 s = 0.0;
139
140 if (s == 0.0)
141 h = 0.0;
142 else
143 {
144 delta = max - min;
145
146 if (red == max)
147 h = (green - blue) / delta;
148 else if (green == max)
149 h = 2 + (blue - red) / delta;
150 else if (blue == max)
151 h = 4 + (red - green) / delta;
152
153 h /= 6.0;
154
155 if (h < 0.0)
156 h += 1.0;
157 else if (h > 1.0)
158 h -= 1.0;
159 }
160
161 *h_out = h;
162 *s_out = s;
163 *v_out = v;
164}
165
166static void
167gtk_color_get_property (GObject *object,
168 guint property_id,
169 GValue *value,
170 GParamSpec *pspec)
171{
172 GtkColor *self = GTK_COLOR (ptr: object);
173
174 switch (property_id)
175 {
176 case PROP_NAME:
177 g_value_set_string (value, v_string: self->name);
178 break;
179
180 case PROP_COLOR:
181 g_value_set_boxed (value, v_boxed: &self->color);
182 break;
183
184 case PROP_RED:
185 g_value_set_float (value, v_float: self->color.red);
186 break;
187
188 case PROP_GREEN:
189 g_value_set_float (value, v_float: self->color.green);
190 break;
191
192 case PROP_BLUE:
193 g_value_set_float (value, v_float: self->color.blue);
194 break;
195
196 case PROP_HUE:
197 g_value_set_int (value, v_int: self->h);
198 break;
199
200 case PROP_SATURATION:
201 g_value_set_int (value, v_int: self->s);
202 break;
203
204 case PROP_VALUE:
205 g_value_set_int (value, v_int: self->v);
206 break;
207
208 default:
209 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
210 break;
211 }
212}
213
214static void
215gtk_color_set_property (GObject *object,
216 guint property_id,
217 const GValue *value,
218 GParamSpec *pspec)
219{
220 GtkColor *self = GTK_COLOR (ptr: object);
221 double h, s, v;
222
223 switch (property_id)
224 {
225 case PROP_NAME:
226 self->name = g_value_dup_string (value);
227 break;
228
229 case PROP_COLOR:
230 self->color = *(GdkRGBA *) g_value_get_boxed (value);
231 rgb_to_hsv (rgba: &self->color, h_out: &h, s_out: &s, v_out: &v);
232 self->h = round (x: 360 * h);
233 self->s = round (x: 100 * s);
234 self->v = round (x: 100 * v);
235 break;
236
237 default:
238 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
239 break;
240 }
241}
242
243static void
244gtk_color_finalize (GObject *object)
245{
246 GtkColor *self = GTK_COLOR (ptr: object);
247
248 g_free (mem: self->name);
249
250 G_OBJECT_CLASS (gtk_color_parent_class)->finalize (object);
251}
252
253static void
254gtk_color_class_init (GtkColorClass *klass)
255{
256 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
257
258 gobject_class->get_property = gtk_color_get_property;
259 gobject_class->set_property = gtk_color_set_property;
260 gobject_class->finalize = gtk_color_finalize;
261
262 color_properties[PROP_NAME] =
263 g_param_spec_string (name: "name", NULL, NULL, NULL, flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
264 color_properties[PROP_COLOR] =
265 g_param_spec_boxed (name: "color", NULL, NULL, GDK_TYPE_RGBA, flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
266 color_properties[PROP_RED] =
267 g_param_spec_float (name: "red", NULL, NULL, minimum: 0, maximum: 1, default_value: 0, flags: G_PARAM_READABLE);
268 color_properties[PROP_GREEN] =
269 g_param_spec_float (name: "green", NULL, NULL, minimum: 0, maximum: 1, default_value: 0, flags: G_PARAM_READABLE);
270 color_properties[PROP_BLUE] =
271 g_param_spec_float (name: "blue", NULL, NULL, minimum: 0, maximum: 1, default_value: 0, flags: G_PARAM_READABLE);
272 color_properties[PROP_HUE] =
273 g_param_spec_int (name: "hue", NULL, NULL, minimum: 0, maximum: 360, default_value: 0, flags: G_PARAM_READABLE);
274 color_properties[PROP_SATURATION] =
275 g_param_spec_int (name: "saturation", NULL, NULL, minimum: 0, maximum: 100, default_value: 0, flags: G_PARAM_READABLE);
276 color_properties[PROP_VALUE] =
277 g_param_spec_int (name: "value", NULL, NULL, minimum: 0, maximum: 100, default_value: 0, flags: G_PARAM_READABLE);
278
279 g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_COLOR_PROPS, pspecs: color_properties);
280}
281
282static void
283gtk_color_init (GtkColor *self)
284{
285}
286
287static GtkColor *
288gtk_color_new (const char *name,
289 float r, float g, float b)
290{
291 GtkColor *result;
292 GdkRGBA color = { r, g, b, 1.0 };
293
294 result = g_object_new (GTK_TYPE_COLOR,
295 first_property_name: "name", name,
296 "color", &color,
297 NULL);
298
299 return result;
300}
301
302#define N_COLORS (256 * 256 * 256)
303
304#define GTK_TYPE_COLOR_LIST (gtk_color_list_get_type ())
305G_DECLARE_FINAL_TYPE (GtkColorList, gtk_color_list, GTK, COLOR_LIST, GObject)
306
307enum {
308 LIST_PROP_0,
309 LIST_PROP_SIZE,
310
311 N_LIST_PROPS
312};
313
314typedef struct _GtkColorList GtkColorList;
315struct _GtkColorList
316{
317 GObject parent_instance;
318
319 GtkColor **colors; /* Always N_COLORS */
320
321 guint size; /* How many colors we allow */
322};
323
324static GType
325gtk_color_list_get_item_type (GListModel *list)
326{
327 return GTK_TYPE_COLOR;
328}
329
330static guint
331gtk_color_list_get_n_items (GListModel *list)
332{
333 GtkColorList *self = GTK_COLOR_LIST (ptr: list);
334
335 return self->size;
336}
337
338static guint
339position_to_color (guint position)
340{
341 static guint map[] = {
342 0xFF0000, 0x00FF00, 0x0000FF,
343 0x7F0000, 0x007F00, 0x00007F,
344 0x3F0000, 0x003F00, 0x00003F,
345 0x1F0000, 0x001F00, 0x00001F,
346 0x0F0000, 0x000F00, 0x00000F,
347 0x070000, 0x000700, 0x000007,
348 0x030000, 0x000300, 0x000003,
349 0x010000, 0x000100, 0x000001
350 };
351 guint result, i;
352
353 result = 0;
354
355 for (i = 0; i < G_N_ELEMENTS (map); i++)
356 {
357 if (position & (1 << i))
358 result ^= map[i];
359 }
360
361 return result;
362}
363
364static gpointer
365gtk_color_list_get_item (GListModel *list,
366 guint position)
367{
368 GtkColorList *self = GTK_COLOR_LIST (ptr: list);
369
370 if (position >= self->size)
371 return NULL;
372
373 position = position_to_color (position);
374
375 if (self->colors[position] == NULL)
376 {
377 guint red, green, blue;
378
379 red = (position >> 16) & 0xFF;
380 green = (position >> 8) & 0xFF;
381 blue = position & 0xFF;
382
383 self->colors[position] = gtk_color_new (name: "", r: red / 255., g: green / 255., b: blue / 255.);
384 }
385
386 return g_object_ref (self->colors[position]);
387}
388
389static void
390gtk_color_list_model_init (GListModelInterface *iface)
391{
392 iface->get_item_type = gtk_color_list_get_item_type;
393 iface->get_n_items = gtk_color_list_get_n_items;
394 iface->get_item = gtk_color_list_get_item;
395}
396
397G_DEFINE_TYPE_WITH_CODE (GtkColorList, gtk_color_list, G_TYPE_OBJECT,
398 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
399 gtk_color_list_model_init))
400
401static GParamSpec *list_properties[N_LIST_PROPS] = { NULL, };
402
403static void
404gtk_color_list_get_property (GObject *object,
405 guint property_id,
406 GValue *value,
407 GParamSpec *pspec)
408{
409 GtkColorList *self = GTK_COLOR_LIST (ptr: object);
410
411 switch (property_id)
412 {
413 case LIST_PROP_SIZE:
414 g_value_set_uint (value, v_uint: self->size);
415 break;
416
417 default:
418 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
419 break;
420 }
421}
422
423static void
424gtk_color_list_set_size (GtkColorList *self,
425 guint size)
426{
427 guint old_size = self->size;
428
429 self->size = size;
430 if (self->size > old_size)
431 g_list_model_items_changed (list: G_LIST_MODEL (ptr: self), position: old_size, removed: 0, added: self->size - old_size);
432 else if (old_size > self->size)
433 g_list_model_items_changed (list: G_LIST_MODEL (ptr: self), position: self->size, removed: old_size - self->size, added: 0);
434
435 g_object_notify_by_pspec (G_OBJECT (self), pspec: list_properties[LIST_PROP_SIZE]);
436}
437
438static void
439gtk_color_list_set_property (GObject *object,
440 guint property_id,
441 const GValue *value,
442 GParamSpec *pspec)
443{
444 GtkColorList *self = GTK_COLOR_LIST (ptr: object);
445
446 switch (property_id)
447 {
448 case LIST_PROP_SIZE:
449 gtk_color_list_set_size (self, size: g_value_get_uint (value));
450 break;
451
452 default:
453 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
454 break;
455 }
456}
457
458static void
459gtk_color_list_dispose (GObject *object)
460{
461 GtkColorList *self = GTK_COLOR_LIST (ptr: object);
462 guint i;
463
464 for (i = 0; i < N_COLORS; i++)
465 {
466 g_clear_object (&self->colors[i]);
467 }
468 g_free (mem: self->colors);
469
470 G_OBJECT_CLASS (gtk_color_parent_class)->finalize (object);
471}
472
473static void
474gtk_color_list_class_init (GtkColorListClass *klass)
475{
476 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
477
478 gobject_class->get_property = gtk_color_list_get_property;
479 gobject_class->set_property = gtk_color_list_set_property;
480 gobject_class->dispose = gtk_color_list_dispose;
481
482 list_properties[LIST_PROP_SIZE] =
483 g_param_spec_uint (name: "size", NULL, NULL, minimum: 0, N_COLORS, default_value: 0, flags: G_PARAM_READWRITE);
484
485 g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_LIST_PROPS, pspecs: list_properties);
486}
487
488static void
489gtk_color_list_init (GtkColorList *self)
490{
491 GBytes *data;
492 char **lines;
493 guint i;
494
495 self->colors = g_new0 (GtkColor *, N_COLORS);
496
497 data = g_resources_lookup_data (path: "/listview_colors/color.names.txt", lookup_flags: 0, NULL);
498 lines = g_strsplit (string: g_bytes_get_data (bytes: data, NULL), delimiter: "\n", max_tokens: 0);
499
500 for (i = 0; lines[i]; i++)
501 {
502 const char *name;
503 char **fields;
504 int red, green, blue;
505 guint pos;
506
507 if (lines[i][0] == '#' || lines[i][0] == '\0')
508 continue;
509
510 fields = g_strsplit (string: lines[i], delimiter: " ", max_tokens: 0);
511 name = fields[1];
512 red = atoi (nptr: fields[3]);
513 green = atoi (nptr: fields[4]);
514 blue = atoi (nptr: fields[5]);
515
516 pos = ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | blue;
517 if (self->colors[pos] == NULL)
518 self->colors[pos] = gtk_color_new (name, r: red / 255., g: green / 255., b: blue / 255.);
519
520 g_strfreev (str_array: fields);
521 }
522 g_strfreev (str_array: lines);
523
524 g_bytes_unref (bytes: data);
525}
526
527GListModel *
528gtk_color_list_new (guint size)
529{
530 return g_object_new (GTK_TYPE_COLOR_LIST,
531 first_property_name: "size", size,
532 NULL);
533}
534
535static char *
536get_rgb_markup (gpointer this,
537 GtkColor *color)
538{
539 if (!color)
540 return NULL;
541
542 return g_strdup_printf (format: "<b>R:</b> %d <b>G:</b> %d <b>B:</b> %d",
543 (int)(color->color.red * 255),
544 (int)(color->color.green * 255),
545 (int)(color->color.blue * 255));
546}
547
548static char *
549get_hsv_markup (gpointer this,
550 GtkColor *color)
551{
552 if (!color)
553 return NULL;
554
555 return g_strdup_printf (format: "<b>H:</b> %d <b>S:</b> %d <b>V:</b> %d",
556 color->h,
557 color->s,
558 color->v);
559}
560
561static void
562setup_simple_listitem_cb (GtkListItemFactory *factory,
563 GtkListItem *list_item)
564{
565 GtkWidget *picture;
566 GtkExpression *color_expression, *expression;
567
568 expression = gtk_constant_expression_new (GTK_TYPE_LIST_ITEM, list_item);
569 color_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, property_name: "item");
570
571 picture = gtk_picture_new ();
572 gtk_widget_set_size_request (widget: picture, width: 32, height: 32);
573 gtk_expression_bind (self: color_expression, target: picture, property: "paintable", NULL);
574
575 gtk_list_item_set_child (self: list_item, child: picture);
576}
577
578static void
579setup_listitem_cb (GtkListItemFactory *factory,
580 GtkListItem *list_item)
581{
582 GtkWidget *box, *picture, *name_label, *rgb_label, *hsv_label;
583 GtkExpression *color_expression, *expression;
584 GtkExpression *params[1];
585
586 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
587 gtk_list_item_set_child (self: list_item, child: box);
588
589 expression = gtk_constant_expression_new (GTK_TYPE_LIST_ITEM, list_item);
590 color_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, property_name: "item");
591
592 expression = gtk_property_expression_new (GTK_TYPE_COLOR,
593 expression: gtk_expression_ref (self: color_expression),
594 property_name: "name");
595 name_label = gtk_label_new (NULL);
596 gtk_expression_bind (self: expression, target: name_label, property: "label", NULL);
597 gtk_box_append (GTK_BOX (box), child: name_label);
598
599 expression = gtk_expression_ref (self: color_expression);
600 picture = gtk_picture_new ();
601 gtk_expression_bind (self: expression, target: picture, property: "paintable", NULL);
602 gtk_box_append (GTK_BOX (box), child: picture);
603
604 params[0] = gtk_expression_ref (self: color_expression);
605 expression = gtk_cclosure_expression_new (G_TYPE_STRING,
606 NULL,
607 n_params: 1, params,
608 callback_func: (GCallback)get_rgb_markup,
609 NULL, NULL);
610
611 rgb_label = gtk_label_new (NULL);
612 gtk_label_set_use_markup (GTK_LABEL (rgb_label), TRUE);
613 gtk_expression_bind (self: expression, target: rgb_label, property: "label", NULL);
614 gtk_box_append (GTK_BOX (box), child: rgb_label);
615
616 params[0] = gtk_expression_ref (self: color_expression);
617 expression = gtk_cclosure_expression_new (G_TYPE_STRING,
618 NULL,
619 n_params: 1, params,
620 callback_func: (GCallback)get_hsv_markup,
621 NULL, NULL);
622
623 hsv_label = gtk_label_new (NULL);
624 gtk_label_set_use_markup (GTK_LABEL (hsv_label), TRUE);
625 gtk_expression_bind (self: expression, target: hsv_label, property: "label", NULL);
626 gtk_box_append (GTK_BOX (box), child: hsv_label);
627
628 gtk_expression_unref (self: color_expression);
629}
630
631static void
632setup_selection_listitem_cb (GtkListItemFactory *factory,
633 GtkListItem *list_item)
634{
635 GtkWidget *picture;
636 GtkExpression *color_expression, *expression;
637
638 expression = gtk_constant_expression_new (GTK_TYPE_LIST_ITEM, list_item);
639 color_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, property_name: "item");
640
641 picture = gtk_picture_new ();
642 gtk_widget_set_size_request (widget: picture, width: 8, height: 8);
643 gtk_expression_bind (self: color_expression, target: picture, property: "paintable", NULL);
644
645 gtk_list_item_set_child (self: list_item, child: picture);
646}
647
648static void
649set_title (gpointer item,
650 const char *title)
651{
652 g_object_set_data (G_OBJECT (item), key: "title", data: (gpointer)title);
653}
654
655static char *
656get_title (gpointer item)
657{
658 return g_strdup (str: (char *)g_object_get_data (G_OBJECT (item), key: "title"));
659}
660
661GtkWidget *
662create_color_grid (void)
663{
664 GtkWidget *gridview;
665 GtkListItemFactory *factory;
666
667 gridview = gtk_grid_view_new (NULL, NULL);
668 gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), policy: GTK_SCROLL_NATURAL);
669 gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), policy: GTK_SCROLL_NATURAL);
670
671 factory = gtk_signal_list_item_factory_new ();
672 g_signal_connect (factory, "setup", G_CALLBACK (setup_simple_listitem_cb), NULL);
673 gtk_grid_view_set_factory (GTK_GRID_VIEW (gridview), factory);
674 g_object_unref (object: factory);
675
676 gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), max_columns: 24);
677 gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (gridview), TRUE);
678
679 return gridview;
680}
681
682static gboolean
683add_colors (GtkWidget *widget,
684 GdkFrameClock *clock,
685 gpointer data)
686{
687 GtkColorList *colors = data;
688 guint limit;
689
690 limit = GPOINTER_TO_UINT (g_object_get_data (data, "limit"));
691 gtk_color_list_set_size (self: colors, MIN (limit, colors->size + MAX (1, limit / 4096)));
692
693 if (colors->size >= limit)
694 return G_SOURCE_REMOVE;
695 else
696 return G_SOURCE_CONTINUE;
697}
698
699static void
700refill (GtkWidget *button,
701 GtkColorList *colors)
702{
703 gtk_color_list_set_size (self: colors, size: 0);
704 gtk_widget_add_tick_callback (widget: button, callback: add_colors, g_object_ref (colors), notify: g_object_unref);
705}
706
707static void
708limit_changed_cb (GtkDropDown *dropdown,
709 GParamSpec *pspec,
710 GtkColorList *colors)
711{
712 guint new_limit, old_limit;
713
714 old_limit = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (colors), "limit"));
715 new_limit = 1 << (3 * (gtk_drop_down_get_selected (self: dropdown) + 1));
716
717 g_object_set_data (G_OBJECT (colors), key: "limit", GUINT_TO_POINTER (new_limit));
718
719 if (old_limit == colors->size)
720 gtk_color_list_set_size (self: colors, size: new_limit);
721}
722
723static void
724limit_changed_cb2 (GtkDropDown *dropdown,
725 GParamSpec *pspec,
726 GtkLabel *label)
727{
728 char *string;
729 int len;
730 guint limit;
731
732 limit = 1 << (3 * (gtk_drop_down_get_selected (self: dropdown) + 1));
733
734 string = g_strdup_printf (format: "%'u", limit);
735 len = g_utf8_strlen (p: string, max: -1);
736 g_free (mem: string);
737
738 gtk_label_set_width_chars (self: label, n_chars: len + 2); /* for " /" */
739}
740
741static void
742items_changed_cb (GListModel *model,
743 guint position,
744 guint removed,
745 guint added,
746 GtkWidget *label)
747{
748 guint n = g_list_model_get_n_items (list: model);
749 char *text;
750
751 text = g_strdup_printf (format: "%'u /", n);
752 gtk_label_set_label (GTK_LABEL (label), str: text);
753 g_free (mem: text);
754}
755
756static void
757setup_number_item (GtkSignalListItemFactory *factory,
758 GtkListItem *item)
759{
760 GtkWidget *label;
761 PangoAttrList *attrs;
762
763 label = gtk_label_new (str: "");
764 gtk_label_set_xalign (GTK_LABEL (label), xalign: 1);
765
766 attrs = pango_attr_list_new ();
767 pango_attr_list_insert (list: attrs, attr: pango_attr_font_features_new (features: "tnum"));
768 gtk_label_set_attributes (GTK_LABEL (label), attrs);
769 pango_attr_list_unref (list: attrs);
770
771 gtk_list_item_set_child (self: item, child: label);
772}
773
774static void
775bind_number_item (GtkSignalListItemFactory *factory,
776 GtkListItem *item)
777{
778 GtkWidget *label;
779 guint limit;
780 char *string;
781
782 label = gtk_list_item_get_child (self: item);
783
784 limit = 1 << (3 * (gtk_list_item_get_position (self: item) + 1));
785 string = g_strdup_printf (format: "%'u", limit);
786 gtk_label_set_label (GTK_LABEL (label), str: string);
787 g_free (mem: string);
788}
789
790static void
791update_selection_count (GListModel *model,
792 guint position,
793 guint removed,
794 guint added,
795 gpointer data)
796{
797 char *text;
798 text = g_strdup_printf (format: "%u", g_list_model_get_n_items (list: model));
799 gtk_label_set_label (GTK_LABEL (data), str: text);
800 g_free (mem: text);
801}
802
803static void
804update_selection_average (GListModel *model,
805 guint position,
806 guint removed,
807 guint added,
808 gpointer data)
809{
810 guint n = g_list_model_get_n_items (list: model);
811 GdkRGBA c = { 0, 0, 0, 1 };
812 guint i;
813 GtkColor *color;
814
815 for (i = 0; i < n; i++)
816 {
817 color = g_list_model_get_item (list: model, position: i);
818
819 c.red += color->color.red;
820 c.green += color->color.green;
821 c.blue += color->color.blue;
822
823 g_object_unref (object: color);
824 }
825
826 color = gtk_color_new (name: "", r: c.red / n, g: c.green / n, b: c.blue / n);
827 gtk_picture_set_paintable (self: GTK_PICTURE (ptr: data), paintable: GDK_PAINTABLE (ptr: color));
828 g_object_unref (object: color);
829}
830
831static void
832update_progress_cb (GtkSortListModel *model,
833 GParamSpec *pspec,
834 GtkProgressBar *progress)
835{
836 guint total;
837 guint pending;
838
839 total = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: model));
840 total = MAX (total, 1); /* avoid div by 0 below */
841 pending = gtk_sort_list_model_get_pending (self: model);
842
843 gtk_widget_set_visible (GTK_WIDGET (progress), visible: pending != 0);
844 gtk_progress_bar_set_fraction (pbar: progress, fraction: (total - pending) / (double) total);
845}
846
847static GtkWidget *window = NULL;
848
849GtkWidget *
850do_listview_colors (GtkWidget *do_widget)
851{
852 if (window == NULL)
853 {
854 GtkMultiSelection *selection;
855 GtkSortListModel *sort_model;
856 GtkWidget *header, *overlay, *gridview, *sw, *box, *dropdown;
857 GtkListItemFactory *factory;
858 GListStore *factories;
859 GtkSorter *sorter;
860 GtkSorter *multi_sorter;
861 GListStore *sorters;
862 GtkExpression *expression;
863 GtkWidget *button;
864 GtkWidget *label;
865 PangoAttrList *attrs;
866 char *string;
867 guint len;
868 GtkWidget *selection_view;
869 GListModel *selection_filter;
870 GtkSelectionModel *no_selection;
871 GtkWidget *grid;
872 GtkWidget *selection_size_label;
873 GtkWidget *selection_average_picture;
874 GtkWidget *selection_info_toggle;
875 GtkWidget *selection_info_revealer;
876 GtkWidget *progress;
877 GtkCssProvider *provider;
878
879 provider = gtk_css_provider_new ();
880 gtk_css_provider_load_from_resource (css_provider: provider, resource_path: "/listview_colors/listview_colors.css");
881 gtk_style_context_add_provider_for_display (display: gdk_display_get_default (),
882 GTK_STYLE_PROVIDER (provider),
883 priority: 800);
884 g_object_unref (object: provider);
885
886 sort_model = gtk_sort_list_model_new (model: gtk_color_list_new (size: 0), NULL);
887 gtk_sort_list_model_set_incremental (self: sort_model, TRUE);
888 selection = gtk_multi_selection_new (model: G_LIST_MODEL (ptr: sort_model));
889
890 window = gtk_window_new ();
891 gtk_window_set_title (GTK_WINDOW (window), title: "Colors");
892 header = gtk_header_bar_new ();
893 gtk_window_set_titlebar (GTK_WINDOW (window), titlebar: header);
894
895 gtk_window_set_default_size (GTK_WINDOW (window), width: 600, height: 400);
896 gtk_window_set_display (GTK_WINDOW (window),
897 display: gtk_widget_get_display (widget: do_widget));
898 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer*)&window);
899
900 overlay = gtk_overlay_new ();
901 gtk_window_set_child (GTK_WINDOW (window), child: overlay);
902
903 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
904 gtk_overlay_set_child (GTK_OVERLAY (overlay), child: box);
905
906 progress = gtk_progress_bar_new ();
907 gtk_widget_set_hexpand (widget: progress, TRUE);
908 gtk_widget_set_valign (widget: progress, align: GTK_ALIGN_START);
909 g_signal_connect (sort_model, "notify::pending", G_CALLBACK (update_progress_cb), progress);
910 gtk_overlay_add_overlay (GTK_OVERLAY (overlay), widget: progress);
911
912 selection_info_revealer = gtk_revealer_new ();
913 gtk_box_append (GTK_BOX (box), child: selection_info_revealer);
914
915 grid = gtk_grid_new ();
916 gtk_revealer_set_child (GTK_REVEALER (selection_info_revealer), child: grid);
917 gtk_widget_set_margin_start (widget: grid, margin: 10);
918 gtk_widget_set_margin_end (widget: grid, margin: 10);
919 gtk_widget_set_margin_top (widget: grid, margin: 10);
920 gtk_widget_set_margin_bottom (widget: grid, margin: 10);
921 gtk_grid_set_row_spacing (GTK_GRID (grid), spacing: 10);
922 gtk_grid_set_column_spacing (GTK_GRID (grid), spacing: 10);
923
924 label = gtk_label_new (str: "Selection");
925 gtk_widget_set_hexpand (widget: label, TRUE);
926 gtk_widget_add_css_class (widget: label, css_class: "title-3");
927 gtk_grid_attach (GTK_GRID (grid), child: label, column: 0, row: 0, width: 5, height: 1);
928
929 gtk_grid_attach (GTK_GRID (grid), child: gtk_label_new (str: "Size:"), column: 0, row: 2, width: 1, height: 1);
930
931 selection_size_label = gtk_label_new (str: "0");
932 gtk_grid_attach (GTK_GRID (grid), child: selection_size_label, column: 1, row: 2, width: 1, height: 1);
933
934 gtk_grid_attach (GTK_GRID (grid), child: gtk_label_new (str: "Average:"), column: 2, row: 2, width: 1, height: 1);
935
936 selection_average_picture = gtk_picture_new ();
937 gtk_widget_set_size_request (widget: selection_average_picture, width: 32, height: 32);
938 gtk_grid_attach (GTK_GRID (grid), child: selection_average_picture, column: 3, row: 2, width: 1, height: 1);
939
940 label = gtk_label_new (str: "");
941 gtk_widget_set_hexpand (widget: label, TRUE);
942 gtk_grid_attach (GTK_GRID (grid), child: label, column: 4, row: 2, width: 1, height: 1);
943
944 sw = gtk_scrolled_window_new ();
945 gtk_widget_set_hexpand (widget: sw, TRUE);
946
947 gtk_grid_attach (GTK_GRID (grid), child: sw, column: 0, row: 1, width: 5, height: 1);
948 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
949 hscrollbar_policy: GTK_POLICY_NEVER,
950 vscrollbar_policy: GTK_POLICY_AUTOMATIC);
951
952 factory = gtk_signal_list_item_factory_new ();
953 g_signal_connect (factory, "setup", G_CALLBACK (setup_selection_listitem_cb), NULL);
954 selection_view = gtk_grid_view_new (NULL, factory);
955 gtk_widget_add_css_class (widget: selection_view, css_class: "compact");
956 gtk_grid_view_set_max_columns (GTK_GRID_VIEW (selection_view), max_columns: 200);
957 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: selection_view);
958
959 sw = gtk_scrolled_window_new ();
960 gtk_box_append (GTK_BOX (box), child: sw);
961
962 gridview = create_color_grid ();
963 gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), model: GTK_SELECTION_MODEL (ptr: selection));
964 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: gridview);
965 gtk_widget_set_hexpand (widget: sw, TRUE);
966 gtk_widget_set_vexpand (widget: sw, TRUE);
967
968 selection_filter = G_LIST_MODEL (ptr: gtk_selection_filter_model_new (model: GTK_SELECTION_MODEL (ptr: selection)));
969 g_signal_connect (selection_filter, "items-changed", G_CALLBACK (update_selection_count), selection_size_label);
970 g_signal_connect (selection_filter, "items-changed", G_CALLBACK (update_selection_average), selection_average_picture);
971
972 no_selection = GTK_SELECTION_MODEL (ptr: gtk_no_selection_new (model: selection_filter));
973 gtk_grid_view_set_model (GTK_GRID_VIEW (selection_view), model: no_selection);
974 g_object_unref (object: no_selection);
975
976 selection_info_toggle = gtk_toggle_button_new ();
977 gtk_button_set_icon_name (GTK_BUTTON (selection_info_toggle), icon_name: "emblem-important-symbolic");
978 gtk_widget_set_tooltip_text (widget: selection_info_toggle, text: "Show selection info");
979 gtk_header_bar_pack_start (GTK_HEADER_BAR (header), child: selection_info_toggle);
980
981 g_object_bind_property (source: selection_info_toggle, source_property: "active",
982 target: selection_info_revealer, target_property: "reveal-child",
983 flags: G_BINDING_DEFAULT);
984
985 button = gtk_button_new_with_mnemonic (label: "_Refill");
986 g_signal_connect (button, "clicked",
987 G_CALLBACK (refill),
988 gtk_sort_list_model_get_model (sort_model));
989
990 gtk_header_bar_pack_start (GTK_HEADER_BAR (header), child: button);
991
992 label = gtk_label_new (str: "0 /");
993 attrs = pango_attr_list_new ();
994 pango_attr_list_insert (list: attrs, attr: pango_attr_font_features_new (features: "tnum"));
995 gtk_label_set_attributes (GTK_LABEL (label), attrs);
996 pango_attr_list_unref (list: attrs);
997 string = g_strdup_printf (format: "%'u", 4096);
998 len = g_utf8_strlen (p: string, max: -1);
999 g_free (mem: string);
1000 gtk_label_set_width_chars (GTK_LABEL (label), n_chars: len + 2);
1001 gtk_label_set_xalign (GTK_LABEL (label), xalign: 1);
1002
1003 g_signal_connect (selection, "items-changed", G_CALLBACK (items_changed_cb), label);
1004 gtk_header_bar_pack_start (GTK_HEADER_BAR (header), child: label);
1005
1006 dropdown = gtk_drop_down_new_from_strings (strings: (const char * const[]) { "8", "64", "512", "4096", "32768", "262144", "2097152", "16777216", NULL });
1007 g_signal_connect (dropdown, "notify::selected",
1008 G_CALLBACK (limit_changed_cb),
1009 gtk_sort_list_model_get_model (sort_model));
1010 g_signal_connect (dropdown, "notify::selected",
1011 G_CALLBACK (limit_changed_cb2),
1012 label);
1013 factory = gtk_signal_list_item_factory_new ();
1014 g_signal_connect (factory, "setup", G_CALLBACK (setup_number_item), NULL);
1015 g_signal_connect (factory, "bind", G_CALLBACK (bind_number_item), NULL);
1016 gtk_drop_down_set_factory (self: GTK_DROP_DOWN (ptr: dropdown), factory);
1017 g_object_unref (object: factory);
1018 gtk_drop_down_set_selected (self: GTK_DROP_DOWN (ptr: dropdown), position: 3); /* 4096 */
1019 gtk_header_bar_pack_start (GTK_HEADER_BAR (header), child: dropdown);
1020
1021 sorters = g_list_store_new (GTK_TYPE_SORTER);
1022
1023 /* An empty multisorter doesn't do any sorting and the sortmodel is
1024 * smart enough to know that.
1025 */
1026 sorter = GTK_SORTER (ptr: gtk_multi_sorter_new ());
1027 set_title (item: sorter, title: "Unsorted");
1028 g_list_store_append (store: sorters, item: sorter);
1029 g_object_unref (object: sorter);
1030
1031 sorter = GTK_SORTER (ptr: gtk_string_sorter_new (expression: gtk_property_expression_new (GTK_TYPE_COLOR, NULL, property_name: "name")));
1032 set_title (item: sorter, title: "Name");
1033 g_list_store_append (store: sorters, item: sorter);
1034 g_object_unref (object: sorter);
1035
1036 multi_sorter = GTK_SORTER (ptr: gtk_multi_sorter_new ());
1037
1038 sorter = GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_property_expression_new (GTK_TYPE_COLOR, NULL, property_name: "red")));
1039 gtk_numeric_sorter_set_sort_order (self: GTK_NUMERIC_SORTER (ptr: sorter), sort_order: GTK_SORT_DESCENDING);
1040 set_title (item: sorter, title: "Red");
1041 g_list_store_append (store: sorters, item: sorter);
1042 gtk_multi_sorter_append (self: GTK_MULTI_SORTER (ptr: multi_sorter), sorter);
1043
1044 sorter = GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_property_expression_new (GTK_TYPE_COLOR, NULL, property_name: "green")));
1045 gtk_numeric_sorter_set_sort_order (self: GTK_NUMERIC_SORTER (ptr: sorter), sort_order: GTK_SORT_DESCENDING);
1046 set_title (item: sorter, title: "Green");
1047 g_list_store_append (store: sorters, item: sorter);
1048 gtk_multi_sorter_append (self: GTK_MULTI_SORTER (ptr: multi_sorter), sorter);
1049
1050 sorter = GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_property_expression_new (GTK_TYPE_COLOR, NULL, property_name: "blue")));
1051 gtk_numeric_sorter_set_sort_order (self: GTK_NUMERIC_SORTER (ptr: sorter), sort_order: GTK_SORT_DESCENDING);
1052 set_title (item: sorter, title: "Blue");
1053 g_list_store_append (store: sorters, item: sorter);
1054 gtk_multi_sorter_append (self: GTK_MULTI_SORTER (ptr: multi_sorter), sorter);
1055
1056 set_title (item: multi_sorter, title: "RGB");
1057 g_list_store_append (store: sorters, item: multi_sorter);
1058 g_object_unref (object: multi_sorter);
1059
1060 multi_sorter = GTK_SORTER (ptr: gtk_multi_sorter_new ());
1061
1062 sorter = GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_property_expression_new (GTK_TYPE_COLOR, NULL, property_name: "hue")));
1063 gtk_numeric_sorter_set_sort_order (self: GTK_NUMERIC_SORTER (ptr: sorter), sort_order: GTK_SORT_DESCENDING);
1064 set_title (item: sorter, title: "Hue");
1065 g_list_store_append (store: sorters, item: sorter);
1066 gtk_multi_sorter_append (self: GTK_MULTI_SORTER (ptr: multi_sorter), sorter);
1067
1068 sorter = GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_property_expression_new (GTK_TYPE_COLOR, NULL, property_name: "saturation")));
1069 gtk_numeric_sorter_set_sort_order (self: GTK_NUMERIC_SORTER (ptr: sorter), sort_order: GTK_SORT_DESCENDING);
1070 set_title (item: sorter, title: "Saturation");
1071 g_list_store_append (store: sorters, item: sorter);
1072 gtk_multi_sorter_append (self: GTK_MULTI_SORTER (ptr: multi_sorter), sorter);
1073
1074 sorter = GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_property_expression_new (GTK_TYPE_COLOR, NULL, property_name: "value")));
1075 gtk_numeric_sorter_set_sort_order (self: GTK_NUMERIC_SORTER (ptr: sorter), sort_order: GTK_SORT_DESCENDING);
1076 set_title (item: sorter, title: "Value");
1077 g_list_store_append (store: sorters, item: sorter);
1078 gtk_multi_sorter_append (self: GTK_MULTI_SORTER (ptr: multi_sorter), sorter);
1079
1080 set_title (item: multi_sorter, title: "HSV");
1081 g_list_store_append (store: sorters, item: multi_sorter);
1082 g_object_unref (object: multi_sorter);
1083
1084 expression = gtk_cclosure_expression_new (G_TYPE_STRING,
1085 NULL,
1086 n_params: 0, NULL,
1087 callback_func: (GCallback)get_title,
1088 NULL, NULL);
1089
1090 dropdown = gtk_drop_down_new (model: G_LIST_MODEL (ptr: sorters), expression);
1091 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10);
1092 gtk_box_append (GTK_BOX (box), child: gtk_label_new (str: "Sort by:"));
1093 gtk_box_append (GTK_BOX (box), child: dropdown);
1094 gtk_header_bar_pack_end (GTK_HEADER_BAR (header), child: box);
1095
1096 g_object_bind_property (source: dropdown, source_property: "selected-item", target: sort_model, target_property: "sorter", flags: G_BINDING_SYNC_CREATE);
1097
1098 factories = g_list_store_new (GTK_TYPE_LIST_ITEM_FACTORY);
1099
1100 factory = gtk_signal_list_item_factory_new ();
1101 g_signal_connect (factory, "setup", G_CALLBACK (setup_simple_listitem_cb), NULL);
1102 set_title (item: factory, title: "Colors");
1103 g_list_store_append (store: factories, item: factory);
1104
1105 factory = gtk_signal_list_item_factory_new ();
1106 g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
1107 set_title (item: factory, title: "Everything");
1108 g_list_store_append (store: factories, item: factory);
1109
1110 expression = gtk_cclosure_expression_new (G_TYPE_STRING,
1111 NULL,
1112 n_params: 0, NULL,
1113 callback_func: (GCallback)get_title,
1114 NULL, NULL);
1115 dropdown = gtk_drop_down_new (model: G_LIST_MODEL (ptr: factories), expression);
1116 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10);
1117 gtk_box_append (GTK_BOX (box), child: gtk_label_new (str: "Show:"));
1118 gtk_box_append (GTK_BOX (box), child: dropdown);
1119 gtk_header_bar_pack_end (GTK_HEADER_BAR (header), child: box);
1120
1121 g_object_bind_property (source: dropdown, source_property: "selected-item", target: gridview, target_property: "factory", flags: G_BINDING_SYNC_CREATE);
1122
1123 g_object_unref (object: selection);
1124 }
1125
1126 if (!gtk_widget_get_visible (widget: window))
1127 gtk_widget_show (widget: window);
1128 else
1129 gtk_window_destroy (GTK_WINDOW (window));
1130
1131 return window;
1132}
1133

source code of gtk/demos/gtk-demo/listview_colors.c