[gtk/wip/layout-manager: 6/14] Add GtkLayoutChild



commit 5cbf6f5fbdf8146a62fa611b2279f587e34ee1f0
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Wed Dec 19 16:01:17 2018 +0000

    Add GtkLayoutChild
    
    Layout managers needs a way to store properties that control the layout
    policy of a widget; typically, we used to store these in GtkContainer's
    child properties, but since GtkLayoutManager is decoupled from the
    actual container widget, we need a separate storage. Additionally, child
    properties have their own downsides, like requiring a separate, global
    GParamSpecPool storage, and additional lookup API.
    
    GtkLayoutChild is a simple GObject class, which means you can introspect
    and document it as you would any other type.

 docs/reference/gtk/gtk4-sections.txt |  14 +++
 gtk/gtk.h                            |   1 +
 gtk/gtklayoutchild.c                 | 189 +++++++++++++++++++++++++++++++++++
 gtk/gtklayoutchild.h                 |  27 +++++
 gtk/gtklayoutmanager.c               |  77 ++++++++++++++
 gtk/gtklayoutmanager.h               |  38 ++++---
 gtk/meson.build                      |   2 +
 7 files changed, 333 insertions(+), 15 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 1179eeaa2f..4d8f1eeacb 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -7164,9 +7164,23 @@ gtk_layout_manager_measure
 gtk_layout_manager_allocate
 gtk_layout_manager_get_request_mode
 gtk_layout_manager_get_widget
+gtk_layout_manager_get_layout_child
 gtk_layout_manager_layout_changed
 
 <SUBSECTION Standard>
 GTK_TYPE_LAYOUT_MANAGER
 gtk_layout_manager_get_type
 </SECTION>
+
+<SECTION>
+<FILE>gtklayoutchild</FILE>
+GtkLayoutChild
+GtkLayoutChildClass
+
+gtk_layout_child_get_layout_manager
+gtk_layout_child_get_child_widget
+
+<SUBSECTION Standard>
+GTK_TYPE_LAYOUT_CHILD
+gtk_layout_child_get_type
+</SECTION>
diff --git a/gtk/gtk.h b/gtk/gtk.h
index d6e419a55b..9bc2e57423 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -135,6 +135,7 @@
 #include <gtk/gtklabel.h>
 #include <gtk/gtklayout.h>
 #include <gtk/gtklayoutmanager.h>
+#include <gtk/gtklayoutchild.h>
 #include <gtk/gtklevelbar.h>
 #include <gtk/gtklinkbutton.h>
 #include <gtk/gtklistbox.h>
diff --git a/gtk/gtklayoutchild.c b/gtk/gtklayoutchild.c
new file mode 100644
index 0000000000..028e80273a
--- /dev/null
+++ b/gtk/gtklayoutchild.c
@@ -0,0 +1,189 @@
+#include "config.h"
+
+#include "gtklayoutchild.h"
+
+#include "gtklayoutmanager.h"
+#include "gtkprivate.h"
+
+/**
+ * SECTION:gtklayoutchild
+ * @Title: GtkLayoutChild
+ * @Short_description: An object containing layout properties
+ *
+ * #GtkLayoutChild is the base class for objects that are meant to hold
+ * layout properties. If a #GtkLayoutManager has per-child properties,
+ * like their packing type, or the horizontal and vertical span, or the
+ * icon name, then the layout manager should use a #GtkLayoutChild
+ * implementation to store those properties.
+ *
+ * A #GtkLayoutChild instance is only ever valid while a widget is part
+ * of a layout.
+ */
+
+typedef struct {
+  GtkLayoutManager *manager;
+  GtkWidget *widget;
+} GtkLayoutChildPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutChild, gtk_layout_child, G_TYPE_OBJECT)
+
+enum {
+  PROP_LAYOUT_MANAGER = 1,
+  PROP_CHILD_WIDGET,
+
+  N_PROPS
+};
+
+static GParamSpec *layout_child_properties[N_PROPS];
+
+static void
+gtk_layout_child_set_property (GObject      *gobject,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  switch (prop_id)
+    {
+    case PROP_LAYOUT_MANAGER:
+      priv->manager = g_value_get_object (value);
+      break;
+
+    case PROP_CHILD_WIDGET:
+      priv->widget = g_value_get_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gtk_layout_child_get_property (GObject      *gobject,
+                               guint         prop_id,
+                               GValue       *value,
+                               GParamSpec   *pspec)
+{
+  GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  switch (prop_id)
+    {
+    case PROP_LAYOUT_MANAGER:
+      g_value_set_object (value, priv->manager);
+      break;
+
+    case PROP_CHILD_WIDGET:
+      g_value_set_object (value, priv->widget);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gtk_layout_child_constructed (GObject *gobject)
+{
+  GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  G_OBJECT_CLASS (gtk_layout_child_parent_class)->constructed (gobject);
+
+  if (priv->manager == NULL)
+    {
+      g_critical ("The layout child of type %s does not have "
+                  "the GtkLayoutChild:layout-manager property set",
+                  G_OBJECT_TYPE_NAME (gobject));
+      return;
+    }
+
+  if (priv->widget == NULL)
+    {
+      g_critical ("The layout child of type %s does not have "
+                  "the GtkLayoutChild:child-widget property set",
+                  G_OBJECT_TYPE_NAME (gobject));
+      return;
+    }
+}
+
+static void
+gtk_layout_child_class_init (GtkLayoutChildClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = gtk_layout_child_set_property;
+  gobject_class->get_property = gtk_layout_child_get_property;
+  gobject_class->constructed = gtk_layout_child_constructed;
+
+  /**
+   * GtkLayoutChild:layout-manager:
+   *
+   * The layout manager that created the #GtkLayoutChild instance.
+   */
+  layout_child_properties[PROP_LAYOUT_MANAGER] =
+    g_param_spec_object ("layout-manager",
+                         "Layout Manager",
+                         "The layout manager that created this object",
+                         GTK_TYPE_LAYOUT_MANAGER,
+                         GTK_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkLayoutChild:child-widget:
+   *
+   * The widget that is associated to the #GtkLayoutChild instance.
+   */
+  layout_child_properties[PROP_CHILD_WIDGET] =
+    g_param_spec_object ("child-widget",
+                         "Child Widget",
+                         "The child widget that is associated to this object",
+                         GTK_TYPE_WIDGET,
+                         GTK_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, layout_child_properties);
+}
+
+static void
+gtk_layout_child_init (GtkLayoutChild *self)
+{
+}
+
+/**
+ * gtk_layout_child_get_layout_manager:
+ * @layout_child: a #GtkLayoutChild
+ *
+ * Retrieves the #GtkLayoutManager instance that created the
+ * given @layout_child.
+ *
+ * Returns: (transfer none): a #GtkLayoutManager
+ */
+GtkLayoutManager *
+gtk_layout_child_get_layout_manager (GtkLayoutChild *layout_child)
+{
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  g_return_val_if_fail (GTK_IS_LAYOUT_CHILD (layout_child), NULL);
+
+  return priv->manager;
+}
+
+/**
+ * gtk_layout_child_get_child_widget:
+ * @layout_child: a #GtkLayoutChild
+ *
+ * Retrieves the #GtkWidget associated to the given @layout_child.
+ *
+ * Returns: (transfer none): a #GtkWidget
+ */
+GtkWidget *
+gtk_layout_child_get_child_widget (GtkLayoutChild *layout_child)
+{
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  g_return_val_if_fail (GTK_IS_LAYOUT_CHILD (layout_child), NULL);
+
+  return priv->widget;
+}
diff --git a/gtk/gtklayoutchild.h b/gtk/gtklayoutchild.h
new file mode 100644
index 0000000000..3be8fdaf6a
--- /dev/null
+++ b/gtk/gtklayoutchild.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_LAYOUT_CHILD (gtk_layout_child_get_type())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (GtkLayoutChild, gtk_layout_child, GTK, LAYOUT_CHILD, GObject)
+
+struct _GtkLayoutChildClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+};
+
+GDK_AVAILABLE_IN_ALL
+GtkLayoutManager *      gtk_layout_child_get_layout_manager     (GtkLayoutChild *layout_child);
+GDK_AVAILABLE_IN_ALL
+GtkWidget *             gtk_layout_child_get_child_widget       (GtkLayoutChild *layout_child);
+
+G_END_DECLS
diff --git a/gtk/gtklayoutmanager.c b/gtk/gtklayoutmanager.c
index 7a5f2f08d5..02abe0d5c7 100644
--- a/gtk/gtklayoutmanager.c
+++ b/gtk/gtklayoutmanager.c
@@ -38,6 +38,7 @@
 #include "config.h"
 
 #include "gtklayoutmanagerprivate.h"
+#include "gtklayoutchild.h"
 #include "gtkwidget.h"
 
 #ifdef G_ENABLE_DEBUG
@@ -57,6 +58,8 @@ typedef struct {
 
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutManager, gtk_layout_manager, G_TYPE_OBJECT)
 
+static GQuark quark_layout_child;
+
 static GtkSizeRequestMode
 gtk_layout_manager_real_get_request_mode (GtkLayoutManager *manager,
                                           GtkWidget        *widget)
@@ -107,6 +110,8 @@ gtk_layout_manager_class_init (GtkLayoutManagerClass *klass)
   klass->get_request_mode = gtk_layout_manager_real_get_request_mode;
   klass->measure = gtk_layout_manager_real_measure;
   klass->allocate = gtk_layout_manager_real_allocate;
+
+  quark_layout_child = g_quark_from_static_string ("-GtkLayoutManager-layout-child");
 }
 
 static void
@@ -278,3 +283,75 @@ gtk_layout_manager_layout_changed (GtkLayoutManager *manager)
   if (priv->widget != NULL)
     gtk_widget_queue_resize (priv->widget);
 }
+
+/**
+ * gtk_layout_manager_get_layout_child:
+ * @manager: a #GtkLayoutManager
+ * @widget: a #GtkWidget
+ *
+ * Retrieves a #GtkLayoutChild instance for the #GtkLayoutManager, creating
+ * one if necessary
+ *
+ * The #GtkLayoutChild instance is owned by the #GtkLayoutManager, and is
+ * guaranteed to exist as long as @widget is a child of the #GtkWidget using
+ * the given #GtkLayoutManager.
+ *
+ * Returns: (transfer none): a #GtkLayoutChild
+ */
+GtkLayoutChild *
+gtk_layout_manager_get_layout_child (GtkLayoutManager *manager,
+                                     GtkWidget        *widget)
+{
+  GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
+  GtkLayoutChild *res;
+  GtkWidget *parent;
+
+  g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), NULL);
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  parent = gtk_widget_get_parent (widget);
+  g_return_val_if_fail (parent != NULL, NULL);
+
+  if (priv->widget != parent)
+    {
+      g_critical ("The parent %s %p of the widget %s %p does not "
+                  "use the given layout manager of type %s %p",
+                  gtk_widget_get_name (parent), parent,
+                  gtk_widget_get_name (widget), widget,
+                  G_OBJECT_TYPE_NAME (manager), manager);
+      return NULL;
+    }
+
+  if (GTK_LAYOUT_MANAGER_GET_CLASS (manager)->create_layout_child == NULL)
+    {
+      g_critical ("The layout manager of type %s %p does not create "
+                  "GtkLayoutChild instances",
+                  G_OBJECT_TYPE_NAME (manager), manager);
+      return NULL;
+    }
+
+  /* We store the LayoutChild into the Widget, so that the LayoutChild
+   * instance goes away once the Widget goes away
+   */
+  res = g_object_get_qdata (G_OBJECT (widget), quark_layout_child);
+  if (res != NULL)
+    {
+      /* If the LayoutChild instance is stale, and refers to another
+       * layout manager, then we simply ask the LayoutManager to
+       * replace it, as it means the layout manager for the parent
+       * widget was replaced
+       */
+      if (gtk_layout_child_get_layout_manager (res) == manager)
+        return res;
+    }
+
+  res = GTK_LAYOUT_MANAGER_GET_CLASS (manager)->create_layout_child (manager, widget);
+  g_assert (res != NULL);
+  g_assert (g_type_is_a (G_OBJECT_TYPE (res), GTK_TYPE_LAYOUT_CHILD));
+
+  g_object_set_qdata_full (G_OBJECT (widget), quark_layout_child,
+                           res,
+                           g_object_unref);
+
+  return res;
+}
diff --git a/gtk/gtklayoutmanager.h b/gtk/gtklayoutmanager.h
index 4d05132c4d..02f2173a36 100644
--- a/gtk/gtklayoutmanager.h
+++ b/gtk/gtklayoutmanager.h
@@ -20,6 +20,7 @@
 
 #include <gtk/gtktypes.h>
 #include <gtk/gtkwidget.h>
+#include <gtk/gtklayoutchild.h>
 
 G_BEGIN_DECLS
 
@@ -48,23 +49,26 @@ struct _GtkLayoutManagerClass
   GObjectClass parent_class;
 
   /*< public >*/
-  GtkSizeRequestMode (* get_request_mode) (GtkLayoutManager *manager,
-                                           GtkWidget        *widget);
+  GtkSizeRequestMode (* get_request_mode)    (GtkLayoutManager *manager,
+                                              GtkWidget        *widget);
 
-  void               (* measure)          (GtkLayoutManager *manager,
-                                           GtkWidget        *widget,
-                                           GtkOrientation    orientation,
-                                           int               for_size,
-                                           int              *minimum,
-                                           int              *natural,
-                                           int              *minimum_baseline,
-                                           int              *natural_baseline);
+  void               (* measure)             (GtkLayoutManager *manager,
+                                              GtkWidget        *widget,
+                                              GtkOrientation    orientation,
+                                              int               for_size,
+                                              int              *minimum,
+                                              int              *natural,
+                                              int              *minimum_baseline,
+                                              int              *natural_baseline);
 
-  void               (* allocate)         (GtkLayoutManager *manager,
-                                           GtkWidget        *widget,
-                                           int               width,
-                                           int               height,
-                                           int               baseline);
+  void               (* allocate)            (GtkLayoutManager *manager,
+                                              GtkWidget        *widget,
+                                              int               width,
+                                              int               height,
+                                              int               baseline);
+
+  GtkLayoutChild *   (* create_layout_child) (GtkLayoutManager *manager,
+                                              GtkWidget        *widget);
 
   /*< private >*/
   gpointer _padding[16];
@@ -95,4 +99,8 @@ GtkWidget *             gtk_layout_manager_get_widget           (GtkLayoutManage
 GDK_AVAILABLE_IN_ALL
 void                    gtk_layout_manager_layout_changed       (GtkLayoutManager *manager);
 
+GDK_AVAILABLE_IN_ALL
+GtkLayoutChild *        gtk_layout_manager_get_layout_child     (GtkLayoutManager *manager,
+                                                                 GtkWidget        *widget);
+
 G_END_DECLS
diff --git a/gtk/meson.build b/gtk/meson.build
index ce3147e5af..88f78c4f4a 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -256,6 +256,7 @@ gtk_public_sources = files([
   'gtkinfobar.c',
   'gtklabel.c',
   'gtklayout.c',
+  'gtklayoutchild.c',
   'gtklayoutmanager.c',
   'gtklevelbar.c',
   'gtklinkbutton.c',
@@ -507,6 +508,7 @@ gtk_public_headers = files([
   'gtkinfobar.h',
   'gtklabel.h',
   'gtklayout.h',
+  'gtklayoutchild.h',
   'gtklayoutmanager.h',
   'gtklevelbar.h',
   'gtklinkbutton.h',


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