GtkContainer: Support focus chain in GtkBuilder

This adds support for a <focus-chain> element which can be
used to specify a custom focus order for the children of
a container.

https://bugzilla.gnome.org/show_bug.cgi?id=582799
This commit is contained in:
Matthias Clasen 2014-09-27 00:20:50 -04:00
parent 6d3c61856b
commit aa3020a39e

View File

@ -221,21 +221,33 @@
* a <packing> element for children, which can contain multiple <property> * a <packing> element for children, which can contain multiple <property>
* elements that specify child properties for the child. * elements that specify child properties for the child.
* *
* An example of child properties in UI definitions: * Since 2.16, child properties can also be marked as translatable using
* the same translatable, comments and context attributes that are used
* for regular properties.
*
* Since 3.16, containers can have a <focus-chain> element containing multiple
* <widget> elements, one for each child that should be added to the focus
* chain. The name attribute gives the id of the widget.
*
* An example of these properties in UI definitions:
* |[ * |[
* <object class="GtkVBox"> * <object class="GtkBox">
* <child> * <child>
* <object class="GtkLabel"/> * <object class="GtkEntry" id="entry1"/>
* <packing> * <packing>
* <property name="pack-type">start</property> * <property name="pack-type">start</property>
* </packing> * </packing>
* </child> * </child>
* <child>
* <object class="GtkEntry" id="entry2"/>
* </child>
* <focus-chain>
* <widget name="entry1"/>
* <widget name="entry2"/>
* </focus-chain>
* </object> * </object>
* ]| * ]|
* *
* Since 2.16, child properties can also be marked as translatable using
* the same translatable, comments and context attributes that are used
* for regular properties.
*/ */
@ -353,6 +365,11 @@ static void gtk_container_buildable_custom_tag_end (GtkBuildable *buildable,
GObject *child, GObject *child,
const gchar *tagname, const gchar *tagname,
gpointer *data); gpointer *data);
static void gtk_container_buildable_custom_finished (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *tagname,
gpointer data);
static gboolean gtk_container_should_propagate_draw (GtkContainer *container, static gboolean gtk_container_should_propagate_draw (GtkContainer *container,
GtkWidget *child, GtkWidget *child,
@ -557,6 +574,7 @@ gtk_container_buildable_init (GtkBuildableIface *iface)
iface->add_child = gtk_container_buildable_add_child; iface->add_child = gtk_container_buildable_add_child;
iface->custom_tag_start = gtk_container_buildable_custom_tag_start; iface->custom_tag_start = gtk_container_buildable_custom_tag_start;
iface->custom_tag_end = gtk_container_buildable_custom_tag_end; iface->custom_tag_end = gtk_container_buildable_custom_tag_end;
iface->custom_finished = gtk_container_buildable_custom_finished;
} }
static void static void
@ -727,6 +745,46 @@ static const GMarkupParser attributes_parser =
attributes_text_element, attributes_text_element,
}; };
typedef struct
{
GSList *items;
GObject *object;
} FocusChainData;
static void
focus_chain_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **names,
const gchar **values,
gpointer user_data,
GError **error)
{
guint i;
FocusChainData *data = (FocusChainData*)user_data;
if (strcmp (element_name, "widget") == 0)
{
for (i = 0; names[i]; i++)
{
if (strcmp (names[i], "name") == 0)
data->items = g_slist_prepend (data->items, g_strdup (values[i]));
}
}
else if (strcmp (element_name, "focus-chain") == 0)
{
return;
}
else
{
g_warning ("Unsupported type tag for GtkContainer %s\n", element_name);
}
}
static const GMarkupParser focus_chain_parser =
{
focus_chain_start_element
};
static gboolean static gboolean
gtk_container_buildable_custom_tag_start (GtkBuildable *buildable, gtk_container_buildable_custom_tag_start (GtkBuildable *buildable,
GtkBuilder *builder, GtkBuilder *builder,
@ -735,14 +793,14 @@ gtk_container_buildable_custom_tag_start (GtkBuildable *buildable,
GMarkupParser *parser, GMarkupParser *parser,
gpointer *data) gpointer *data)
{ {
PackingPropertiesData *parser_data;
if (parent_buildable_iface->custom_tag_start (buildable, builder, child, if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
tagname, parser, data)) tagname, parser, data))
return TRUE; return TRUE;
if (child && strcmp (tagname, "packing") == 0) if (child && strcmp (tagname, "packing") == 0)
{ {
PackingPropertiesData *parser_data;
parser_data = g_slice_new0 (PackingPropertiesData); parser_data = g_slice_new0 (PackingPropertiesData);
parser_data->string = g_string_new (""); parser_data->string = g_string_new ("");
parser_data->builder = builder; parser_data->builder = builder;
@ -754,6 +812,17 @@ gtk_container_buildable_custom_tag_start (GtkBuildable *buildable,
*data = parser_data; *data = parser_data;
return TRUE; return TRUE;
} }
else if (!child && strcmp (tagname, "focus-chain") == 0)
{
FocusChainData *parser_data;
parser_data = g_slice_new0 (FocusChainData);
parser_data->items = NULL;
parser_data->object = G_OBJECT (buildable);
*parser = focus_chain_parser;
*data = parser_data;
return TRUE;
}
return FALSE; return FALSE;
} }
@ -778,6 +847,48 @@ gtk_container_buildable_custom_tag_end (GtkBuildable *buildable,
child, tagname, data); child, tagname, data);
} }
static void
gtk_container_buildable_custom_finished (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *tagname,
gpointer data)
{
if (strcmp (tagname, "focus-chain") == 0)
{
FocusChainData *parser_data = (FocusChainData*)data;
GSList *l;
GList *chain;
GObject *object;
chain = NULL;
for (l = parser_data->items; l; l = l->next)
{
object = gtk_builder_get_object (builder, l->data);
if (!object)
{
g_warning ("Unknown object %s specified in focus-chain for %s",
(const gchar*)l->data,
gtk_buildable_get_name (GTK_BUILDABLE (parser_data->object)));
continue;
}
chain = g_list_prepend (chain, object);
}
gtk_container_set_focus_chain (GTK_CONTAINER (parser_data->object), chain);
g_list_free (chain);
g_slist_free_full (parser_data->items, g_free);
g_slice_free (FocusChainData, parser_data);
return;
}
if (parent_buildable_iface->custom_finished)
parent_buildable_iface->custom_finished (buildable, builder,
child, tagname, data);
}
/** /**
* gtk_container_child_type: * gtk_container_child_type:
* @container: a #GtkContainer * @container: a #GtkContainer