[gtk/prop-list] wip: Add GtkDropDown



commit 1213f61e02ad6ba46c4c4ca83b70d3fb3cb8aada
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Dec 8 20:22:06 2019 -0500

    wip: Add GtkDropDown
    
    This is a simple drop down control using list models.

 gtk/gtk.h             |   1 +
 gtk/gtkdropdown.c     | 492 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkdropdown.h     |  57 ++++++
 gtk/meson.build       |   2 +
 gtk/ui/gtkdropdown.ui |  64 +++++++
 tests/meson.build     |   1 +
 tests/testdropdown.c  |  89 +++++++++
 7 files changed, 706 insertions(+)
---
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 096fdbca70..78f971db05 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -101,6 +101,7 @@
 #include <gtk/gtkdragdest.h>
 #include <gtk/gtkdragsource.h>
 #include <gtk/gtkdrawingarea.h>
+#include <gtk/gtkdropdown.h>
 #include <gtk/gtkeditable.h>
 #include <gtk/gtkentry.h>
 #include <gtk/gtkentrybuffer.h>
diff --git a/gtk/gtkdropdown.c b/gtk/gtkdropdown.c
new file mode 100644
index 0000000000..3d8529c3a2
--- /dev/null
+++ b/gtk/gtkdropdown.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright © 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Matthias Clasen <mclasen redhat com>
+ */
+
+#include "config.h"
+
+#include "gtkdropdown.h"
+
+#include "gtkintl.h"
+#include "gtklistview.h"
+#include "gtklistitemfactory.h"
+#include "gtklistitemwidgetprivate.h"
+#include "gtkpopover.h"
+#include "gtkprivate.h"
+#include "gtksingleselection.h"
+#include "gtknoselection.h"
+#include "gtkselectionmodel.h"
+#include "gtkstylecontext.h"
+#include "gtkiconprivate.h"
+#include "gtkwidgetprivate.h"
+#include "gtknative.h"
+#include "gtktogglebutton.h"
+#include "gtkstack.h"
+
+
+struct _GtkDropDown
+{
+  GtkWidget parent_instance;
+
+  GtkListItemFactory *factory;
+  GListModel *model;
+  GListModel *selection;
+  GListModel *popup_selection;
+
+  GtkWidget *popup;
+  GtkWidget *button;
+
+  GtkWidget *popup_list;
+  GtkWidget *button_stack;
+  GtkWidget *button_item;
+  GtkWidget *button_placeholder;
+
+  guint selected;
+};
+
+struct _GtkDropDownClass
+{
+  GtkDropDownClass parent_class;
+};
+
+enum
+{
+  PROP_0,
+  PROP_FACTORY,
+  PROP_MODEL,
+  PROP_SELECTED,
+
+  N_PROPS
+};
+
+G_DEFINE_TYPE (GtkDropDown, gtk_drop_down, GTK_TYPE_WIDGET)
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+button_toggled (GtkWidget *widget,
+                gpointer   data)
+{
+  GtkDropDown *self = data;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+    gtk_popover_popup (GTK_POPOVER (self->popup));
+  else
+    gtk_popover_popdown (GTK_POPOVER (self->popup));
+}
+
+static void
+popover_closed (GtkPopover *popover,
+                gpointer    data)
+{
+  GtkDropDown *self = data;
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), FALSE);
+}
+
+static void
+row_activated (GtkListView *listview,
+               guint        position,
+               gpointer     data)
+{
+  GtkDropDown *self = data;
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), FALSE);
+  gtk_popover_popdown (GTK_POPOVER (self->popup));
+
+  gtk_drop_down_set_selected (self, gtk_single_selection_get_selected (GTK_SINGLE_SELECTION 
(self->popup_selection)));
+}
+ 
+static void
+selection_changed (GtkSelectionModel *selection,
+                   guint              position,
+                   guint              n_items,
+                   gpointer           data)
+{
+  GtkDropDown *self = data;
+  guint selected;
+  gpointer item;
+
+  selected = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (self->selection));
+  item = gtk_single_selection_get_selected_item (GTK_SINGLE_SELECTION (self->selection));
+
+  if (selected == GTK_INVALID_LIST_POSITION)
+    {
+      gtk_stack_set_visible_child_name (GTK_STACK (self->button_stack), "empty");
+    }
+  else
+    {
+      gtk_stack_set_visible_child_name (GTK_STACK (self->button_stack), "item");
+      gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (self->button_item), selected, item, FALSE);
+    }
+
+  gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (self->popup_selection), selected);
+}
+
+static void
+gtk_drop_down_dispose (GObject *object)
+{
+  GtkDropDown *self = GTK_DROP_DOWN (object);
+
+  g_clear_pointer (&self->popup, gtk_widget_unparent);
+  g_clear_pointer (&self->button, gtk_widget_unparent);
+
+  g_clear_object (&self->model);
+  if (self->selection)
+    g_signal_handlers_disconnect_by_func (self->selection, selection_changed, self);
+  g_clear_object (&self->selection);
+  g_clear_object (&self->popup_selection);
+
+  G_OBJECT_CLASS (gtk_drop_down_parent_class)->dispose (object);
+}
+
+static void
+gtk_drop_down_get_property (GObject    *object,
+                            guint       property_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  GtkDropDown *self = GTK_DROP_DOWN (object);
+
+  switch (property_id)
+    {
+    case PROP_FACTORY:
+      g_value_set_object (value, self->factory);
+      break;
+
+    case PROP_MODEL:
+      g_value_set_object (value, self->model);
+      break;
+
+    case PROP_SELECTED:
+      g_value_set_uint (value, gtk_drop_down_get_selected (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_drop_down_set_property (GObject      *object,
+                            guint         property_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  GtkDropDown *self = GTK_DROP_DOWN (object);
+
+  switch (property_id)
+    {
+    case PROP_FACTORY:
+      gtk_drop_down_set_factory (self, g_value_get_object (value));
+      break;
+
+    case PROP_MODEL:
+      gtk_drop_down_set_model (self, g_value_get_object (value));
+      break;
+
+    case PROP_SELECTED:
+      gtk_drop_down_set_selected (self, g_value_get_uint (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_drop_down_measure (GtkWidget      *widget,
+                       GtkOrientation  orientation,
+                       int             size,
+                       int            *minimum,
+                       int            *natural,
+                       int            *minimum_baseline,
+                       int            *natural_baseline)
+{
+  GtkDropDown *self = GTK_DROP_DOWN (widget);
+
+  gtk_widget_measure (self->button,
+                      orientation,
+                      size,
+                      minimum, natural,
+                      minimum_baseline, natural_baseline);
+}
+
+static void
+gtk_drop_down_size_allocate (GtkWidget *widget,
+                             int        width,
+                             int        height,
+                             int        baseline)
+{
+  GtkDropDown *self = GTK_DROP_DOWN (widget);
+
+  gtk_widget_size_allocate (self->button, &(GtkAllocation) { 0, 0, width, height }, baseline);
+
+  gtk_native_check_resize (GTK_NATIVE (self->popup));
+}
+
+
+static void
+gtk_drop_down_class_init (GtkDropDownClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose = gtk_drop_down_dispose;
+  gobject_class->get_property = gtk_drop_down_get_property;
+  gobject_class->set_property = gtk_drop_down_set_property;
+
+  widget_class->measure = gtk_drop_down_measure;
+  widget_class->size_allocate = gtk_drop_down_size_allocate;
+
+  /**
+   * GtkDropDown:factory:
+   *
+   * Factory for populating list items
+   */
+  properties[PROP_FACTORY] =
+    g_param_spec_object ("factory",
+                         P_("Factory"),
+                         P_("Factory for populating list items"),
+                         GTK_TYPE_LIST_ITEM_FACTORY,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GtkDropDown:model:
+   *
+   * Model for the items displayed
+   */
+  properties[PROP_MODEL] =
+    g_param_spec_object ("model",
+                         P_("Model"),
+                         P_("Model for the items displayed"),
+                         G_TYPE_LIST_MODEL,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_SELECTED] =
+    g_param_spec_uint ("selected",
+                         P_("Selected"),
+                         P_("Position of the selected item"),
+                         0, G_MAXUINT, GTK_INVALID_LIST_POSITION,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkdropdown.ui");
+  gtk_widget_class_bind_template_child (widget_class, GtkDropDown, button);
+  gtk_widget_class_bind_template_child (widget_class, GtkDropDown, button_stack);
+  gtk_widget_class_bind_template_child (widget_class, GtkDropDown, button_item);
+  gtk_widget_class_bind_template_child (widget_class, GtkDropDown, popup);
+  gtk_widget_class_bind_template_child (widget_class, GtkDropDown, popup_list);
+  gtk_widget_class_bind_template_callback (widget_class, row_activated);
+
+  gtk_widget_class_bind_template_callback (widget_class, button_toggled);
+  gtk_widget_class_bind_template_callback (widget_class, popover_closed);
+
+  gtk_widget_class_set_css_name (widget_class, I_("combobox"));
+}
+
+static void
+gtk_drop_down_init (GtkDropDown *self)
+{
+  g_type_ensure (GTK_TYPE_ICON);
+  g_type_ensure (GTK_TYPE_LIST_ITEM_WIDGET);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  self->selected = GTK_INVALID_LIST_POSITION;
+}
+
+/**
+ * gtk_drop_down_new:
+ *
+ * Creates a new empty #GtkDropDown.
+ *
+ * You most likely want to call gtk_drop_down_set_factory() to
+ * set up a way to map its items to widgets and gtk_drop_down_set_model()
+ * to set a model to provide items next.
+ *
+ * Returns: a new #GtkDropDown
+ **/
+GtkWidget *
+gtk_drop_down_new (void)
+{
+  return g_object_new (GTK_TYPE_DROP_DOWN, NULL);
+}
+
+/**
+ * gtk_drop_down_new_with_factory:
+ * @factory: (transfer full): The factory to populate items with
+ *
+ * Creates a new #GtkDropDown that uses the given @factory for
+ * mapping items to widgets.
+ *
+ * You most likely want to call gtk_drop_down_set_model() to set
+ * a model next.
+ *
+ * Returns: a new #GtkDropDown using the given @factory
+ **/
+GtkWidget *
+gtk_drop_down_new_with_factory (GtkListItemFactory *factory)
+{
+  GtkWidget *result;
+
+  g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
+
+  result = g_object_new (GTK_TYPE_DROP_DOWN,
+                         "factory", factory,
+                         NULL);
+
+  g_object_unref (factory);
+
+  return result;
+}
+
+/**
+ * gtk_drop_down_get_model:
+ * @self: a #GtkDropDown
+ *
+ * Gets the model that's currently used to read the items displayed.
+ *
+ * Returns: (nullable) (transfer none): The model in use
+ **/
+GListModel *
+gtk_drop_down_get_model (GtkDropDown *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_DOWN (self), NULL);
+
+  return self->model;
+}
+
+/**
+ * gtk_drop_down_set_model:
+ * @self: a #GtkDropDown
+ * @model: (allow-none) (transfer none): the model to use or %NULL for none
+ *
+ * Sets the #GListModel to use.
+ */
+void
+gtk_drop_down_set_model (GtkDropDown *self,
+                         GListModel  *model)
+{
+  g_return_if_fail (GTK_IS_DROP_DOWN (self));
+  g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+
+  if (!g_set_object (&self->model, model))
+    return;
+
+  if (model == NULL)
+    {
+      gtk_list_view_set_model (GTK_LIST_VIEW (self->popup_list), NULL);
+
+      if (self->selection)
+        g_signal_handlers_disconnect_by_func (self->selection, selection_changed, self);
+
+      g_clear_object (&self->selection);
+      g_clear_object (&self->popup_selection);
+    }
+  else
+    {
+      GListModel *selection;
+
+      selection = G_LIST_MODEL (gtk_single_selection_new (model));
+      g_set_object (&self->popup_selection, selection);
+      gtk_list_view_set_model (GTK_LIST_VIEW (self->popup_list), selection);
+      g_object_unref (selection);
+
+      if (GTK_IS_SINGLE_SELECTION (model))
+        selection = g_object_ref (model);
+      else
+        selection = G_LIST_MODEL (gtk_single_selection_new (model));
+      g_set_object (&self->selection, selection);
+      g_object_unref (selection);
+
+      g_signal_connect (self->selection, "selection-changed", G_CALLBACK (selection_changed), self);
+      gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (self->selection), self->selected);
+    }
+  
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
+}
+
+/**
+ * gtk_drop_down_get_factory:
+ * @self: a #GtkDropDown
+ *
+ * Gets the factory that's currently used to populate list items.
+ *
+ * Returns: (nullable) (transfer none): The factory in use
+ **/
+GtkListItemFactory *
+gtk_drop_down_get_factory (GtkDropDown *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_DOWN (self), NULL);
+
+  return self->factory;
+}
+
+/**
+ * gtk_drop_down_set_factory:
+ * @self: a #GtkDropDown
+ * @factory: (allow-none) (transfer none): the factory to use or %NULL for none
+ *
+ * Sets the #GtkListItemFactory to use for populating list items.
+ **/
+void
+gtk_drop_down_set_factory (GtkDropDown        *self,
+                           GtkListItemFactory *factory)
+{
+  g_return_if_fail (GTK_IS_DROP_DOWN (self));
+  g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
+
+  if (!g_set_object (&self->factory, factory))
+    return;
+
+  gtk_list_view_set_factory (GTK_LIST_VIEW (self->popup_list), factory);
+  gtk_list_item_widget_set_factory (GTK_LIST_ITEM_WIDGET (self->button_item), factory);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
+}
+
+void
+gtk_drop_down_set_selected (GtkDropDown *self,
+                            guint        position)
+{
+  g_return_if_fail (GTK_IS_DROP_DOWN (self));
+
+  if (self->selection == NULL)
+    return;
+
+  if (gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (self->selection)) == position)
+    return;
+
+  gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (self->selection), position);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
+}
+
+guint
+gtk_drop_down_get_selected (GtkDropDown *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_DOWN (self), GTK_INVALID_LIST_POSITION);
+
+  if (self->selection == NULL)
+    return GTK_INVALID_LIST_POSITION;
+
+  return gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (self->selection));
+}
diff --git a/gtk/gtkdropdown.h b/gtk/gtkdropdown.h
new file mode 100644
index 0000000000..107b5b1b45
--- /dev/null
+++ b/gtk/gtkdropdown.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Matthias Clasen
+ */
+
+#ifndef __GTK_DROP_DOWN_H__
+#define __GTK_DROP_DOWN_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_DROP_DOWN         (gtk_drop_down_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkDropDown, gtk_drop_down, GTK, DROP_DOWN, GtkWidget)
+
+GDK_AVAILABLE_IN_ALL
+GtkWidget *     gtk_drop_down_new                               (void);
+GDK_AVAILABLE_IN_ALL
+GtkWidget *     gtk_drop_down_new_with_factory                  (GtkListItemFactory     *factory);
+
+GDK_AVAILABLE_IN_ALL
+GListModel *    gtk_drop_down_get_model                         (GtkDropDown            *self);
+GDK_AVAILABLE_IN_ALL
+void            gtk_drop_down_set_model                         (GtkDropDown            *self,
+                                                                 GListModel             *model);
+GDK_AVAILABLE_IN_ALL
+void            gtk_drop_down_set_factory                       (GtkDropDown            *self,
+                                                                 GtkListItemFactory     *factory);
+GDK_AVAILABLE_IN_ALL
+GtkListItemFactory *
+                gtk_drop_down_get_factory                       (GtkDropDown            *self);
+
+GDK_AVAILABLE_IN_ALL
+void            gtk_drop_down_set_selected                      (GtkDropDown            *self,
+                                                                 guint                   position);
+GDK_AVAILABLE_IN_ALL
+guint           gtk_drop_down_get_selected                      (GtkDropDown            *self);
+
+G_END_DECLS
+
+#endif  /* __GTK_DROP_DOWN_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index d0160c857d..d546efbc50 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -229,6 +229,7 @@ gtk_public_sources = files([
   'gtkdragdest.c',
   'gtkdragsource.c',
   'gtkdrawingarea.c',
+  'gtkdropdown.c',
   'gtkeditable.c',
   'gtkemojichooser.c',
   'gtkemojicompletion.c',
@@ -520,6 +521,7 @@ gtk_public_headers = files([
   'gtkdragdest.h',
   'gtkdragsource.h',
   'gtkdrawingarea.h',
+  'gtkdropdown.h',
   'gtkeditable.h',
   'gtkentry.h',
   'gtkentrybuffer.h',
diff --git a/gtk/ui/gtkdropdown.ui b/gtk/ui/gtkdropdown.ui
new file mode 100644
index 0000000000..d3754203fb
--- /dev/null
+++ b/gtk/ui/gtkdropdown.ui
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk40">
+  <template class="GtkDropDown" parent="GtkWidget">
+    <child>
+      <object class="GtkToggleButton" id="button">
+        <signal name="toggled" handler="button_toggled"/>
+        <child>
+          <object class="GtkBox">
+            <child>
+              <object class="GtkStack" id="button_stack">
+                <property name="hhomogeneous">0</property>
+                <child>
+                  <object class="GtkStackPage">
+                    <property name="name">empty</property>
+                    <property name="child">
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">(None)</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkStackPage">
+                    <property name="name">item</property>
+                    <property name="child">
+                      <object class="GtkListItemWidget" id="button_item"/>
+                    </property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkIcon">
+                <property name="css-name">arrow</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkPopover" id="popup">
+    <signal name="closed" handler="popover_closed"/>
+    <property name="relative-to">GtkDropDown</property>
+    <property name="has-arrow">0</property>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="hscrollbar-policy">never</property>
+            <property name="vscrollbar-policy">never</property>
+            <child>
+              <object class="GtkListView" id="popup_list">
+                <signal name="activate" handler="row_activated"/>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/tests/meson.build b/tests/meson.build
index 2be5d96dae..479ae8db9b 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,5 +1,6 @@
 gtk_tests = [
   # testname, optional extra sources
+  ['testdropdown'],
   ['rendernode'],
   ['rendernode-create-tests'],
   ['overlayscroll'],
diff --git a/tests/testdropdown.c b/tests/testdropdown.c
new file mode 100644
index 0000000000..98ca331b57
--- /dev/null
+++ b/tests/testdropdown.c
@@ -0,0 +1,89 @@
+/* simple.c
+ * Copyright (C) 2017  Red Hat, Inc
+ * Author: Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <gtk/gtk.h>
+
+static void
+setup_item (GtkSignalListItemFactory *self,
+            GtkListItem              *list_item,
+            gpointer                  data)
+{
+  GtkWidget *label;
+
+  label = gtk_label_new (NULL);
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_list_item_set_child (list_item, label);
+}
+
+static void
+bind_item (GtkSignalListItemFactory *factory,
+           GtkListItem              *list_item)
+{
+  gpointer item;
+  GtkWidget *label;
+
+  item = gtk_list_item_get_item (list_item);
+  label = gtk_list_item_get_child (list_item);
+
+  gtk_label_set_label (GTK_LABEL (label), pango_font_family_get_name (PANGO_FONT_FAMILY (item)));
+}
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window, *button, *box, *spin;
+  GtkListItemFactory *factory;
+  GListModel *model;
+
+  gtk_init ();
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title (GTK_WINDOW (window), "hello world");
+  gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+  g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+
+  button = gtk_drop_down_new ();
+  g_object_set (button, "margin", 10, NULL);
+
+  factory = gtk_signal_list_item_factory_new ();
+  g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
+  g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
+  gtk_drop_down_set_factory (GTK_DROP_DOWN (button), factory);
+  g_object_unref (factory);
+
+  model = G_LIST_MODEL (pango_cairo_font_map_get_default ());
+  gtk_drop_down_set_model (GTK_DROP_DOWN (button), model);
+  gtk_drop_down_set_selected (GTK_DROP_DOWN (button), 0);
+
+  gtk_container_add (GTK_CONTAINER (window), box);
+  gtk_container_add (GTK_CONTAINER (box), button);
+
+  spin = gtk_spin_button_new_with_range (-1, g_list_model_get_n_items (G_LIST_MODEL (model)), 1);
+  g_object_set (spin, "margin", 10, NULL);
+  g_object_bind_property  (button, "selected", spin, "value", G_BINDING_SYNC_CREATE | 
G_BINDING_BIDIRECTIONAL);
+  gtk_container_add (GTK_CONTAINER (box), spin);
+
+  g_object_unref (model);
+
+  gtk_widget_show (window);
+
+  gtk_main ();
+
+  return 0;
+}


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