[gtk+/composite-templates-new: 4/18] Add Composite Child machinery and APIs to GtkContainer
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/composite-templates-new: 4/18] Add Composite Child machinery and APIs to GtkContainer
- Date: Mon, 25 Mar 2013 11:21:22 +0000 (UTC)
commit 2086e3e8fda99279e26c7b26dd5d40e86939959a
Author: Tristan Van Berkom <tristanvb openismus com>
Date: Wed Mar 20 11:56:39 2013 +0900
Add Composite Child machinery and APIs to GtkContainer
This commit implements the needed machinery for GtkContainer
to build it's composite content from GtkBuilder XML and
adds the following API:
o gtk_container_init_template()
An api to be called in instance initializers of any
GtkContainer subclass that uses template XML to build it's components.
o gtk_container_class_set_template()
API to associate GtkBuilder XML to a given GtkContainer subclass
o gtk_container_class_automate_child()
API to declare an object built by GtkBuilder to be associated
with an instance structure offset and automatically set.
o gtk_container_get_automated_child()
API for bindings to fetch a child declared to be automated by
gtk_container_class_automate_child(), for the case where bindings
do not generate GObjects under the hood and cannot use structure
offsets to resolve composite object pointers.
o gtk_container_class_declare_callback[s]()
Declare static functions to be used in signal callbacks from
a given class's template XML
o gtk_container_class_set_connect_func()
API for bindings to override the signal connection machinery
for a given GtkContainer derived class.
docs/reference/gtk/gtk3-sections.txt | 10 +
gtk/gtk.symbols | 6 +
gtk/gtkcontainer.c | 776 ++++++++++++++++++++++++++++++++++
gtk/gtkcontainer.h | 78 ++++-
4 files changed, 869 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index 2098369..aa486cf 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -930,6 +930,16 @@ gtk_container_class_find_child_property
gtk_container_class_install_child_property
gtk_container_class_list_child_properties
gtk_container_class_handle_border_width
+gtk_container_init_template
+gtk_container_class_set_template
+gtk_container_class_set_template_from_resource
+gtk_container_get_automated_child
+gtk_container_class_bind_child
+gtk_container_class_bind_child_internal
+gtk_container_class_automate_child
+gtk_container_class_bind_callback
+gtk_container_class_declare_callback
+gtk_container_class_set_connect_func
<SUBSECTION Standard>
GTK_CONTAINER
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 31a2a8b..145f0d4 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -737,12 +737,18 @@ gtk_container_child_set
gtk_container_child_set_property
gtk_container_child_set_valist
gtk_container_child_type
+gtk_container_class_automate_child
+gtk_container_class_declare_callback
gtk_container_class_find_child_property
gtk_container_class_handle_border_width
gtk_container_class_install_child_property
gtk_container_class_list_child_properties
+gtk_container_class_set_signal_connect_func
+gtk_container_class_set_template
+gtk_container_class_set_template_from_resource
gtk_container_forall
gtk_container_foreach
+gtk_container_get_automated_child
gtk_container_get_border_width
gtk_container_get_children
gtk_container_get_focus_chain
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index ce16fe4..bcf9520 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -229,8 +229,90 @@
* for regular properties.
* </para>
* </refsect2>
+ * <refsect2 id="GtkContainer-BUILDER-TEMPLATES">
+ * <title>Building composite widgets from template XML</title>
+ * <para>
+ * GtkContainer exposes some facilities to automate the proceedure
+ * of creating composite widgets using #GtkBuilder interface description
+ * language.
+ * </para>
+ * <para>
+ * To create composite widgets with #GtkBuilder XML, one must associate
+ * the interface description with the container class at class initialization
+ * time using gtk_container_class_set_template().
+ * </para>
+ * <para>
+ * The interface description semantics expected in composite template descriptions
+ * is slightly different from regulare #GtkBuilder XML.
+ * </para>
+ * <para>
+ * Unlike regular interface descriptions, gtk_container_class_set_template() will expect a
+ * <template> tag as a direct child of the toplevel <interface>
+ * tag. The <template> tag must specify the "class" attribute which
+ * must be the type name of the container. Optionally, the "parent" attribute
+ * may be specified to specify the direct parent type of the container type, this
+ * is ignored by the GtkBuilder but required for Glade to introspect what kind
+ * of properties and internal children exist for a given type when the actual
+ * type does not exist.
+ * </para>
+ * <para>
+ * The XML which is contained inside the <template> tag behaves as if
+ * it were added to the <object> tag defining @widget itself. You may set
+ * properties on @widget by inserting <property> tags into the <template>
+ * tag, and also add <child> tags to add children and extend @widget in the
+ * normal way you would with <object> tags.
+ * </para>
+ * <para>
+ * Additionally, <object> tags can also be added before and
+ * after the initial <template> tag in the normal way, allowing
+ * one to define auxilary objects which might be referenced by other
+ * widgets declared as children of the <template> tag.
+ * </para>
+ * <para>
+ * <example>
+ * <title>A GtkBuilder Template Definition</title>
+ * <programlisting><![CDATA[
+ * <interface>
+ * <template class="FooWidget" parent="GtkBox">
+ * <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+ * <property name="spacing">4</property>
+ * <child>
+ * <object class="GtkButton" id="hello_button">
+ * <property name="label">Hello World</property>
+ * </object>
+ * </child>
+ * <child>
+ * <object class="GtkButton" id="goodbye_button">
+ * <property name="label">Goodbye World</property>
+ * </object>
+ * </child>
+ * </template>
+ * </interface>
+ * ]]></programlisting>
+ * </example>
+ * </para>
+ * </refsect2>
*/
+typedef struct {
+ gchar *name; /* Name of the template automatic child */
+ gboolean internal_child; /* Whether the automatic widget should be exported as an
<internal-child> */
+ gssize offset; /* Instance private data offset where to set the automatic child (or
-1) */
+} AutomaticChildClass;
+
+typedef struct {
+ gchar *callback_name;
+ GCallback callback_symbol;
+} CallbackSymbol;
+
+struct _GtkContainerTemplate {
+ GBytes *data;
+ GSList *children;
+ GSList *callbacks;
+ GtkBuilderConnectFunc connect_func;
+ gpointer connect_data;
+ GDestroyNotify destroy_notify;
+};
struct _GtkContainerPrivate
{
@@ -239,6 +321,11 @@ struct _GtkContainerPrivate
guint resize_handler;
GdkFrameClock *resize_clock;
+ /* A hash by GType key, containing
+ * hash tables by widget name
+ */
+ GHashTable *auto_children;
+
guint border_width : 16;
guint has_focus_chain : 1;
@@ -323,12 +410,33 @@ static gchar* gtk_container_child_default_composite_name (GtkContainer *containe
static GtkWidgetPath * gtk_container_real_get_path_for_child (GtkContainer *container,
GtkWidget *child);
+/* --- functions dealing with template data structures --- */
+static AutomaticChildClass *automatic_child_class_new (const gchar *name,
+ gboolean internal_child,
+ gssize offset);
+static void automatic_child_class_free (AutomaticChildClass *child_class);
+static CallbackSymbol *callback_symbol_new (const gchar *name,
+ GCallback callback);
+static void callback_symbol_free (CallbackSymbol *callback);
+static void template_data_free (GtkContainerTemplate *template_data);
+static GHashTable *get_auto_child_hash (GtkContainer *container,
+ GType type,
+ gboolean create);
+static gboolean setup_automatic_child (GtkContainerTemplate *template_data,
+ GType class_type,
+ AutomaticChildClass *child_class,
+ GtkContainer *container,
+ GtkBuilder *builder);
+
/* GtkBuildable */
static void gtk_container_buildable_init (GtkBuildableIface *iface);
static void gtk_container_buildable_add_child (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *type);
+static GObject *gtk_container_buildable_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *childname);
static gboolean gtk_container_buildable_custom_tag_start (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
@@ -402,6 +510,7 @@ gtk_container_base_class_init (GtkContainerClass *class)
/* reset instance specifc class fields that don't get inherited */
class->set_child_property = NULL;
class->get_child_property = NULL;
+ class->tmpl = NULL;
}
static void
@@ -419,6 +528,7 @@ gtk_container_base_class_finalize (GtkContainerClass *class)
g_param_spec_unref (pspec);
}
g_list_free (list);
+ template_data_free (class->tmpl);
}
static void
@@ -526,6 +636,7 @@ gtk_container_buildable_init (GtkBuildableIface *iface)
{
parent_buildable_iface = g_type_interface_peek_parent (iface);
iface->add_child = gtk_container_buildable_add_child;
+ iface->get_internal_child = gtk_container_buildable_get_internal_child;
iface->custom_tag_start = gtk_container_buildable_custom_tag_start;
iface->custom_tag_end = gtk_container_buildable_custom_tag_end;
}
@@ -550,6 +661,49 @@ gtk_container_buildable_add_child (GtkBuildable *buildable,
g_type_name (G_OBJECT_TYPE (child)), g_type_name (G_OBJECT_TYPE (buildable)));
}
+static GObject *
+gtk_container_buildable_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *childname)
+{
+ GtkContainerClass *class;
+ GSList *l;
+ GType internal_child_type = 0;
+
+ /* Check if it's a valid internal child first, only children
+ * that are explicitly declared to be exported as 'internal-children'
+ * can be evaluated here
+ */
+ for (class = GTK_CONTAINER_GET_CLASS (buildable);
+ GTK_IS_CONTAINER_CLASS (class);
+ class = g_type_class_peek_parent (class))
+ {
+ GtkContainerTemplate *template = class->tmpl;
+
+ if (!template)
+ continue;
+
+ for (l = template->children; l && internal_child_type == 0; l = l->next)
+ {
+ AutomaticChildClass *child_class = l->data;
+
+ if (child_class->internal_child &&
+ strcmp (childname, child_class->name) == 0)
+ internal_child_type = G_OBJECT_CLASS_TYPE (class);
+ }
+ }
+
+ /* Now return the 'internal-child' from the class which declared it, note
+ * that gtk_container_get_automated_child() an API used to access objects
+ * which are in the private scope of a given class (so it's not appropriate
+ * to call it here if the child is not explicitly marked to be internal).
+ */
+ if (internal_child_type != 0)
+ return gtk_container_get_automated_child (GTK_CONTAINER (buildable), internal_child_type, childname);
+
+ return parent_buildable_iface->get_internal_child (buildable, builder, childname);
+}
+
static void
gtk_container_buildable_set_child_property (GtkContainer *container,
GtkBuilder *builder,
@@ -1350,6 +1504,83 @@ gtk_container_init (GtkContainer *container)
priv->reallocate_redraws = FALSE;
}
+#ifdef G_ENABLE_DEBUG
+
+typedef struct {
+ AutomaticChildClass *child_class;
+ GType container_type;
+ GObject *object;
+ gboolean did_finalize;
+} FinalizeAssertion;
+
+static void
+finalize_assertion_weak_ref (gpointer data,
+ GObject *where_the_object_was)
+{
+ FinalizeAssertion *assertion = (FinalizeAssertion *)data;
+ assertion->did_finalize = TRUE;
+}
+
+static FinalizeAssertion *
+finalize_assertion_new (GtkContainer *container,
+ GType container_type,
+ AutomaticChildClass *child_class)
+{
+ FinalizeAssertion *assertion = NULL;
+ GObject *object;
+
+ object = gtk_container_get_automated_child (container, container_type, child_class->name);
+
+ /* We control the hash table entry, the object should never be NULL
+ */
+ g_assert (object);
+ if (!G_IS_OBJECT (object))
+ g_critical ("Automated component `%s' of class `%s' seems to have been prematurely finalized",
+ child_class->name, g_type_name (container_type));
+ else
+ {
+ assertion = g_slice_new0 (FinalizeAssertion);
+ assertion->child_class = child_class;
+ assertion->container_type = container_type;
+ assertion->object = object;
+
+ g_object_weak_ref (object, finalize_assertion_weak_ref, assertion);
+ }
+
+ return assertion;
+}
+
+static GSList *
+build_finalize_assertion_list (GtkContainer *container)
+{
+ GType class_type;
+ GtkContainerClass *class;
+ GSList *l, *list = NULL;
+
+ for (class = GTK_CONTAINER_GET_CLASS (container);
+ GTK_IS_CONTAINER_CLASS (class);
+ class = g_type_class_peek_parent (class))
+ {
+ if (!class->tmpl)
+ continue;
+
+ class_type = G_OBJECT_CLASS_TYPE (class);
+
+ for (l = class->tmpl->children; l; l = l->next)
+ {
+ AutomaticChildClass *child_class = l->data;
+ FinalizeAssertion *assertion;
+
+ assertion = finalize_assertion_new (container, class_type, child_class);
+ list = g_slist_prepend (list, assertion);
+ }
+ }
+
+ return list;
+}
+
+#endif /* G_ENABLE_DEBUG */
+
static void
gtk_container_destroy (GtkWidget *widget)
{
@@ -1376,6 +1607,77 @@ gtk_container_destroy (GtkWidget *widget)
gtk_container_foreach (container, (GtkCallback) gtk_widget_destroy, NULL);
+ if (priv->auto_children)
+ {
+ GType class_type;
+ GtkContainerClass *class;
+ GSList *l;
+
+#ifdef G_ENABLE_DEBUG
+ GSList *assertions = NULL;
+
+ /* Note, GTK_CONTAINER_ASSERT_COMPONENTS is very useful
+ * to catch ref counting bugs, but can only be used in
+ * test cases which simply create and destroy a composite
+ * widget.
+ *
+ * This is because some API can expose components explicitly,
+ * and so we cannot assert that a component is expected to finalize
+ * in a full application ecosystem.
+ */
+ if (g_getenv ("GTK_CONTAINER_ASSERT_COMPONENTS") != NULL)
+ assertions = build_finalize_assertion_list (container);
+#endif /* G_ENABLE_DEBUG */
+
+ /* Release references to all automated children */
+ g_hash_table_destroy (priv->auto_children);
+ priv->auto_children = NULL;
+
+#ifdef G_ENABLE_DEBUG
+ for (l = assertions; l; l = l->next)
+ {
+ FinalizeAssertion *assertion = l->data;
+
+ if (!assertion->did_finalize)
+ g_critical ("Automated component `%s' of class `%s' did not finalize in gtk_container_destroy(). "
+ "Current reference count is %d",
+ assertion->child_class->name,
+ g_type_name (assertion->container_type),
+ assertion->object->ref_count);
+
+ g_slice_free (FinalizeAssertion, assertion);
+ }
+ g_slist_free (assertions);
+#endif /* G_ENABLE_DEBUG */
+
+ /* Set any automatic private data pointers to NULL */
+ for (class = GTK_CONTAINER_GET_CLASS (container);
+ GTK_IS_CONTAINER_CLASS (class);
+ class = g_type_class_peek_parent (class))
+ {
+ if (!class->tmpl)
+ continue;
+
+ class_type = G_OBJECT_CLASS_TYPE (class);
+
+ for (l = class->tmpl->children; l; l = l->next)
+ {
+ AutomaticChildClass *child_class = l->data;
+
+ if (child_class->offset >= 0)
+ {
+ gpointer class_private;
+ GObject **destination;
+
+ /* Nullify instance private data for internal children */
+ class_private = G_TYPE_INSTANCE_GET_PRIVATE (container, class_type, gpointer);
+ destination = G_STRUCT_MEMBER_P (class_private, child_class->offset);
+ *destination = NULL;
+ }
+ }
+ }
+ }
+
GTK_WIDGET_CLASS (parent_class)->destroy (widget);
}
@@ -3415,3 +3717,477 @@ gtk_container_get_path_for_child (GtkContainer *container,
return path;
}
+
+/****************************************************************
+ * GtkBuilder automated templates *
+ ****************************************************************/
+static AutomaticChildClass *
+automatic_child_class_new (const gchar *name,
+ gboolean internal_child,
+ gssize offset)
+{
+ AutomaticChildClass *child_class = g_slice_new0 (AutomaticChildClass);
+
+ child_class->name = g_strdup (name);
+ child_class->internal_child = internal_child;
+ child_class->offset = offset;
+
+ return child_class;
+}
+
+static void
+automatic_child_class_free (AutomaticChildClass *child_class)
+{
+ if (child_class)
+ {
+ g_free (child_class->name);
+ g_slice_free (AutomaticChildClass, child_class);
+ }
+}
+
+static CallbackSymbol *
+callback_symbol_new (const gchar *name,
+ GCallback callback)
+{
+ CallbackSymbol *cb = g_slice_new0 (CallbackSymbol);
+
+ cb->callback_name = g_strdup (name);
+ cb->callback_symbol = callback;
+
+ return cb;
+}
+
+static void
+callback_symbol_free (CallbackSymbol *callback)
+{
+ if (callback)
+ {
+ g_free (callback->callback_name);
+ g_slice_free (CallbackSymbol, callback);
+ }
+}
+
+static void
+template_data_free (GtkContainerTemplate *template_data)
+{
+ if (template_data)
+ {
+ g_bytes_unref (template_data->data);
+ g_slist_free_full (template_data->children, (GDestroyNotify)automatic_child_class_free);
+ g_slist_free_full (template_data->callbacks, (GDestroyNotify)callback_symbol_free);
+
+ if (template_data->connect_data &&
+ template_data->destroy_notify)
+ template_data->destroy_notify (template_data->connect_data);
+
+ g_slice_free (GtkContainerTemplate, template_data);
+ }
+}
+
+static GHashTable *
+get_auto_child_hash (GtkContainer *container,
+ GType type,
+ gboolean create)
+{
+ GHashTable *auto_child_hash;
+
+ if (container->priv->auto_children == NULL)
+ {
+ if (!create)
+ return NULL;
+
+ container->priv->auto_children =
+ g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)g_hash_table_destroy);
+ }
+
+ auto_child_hash =
+ g_hash_table_lookup (container->priv->auto_children, GSIZE_TO_POINTER (type));
+
+ if (!auto_child_hash && create)
+ {
+ auto_child_hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify)g_object_unref);
+
+ g_hash_table_insert (container->priv->auto_children,
+ GSIZE_TO_POINTER (type),
+ auto_child_hash);
+ }
+
+ return auto_child_hash;
+}
+
+static gboolean
+setup_automatic_child (GtkContainerTemplate *template_data,
+ GType class_type,
+ AutomaticChildClass *child_class,
+ GtkContainer *container,
+ GtkBuilder *builder)
+{
+ GHashTable *auto_child_hash;
+ GObject *object;
+
+ object = gtk_builder_get_object (builder, child_class->name);
+ if (!object)
+ {
+ g_critical ("Unable to retrieve object '%s' from class template for type '%s' while building a '%s'",
+ child_class->name, g_type_name (class_type), G_OBJECT_TYPE_NAME (container));
+ return FALSE;
+ }
+
+ /* Insert into the hash so that it can be fetched with
+ * gtk_container_get_automated_child() and also in automated
+ * implementations of GtkBuildable.get_internal_child()
+ */
+ auto_child_hash = get_auto_child_hash (container, class_type, TRUE);
+ g_hash_table_insert (auto_child_hash, child_class->name, g_object_ref (object));
+
+ if (child_class->offset >= 0)
+ {
+ gpointer class_private;
+ GObject **destination;
+
+ /* Assign 'object' to the specified offset in the instance private data */
+ class_private = G_TYPE_INSTANCE_GET_PRIVATE (container, class_type, gpointer);
+ destination = G_STRUCT_MEMBER_P (class_private, child_class->offset);
+ *destination = object;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gtk_container_init_template:
+ * @container: a #GtkContainer
+ *
+ * Creates and initializes child widgets defined in templates. This
+ * function must be called in the instance initializer for any
+ * class which assigned itself a template using gtk_container_class_set_template()
+ *
+ * It is important to call this function in the instance initializer
+ * of a #GtkContainer subclass and not in #GObject.constructed() or
+ * #GObject.constructor() for two reasons.
+ *
+ * One reason is that generally derived widgets will assume that parent
+ * class composite widgets have been created in their instance
+ * initializers.
+ *
+ * Another reason is that when calling g_object_new() on a container with
+ * composite templates, it's important to build the composite widgets
+ * before the construct properties are set. Properties passed to g_object_new()
+ * should take precedence over properties set in the private template XML.
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_init_template (GtkContainer *container)
+{
+ GtkContainerTemplate *template;
+ GtkBuilder *builder;
+ GError *error = NULL;
+ GObject *object;
+ GtkWidget *widget;
+ GSList *l;
+ GType class_type;
+
+ g_return_if_fail (GTK_IS_CONTAINER (container));
+
+ widget = GTK_WIDGET (container);
+ object = G_OBJECT (container);
+ class_type = G_OBJECT_TYPE (container);
+
+ template = GTK_CONTAINER_GET_CLASS (container)->tmpl;
+ g_return_if_fail (template != NULL);
+
+ /* It is important that gtk_container_template_init() to build templates at widget_init()
+ * time rather than in the constructor() for two reasons:
+ *
+ * o Many third party derived container widgets assume
+ * that parent class widgetry is already built in thier own
+ * widget _init() time. For instance GtkDialog child classes
+ * which access the vbox/action_area from the _init() function.
+ *
+ * o When calling g_object_new() on a container with composite
+ * templates, it's important to build widgetry and set properties
+ * before the construct properties are set. Properties passed
+ * to g_object_new() should take precedence over properties
+ * set in the private template XML.
+ */
+ builder = gtk_builder_new ();
+
+ /* Add any callback symbols declared for this GType to the GtkBuilder namespace */
+ for (l = template->callbacks; l; l = l->next)
+ {
+ CallbackSymbol *callback = l->data;
+
+ gtk_builder_add_callback_symbol (builder, callback->callback_name, callback->callback_symbol);
+ }
+
+ /* This will build the template XML as children to the container instance, also it
+ * will validate that the template is created for the correct GType and assert that
+ * there is no infinate recursion.
+ */
+ if (!_gtk_builder_extend_with_template (builder, widget, class_type,
+ (const gchar *)g_bytes_get_data (template->data, NULL),
+ g_bytes_get_size (template->data),
+ &error))
+ {
+ g_critical ("Error building template class '%s' for an instance of type '%s': %s",
+ g_type_name (class_type), G_OBJECT_TYPE_NAME (object), error->message);
+ g_error_free (error);
+
+ /* This should never happen, if the template XML cannot be built
+ * then it is a critical programming error.
+ */
+ g_object_unref (builder);
+ return;
+ }
+
+ /* Build the automatic child data
+ */
+ for (l = template->children; l; l = l->next)
+ {
+ AutomaticChildClass *child_class = l->data;
+
+ /* This will setup the pointer of an automated child, and cause
+ * it to be available in any GtkBuildable.get_internal_child()
+ * invocations which may follow by reference in child classes.
+ */
+ if (!setup_automatic_child (template,
+ class_type,
+ child_class,
+ container,
+ builder))
+ {
+ g_object_unref (builder);
+ return;
+ }
+ }
+
+ /* Connect signals. All signal data from a template receive the
+ * template instance as user data automatically.
+ *
+ * A GtkBuilderConnectFunc can be provided to gtk_container_class_set_signal_connect_func()
+ * in order for templates to be usable by bindings.
+ */
+ if (template->connect_func)
+ gtk_builder_connect_signals_full (builder, template->connect_func, template->connect_data);
+ else
+ gtk_builder_connect_signals (builder, object);
+
+ g_object_unref (builder);
+}
+
+/**
+ * gtk_container_class_set_template:
+ * @container_class: A #GtkContainerClass
+ * @template_bytes: A #GBytes holding the #GtkBuilder XML
+ *
+ * This should be called at class initialization time to specify
+ * the GtkBuilder XML to be used to extend a #GtkContainer type widget.
+ *
+ * For convenience, gtk_container_class_set_template_from_resource() is also provided.
+ *
+ * <note><para>Any class that installs templates must call gtk_container_init_template()
+ * in the widget's instance initializer</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_set_template (GtkContainerClass *container_class,
+ GBytes *template_bytes)
+{
+ g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+ g_return_if_fail (container_class->tmpl == NULL);
+ g_return_if_fail (template_bytes != NULL);
+
+ container_class->tmpl = g_slice_new0 (GtkContainerTemplate);
+ container_class->tmpl->data = g_bytes_ref (template_bytes);
+}
+
+/**
+ * gtk_container_class_set_template_from_resource:
+ * @container_class: A #GtkContainerClass
+ * @resource_name: The name of the resource to load the template from
+ *
+ * A convenience function to call gtk_container_class_set_template().
+ *
+ * <note><para>Any class that installs templates must call gtk_container_init_template()
+ * in the widget's instance initializer</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_set_template_from_resource (GtkContainerClass *container_class,
+ const gchar *resource_name)
+{
+ GError *error = NULL;
+ GBytes *bytes = NULL;
+
+ g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+ g_return_if_fail (container_class->tmpl == NULL);
+ g_return_if_fail (resource_name && resource_name[0]);
+
+ bytes = g_resources_lookup_data (resource_name, 0, &error);
+ if (!bytes)
+ {
+ g_critical ("Unable to load resource for composite template for type '%s': %s",
+ G_OBJECT_CLASS_NAME (container_class), error->message);
+ g_error_free (error);
+ return;
+ }
+
+ gtk_container_class_set_template (container_class, bytes);
+ g_bytes_unref (bytes);
+}
+
+/**
+ * gtk_container_class_declare_callback:
+ * @container_class: A #GtkContainerClass
+ * @callback_name: The name of the callback as expected in the template XML
+ * @callback_symbol: (scope async): The callback symbol
+ *
+ * Declares a @callback_symbol to handle @callback_name from the template XML
+ * defined for @container_type. See gtk_builder_add_callback_symbol().
+ *
+ * <note><para>This must be called from a composite container classes class
+ * initializer after calling gtk_container_class_set_template()</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_declare_callback (GtkContainerClass *container_class,
+ const gchar *callback_name,
+ GCallback callback_symbol)
+{
+ CallbackSymbol *cb;
+
+ g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+ g_return_if_fail (container_class->tmpl != NULL);
+ g_return_if_fail (callback_name && callback_name[0]);
+ g_return_if_fail (callback_symbol != NULL);
+
+ cb = callback_symbol_new (callback_name, callback_symbol);
+ container_class->tmpl->callbacks = g_slist_prepend (container_class->tmpl->callbacks, cb);
+}
+
+/**
+ * gtk_container_class_set_connect_func:
+ * @container_class: A #GtkContainerClass
+ * @connect_func: The #GtkBuilderConnectFunc to use when connecting signals in the class template
+ * @connect_data: The data to pass to @connect_func
+ * @connect_data_destroy: The #GDestroyNotify to free @connect_data, this will only be used at
+ * class finalization time, when no classes of type @container_type are in use
anymore.
+ *
+ * For use in lanuage bindings, this will override the default #GtkBuilderConnectFunc to be
+ * used when parsing GtkBuilder xml from this class's template data.
+ *
+ * <note><para>This must be called from a composite container classes class
+ * initializer after calling gtk_container_class_set_template()</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_set_connect_func (GtkContainerClass *container_class,
+ GtkBuilderConnectFunc connect_func,
+ gpointer connect_data,
+ GDestroyNotify connect_data_destroy)
+{
+ g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+ g_return_if_fail (container_class->tmpl != NULL);
+
+ /* Defensive, destroy any previously set data */
+ if (container_class->tmpl->connect_data &&
+ container_class->tmpl->destroy_notify)
+ container_class->tmpl->destroy_notify (container_class->tmpl->connect_data);
+
+ container_class->tmpl->connect_func = connect_func;
+ container_class->tmpl->connect_data = connect_data;
+ container_class->tmpl->destroy_notify = connect_data_destroy;
+}
+
+/**
+ * gtk_container_class_automate_child:
+ * @container_class: A #GtkContainerClass
+ * @name: The "id" of the child defined in the template XML
+ * @internal_child: Whether the child should be accessible as an "internal-child"
+ * when this class is used in GtkBuilder XML
+ * @struct_offset: The structure offset into the composite container's instance private structure
+ * where the automated child pointer should be set, or -1 to not assign the pointer.
+ *
+ * Automatically assign an object declared in the class template XML to be set to a location
+ * on a freshly built instance's private data, or alternatively accessible via
gtk_container_get_automated_child().
+ *
+ * An explicit strong reference will be held automatically for the duration of your
+ * instance's life cycle, it will be released automatically when #GObjectClass.dispose() runs
+ * on your instance and if a @struct_offset that is >= 0 is specified, then the automatic location
+ * in your instance private data will be set to %NULL. You can however access an automated child
+ * pointer the first time your classes #GObjectClass.dispose() runs, or alternatively in
+ * #GtkWidgetClass.destroy().
+ *
+ * If @internal_child is specified, #GtkBuildableIface.get_internal_child() will be automatically
+ * implemented by the #GtkContainer class so there is no need to implement it manually.
+ *
+ * <note><para>This must be called from a composite container classes class
+ * initializer after calling gtk_container_class_set_template()</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_automate_child (GtkContainerClass *container_class,
+ const gchar *name,
+ gboolean internal_child,
+ gssize struct_offset)
+{
+ AutomaticChildClass *child_class;
+
+ g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+ g_return_if_fail (container_class->tmpl != NULL);
+ g_return_if_fail (name && name[0]);
+
+ child_class = automatic_child_class_new (name,
+ internal_child,
+ struct_offset);
+ container_class->tmpl->children =
+ g_slist_prepend (container_class->tmpl->children, child_class);
+}
+
+/**
+ * gtk_container_get_automated_child:
+ * @container: A #GtkContainer
+ * @container_type: The #GType to get an automated child for
+ * @name: The "id" of the child defined in the template XML
+ *
+ * Fetch an object build from the template XML for @container_type in this @container instance.
+ *
+ * This will only report children which were previously declared with gtk_container_class_automate_child().
+ *
+ * This function is only meant to be called for code which is private to the @container_type which
+ * declared the child and is meant for language bindings which cannot easily make use
+ * of the GObject structure offsets.
+ *
+ * Returns: (transfer none): The object built in the template XML with the id @name
+ */
+GObject *
+gtk_container_get_automated_child (GtkContainer *container,
+ GType container_type,
+ const gchar *name)
+{
+ GHashTable *auto_child_hash;
+ GObject *ret = NULL;
+
+ g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
+ g_return_val_if_fail (g_type_name (container_type) != NULL, NULL);
+ g_return_val_if_fail (name && name[0], NULL);
+
+ auto_child_hash = get_auto_child_hash (container, container_type, FALSE);
+
+ if (auto_child_hash)
+ ret = g_hash_table_lookup (auto_child_hash, name);
+
+ return ret;
+}
diff --git a/gtk/gtkcontainer.h b/gtk/gtkcontainer.h
index 3bcd2b1..fd26700 100644
--- a/gtk/gtkcontainer.h
+++ b/gtk/gtkcontainer.h
@@ -31,6 +31,7 @@
#endif
#include <gtk/gtkwidget.h>
+#include <gtk/gtkbuilder.h>
G_BEGIN_DECLS
@@ -46,6 +47,7 @@ G_BEGIN_DECLS
typedef struct _GtkContainer GtkContainer;
typedef struct _GtkContainerPrivate GtkContainerPrivate;
typedef struct _GtkContainerClass GtkContainerClass;
+typedef struct _GtkContainerTemplate GtkContainerTemplate;
struct _GtkContainer
{
@@ -91,6 +93,8 @@ struct _GtkContainerClass
unsigned int _handle_border_width : 1;
+ GtkContainerTemplate *tmpl;
+
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
@@ -99,7 +103,6 @@ struct _GtkContainerClass
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
- void (*_gtk_reserved8) (void);
};
@@ -222,6 +225,79 @@ void gtk_container_class_handle_border_width (GtkContainerClass *klass);
GtkWidgetPath * gtk_container_get_path_for_child (GtkContainer *container,
GtkWidget *child);
+/**
+ * gtk_container_class_bind_callback:
+ * @container_class: a #GtkContainerClass
+ * @callback: the callback symbol
+ *
+ * Shorthand for gtk_container_class_declare_callback(), adds a symbol
+ * by it's own name to the @container_class.
+ *
+ * Since: 3.10
+ */
+#define gtk_container_class_bind_callback(container_class, callback) \
+ gtk_container_class_declare_callback (GTK_CONTAINER_CLASS (container_class), \
+ #callback, G_CALLBACK(callback))
+
+/**
+ * gtk_container_class_bind_child:
+ * @container_class: a #GtkContainerClass
+ * @private_data_type: the type name of this container class's instance private data
+ * @member_name: name of the instance private member on @private_data_type
+ *
+ * Shorthand for gtk_container_class_automate_child(). This macro assumes that
+ * the @member_name is the name of the component instance to lookup as specified
+ * in the composite template.
+ *
+ * Since: 3.10
+ */
+#define gtk_container_class_bind_child(container_class, private_data_type, member_name) \
+ gtk_container_class_automate_child (container_class, #member_name, FALSE, \
+ G_STRUCT_OFFSET (private_data_type, member_name))
+
+/**
+ * gtk_container_class_bind_child_internal:
+ * @container_class: a #GtkContainerClass
+ * @private_data_type: the type name of this container class's instance private data
+ * @member_name: name of the instance private member on @private_data_type
+ *
+ * Shorthand for gtk_container_class_automate_child(). Essentially the same as
+ * gtk_container_class_bind_child() except that it will export the child as
+ * an internal child.
+ *
+ * Since: 3.10
+ */
+#define gtk_container_class_bind_child_internal(container_class, private_data_type, member_name) \
+ gtk_container_class_automate_child (container_class, #member_name, TRUE, \
+ G_STRUCT_OFFSET (private_data_type, member_name))
+
+GDK_AVAILABLE_IN_3_10
+void gtk_container_init_template (GtkContainer *container);
+GDK_AVAILABLE_IN_3_10
+GObject *gtk_container_get_automated_child (GtkContainer *container,
+ GType container_type,
+ const gchar *name);
+GDK_AVAILABLE_IN_3_10
+void gtk_container_class_set_template (GtkContainerClass *container_class,
+ GBytes *template_bytes);
+GDK_AVAILABLE_IN_3_10
+void gtk_container_class_set_template_from_resource (GtkContainerClass *container_class,
+ const gchar *resource_name);
+GDK_AVAILABLE_IN_3_10
+void gtk_container_class_declare_callback (GtkContainerClass *container_class,
+ const gchar *callback_name,
+ GCallback callback_symbol);
+GDK_AVAILABLE_IN_3_10
+void gtk_container_class_set_connect_func (GtkContainerClass *container_class,
+ GtkBuilderConnectFunc connect_func,
+ gpointer connect_data,
+ GDestroyNotify connect_data_destroy);
+GDK_AVAILABLE_IN_3_10
+void gtk_container_class_automate_child (GtkContainerClass *container_class,
+ const gchar *name,
+ gboolean internal_child,
+ gssize struct_offset);
+
G_END_DECLS
#endif /* __GTK_CONTAINER_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]