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.
This commit is contained in:
Emmanuele Bassi 2018-12-19 16:01:17 +00:00
parent 15fda18791
commit 5cbf6f5fbd
7 changed files with 333 additions and 15 deletions

View File

@ -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>

View File

@ -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>

189
gtk/gtklayoutchild.c Normal file
View File

@ -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;
}

27
gtk/gtklayoutchild.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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',