diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2018-12-19 16:01:17 +0000 |
---|---|---|
committer | Emmanuele Bassi <ebassi@gnome.org> | 2019-03-26 00:11:27 +0000 |
commit | 5cbf6f5fbdf8146a62fa611b2279f587e34ee1f0 (patch) | |
tree | 12eb9ce0997e62210b71a423fc001b2d1d54c1da | |
parent | 15fda18791ae6df027620a2a8a42899f632337a8 (diff) | |
download | gtk+-5cbf6f5fbdf8146a62fa611b2279f587e34ee1f0.tar.gz |
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.
-rw-r--r-- | docs/reference/gtk/gtk4-sections.txt | 14 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtklayoutchild.c | 189 | ||||
-rw-r--r-- | gtk/gtklayoutchild.h | 27 | ||||
-rw-r--r-- | gtk/gtklayoutmanager.c | 77 | ||||
-rw-r--r-- | gtk/gtklayoutmanager.h | 38 | ||||
-rw-r--r-- | 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> @@ -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', |