listitemfactory: Make this callback-based

This way, we no longer prescribe the use of either GtkListItem or
GtkListItemWidget.

This means we can use it in other places, such as for custom section
header objects or with my Canvas ideas.
This commit is contained in:
Benjamin Otte 2022-06-28 16:34:24 +02:00
parent c00b234440
commit 57f2b5d2e6
7 changed files with 254 additions and 185 deletions

View File

@ -85,22 +85,24 @@ static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item)
GObject *item,
gboolean bind,
GFunc func,
gpointer data)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (factory);
GtkBuilder *builder;
GError *error = NULL;
GTK_LIST_ITEM_FACTORY_CLASS (gtk_builder_list_item_factory_parent_class)->setup (factory, widget, list_item);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_builder_list_item_factory_parent_class)->setup (factory, item, bind, func, data);
builder = gtk_builder_new ();
gtk_builder_set_current_object (builder, G_OBJECT (list_item));
gtk_builder_set_current_object (builder, item);
if (self->scope)
gtk_builder_set_scope (builder, self->scope);
if (!gtk_builder_extend_with_template (builder, G_OBJECT (list_item), G_OBJECT_TYPE (list_item),
if (!gtk_builder_extend_with_template (builder, G_OBJECT (item), G_OBJECT_TYPE (item),
(const char *)g_bytes_get_data (self->data, NULL),
g_bytes_get_size (self->data),
&error))

View File

@ -43,10 +43,13 @@ G_DEFINE_TYPE (GtkColumnListItemFactory, gtk_column_list_item_factory, GTK_TYPE_
static void
gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item)
GObject *item,
gboolean bind,
GFunc func,
gpointer data)
{
GtkColumnListItemFactory *self = GTK_COLUMN_LIST_ITEM_FACTORY (factory);
GtkListItemWidget *widget = data;
GListModel *columns;
guint i;
@ -54,7 +57,7 @@ gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
gtk_widget_set_layout_manager (GTK_WIDGET (widget),
gtk_column_view_layout_new (self->view));
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, widget, list_item);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, item, bind, func, data);
columns = gtk_column_view_get_columns (self->view);
@ -63,7 +66,7 @@ gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
GtkColumnViewColumn *column = g_list_model_get_item (columns, i);
gtk_column_list_item_factory_add_column (self,
list_item->owner,
widget,
column,
FALSE);
@ -73,12 +76,15 @@ gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
static void
gtk_column_list_item_factory_teardown (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item)
GObject *item,
gboolean unbind,
GFunc func,
gpointer data)
{
GtkListItemWidget *widget = data;
GtkWidget *child;
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->teardown (factory, widget, list_item);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->teardown (factory, item, unbind, func, data);
while ((child = gtk_widget_get_first_child (GTK_WIDGET (widget))))
{
@ -88,21 +94,25 @@ gtk_column_list_item_factory_teardown (GtkListItemFactory *factory,
static void
gtk_column_list_item_factory_update (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
GObject *item,
gboolean unbind,
gboolean bind,
GFunc func,
gpointer data)
{
GtkListItem *list_item = GTK_LIST_ITEM (item);
GtkWidget *child;
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->update (factory, item, unbind, bind, func, data);
for (child = gtk_widget_get_first_child (GTK_WIDGET (widget));
for (child = gtk_widget_get_first_child (GTK_WIDGET (list_item->owner));
child;
child = gtk_widget_get_next_sibling (child))
{
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (child), position, item, selected);
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (child),
gtk_list_item_get_position (list_item),
gtk_list_item_get_item (list_item),
gtk_list_item_get_selected (list_item));
}
}

View File

@ -80,31 +80,36 @@ G_DEFINE_TYPE (GtkListItemFactory, gtk_list_item_factory, G_TYPE_OBJECT)
static void
gtk_list_item_factory_default_setup (GtkListItemFactory *self,
GtkListItemWidget *widget,
GtkListItem *list_item)
GObject *item,
gboolean bind,
GFunc func,
gpointer data)
{
gtk_list_item_widget_default_setup (widget, list_item);
if (func)
func (item, data);
}
static void
gtk_list_item_factory_default_teardown (GtkListItemFactory *self,
GtkListItemWidget *widget,
GtkListItem *list_item)
GObject *item,
gboolean unbind,
GFunc func,
gpointer data)
{
gtk_list_item_widget_default_teardown (widget, list_item);
gtk_list_item_set_child (list_item, NULL);
if (func)
func (item, data);
}
static void
gtk_list_item_factory_default_update (GtkListItemFactory *self,
GtkListItemWidget *widget,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
GObject *item,
gboolean unbind,
gboolean bind,
GFunc func,
gpointer data)
{
gtk_list_item_widget_default_update (widget, list_item, position, item, selected);
if (func)
func (item, data);
}
static void
@ -122,45 +127,38 @@ gtk_list_item_factory_init (GtkListItemFactory *self)
void
gtk_list_item_factory_setup (GtkListItemFactory *self,
GtkListItemWidget *widget)
GObject *item,
gboolean bind,
GFunc func,
gpointer data)
{
GtkListItem *list_item;
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
list_item = gtk_list_item_new ();
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->setup (self, widget, list_item);
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->setup (self, item, bind, func, data);
}
void
gtk_list_item_factory_teardown (GtkListItemFactory *self,
GtkListItemWidget *widget)
GObject *item,
gboolean unbind,
GFunc func,
gpointer data)
{
GtkListItem *list_item;
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
list_item = gtk_list_item_widget_get_list_item (widget);
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->teardown (self, widget, list_item);
g_object_unref (list_item);
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->teardown (self, item, unbind, func, data);
}
void
gtk_list_item_factory_update (GtkListItemFactory *self,
GtkListItemWidget *widget,
guint position,
gpointer item,
gboolean selected)
GObject *item,
gboolean unbind,
gboolean bind,
GFunc func,
gpointer data)
{
GtkListItem *list_item;
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
g_return_if_fail (GTK_IS_LIST_ITEM_WIDGET (widget));
g_return_if_fail (G_IS_OBJECT (item));
list_item = gtk_list_item_widget_get_list_item (widget);
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->update (self, widget, list_item, position, item, selected);
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->update (self, item, unbind, bind, func, data);
}

View File

@ -37,33 +37,44 @@ struct _GtkListItemFactoryClass
/* setup @list_item so it can be bound */
void (* setup) (GtkListItemFactory *self,
GtkListItemWidget *widget,
GtkListItem *list_item);
GObject *item,
gboolean bind,
GFunc func,
gpointer data);
/* undo the effects of GtkListItemFactoryClass::setup() */
void (* teardown) (GtkListItemFactory *self,
GtkListItemWidget *widget,
GtkListItem *list_item);
GObject *item,
gboolean unbind,
GFunc func,
gpointer data);
/* Update properties on @list_item to the given @item, which is in @position and @selected state.
* One or more of those properties might be unchanged. */
void (* update) (GtkListItemFactory *self,
GtkListItemWidget *widget,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected);
GObject *item,
gboolean unbind,
gboolean bind,
GFunc func,
gpointer data);
};
void gtk_list_item_factory_setup (GtkListItemFactory *self,
GtkListItemWidget *widget);
GObject *item,
gboolean bind,
GFunc func,
gpointer data);
void gtk_list_item_factory_teardown (GtkListItemFactory *self,
GtkListItemWidget *widget);
GObject *item,
gboolean unbind,
GFunc func,
gpointer data);
void gtk_list_item_factory_update (GtkListItemFactory *self,
GtkListItemWidget *widget,
guint position,
gpointer item,
gboolean selected);
GObject *item,
gboolean unbind,
gboolean bind,
GFunc func,
gpointer data);
G_END_DECLS

View File

@ -140,6 +140,85 @@ gtk_list_item_widget_grab_focus (GtkWidget *widget)
return GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget);
}
static void
gtk_list_item_widget_setup_func (gpointer object,
gpointer data)
{
GtkListItemWidget *self = data;
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
GtkListItem *list_item = object;
priv->list_item = list_item;
list_item->owner = self;
if (list_item->child)
gtk_list_item_widget_add_child (self, list_item->child);
gtk_list_item_widget_set_activatable (self, list_item->activatable);
gtk_list_item_do_notify (list_item,
priv->item != NULL,
priv->position != GTK_INVALID_LIST_POSITION,
priv->selected);
}
static void
gtk_list_item_widget_setup_factory (GtkListItemWidget *self)
{
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
GtkListItem *list_item;
list_item = gtk_list_item_new ();
gtk_list_item_factory_setup (priv->factory,
G_OBJECT (list_item),
priv->item != NULL,
gtk_list_item_widget_setup_func,
self);
g_assert (priv->list_item == list_item);
}
static void
gtk_list_item_widget_teardown_func (gpointer object,
gpointer data)
{
GtkListItemWidget *self = data;
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
GtkListItem *list_item = object;
g_assert (priv->list_item == list_item);
priv->list_item = NULL;
list_item->owner = NULL;
if (list_item->child)
gtk_list_item_widget_remove_child (self, list_item->child);
gtk_list_item_widget_set_activatable (self, FALSE);
gtk_list_item_do_notify (list_item,
priv->item != NULL,
priv->position != GTK_INVALID_LIST_POSITION,
priv->selected);
gtk_list_item_set_child (list_item, NULL);
}
static void
gtk_list_item_widget_teardown_factory (GtkListItemWidget *self)
{
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
gtk_list_item_factory_teardown (priv->factory,
G_OBJECT (priv->list_item),
priv->item != NULL,
gtk_list_item_widget_teardown_func,
self);
g_assert (priv->list_item == NULL);
}
static void
gtk_list_item_widget_root (GtkWidget *widget)
{
@ -149,7 +228,7 @@ gtk_list_item_widget_root (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->root (widget);
if (priv->factory)
gtk_list_item_factory_setup (priv->factory, self);
gtk_list_item_widget_setup_factory (self);
}
static void
@ -161,7 +240,7 @@ gtk_list_item_widget_unroot (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->unroot (widget);
if (priv->list_item)
gtk_list_item_factory_teardown (priv->factory, self);
gtk_list_item_widget_teardown_factory (self);
}
static void
@ -474,6 +553,45 @@ gtk_list_item_widget_new (GtkListItemFactory *factory,
NULL);
}
typedef struct {
GtkListItemWidget *widget;
guint position;
gpointer item;
gboolean selected;
} GtkListItemWidgetUpdate;
static void
gtk_list_item_widget_update_func (gpointer object,
gpointer data)
{
GtkListItemWidgetUpdate *update = data;
GtkListItem *list_item = object;
/* Track notify manually instead of freeze/thaw_notify for performance reasons. */
gboolean notify_item = FALSE, notify_position = FALSE, notify_selected = FALSE;
GtkListItemWidget *self = update->widget;
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
/* FIXME: It's kinda evil to notify external objects from here... */
if (g_set_object (&priv->item, update->item))
notify_item = TRUE;
if (priv->position != update->position)
{
priv->position = update->position;
notify_position = TRUE;
}
if (priv->selected != update->selected)
{
priv->selected = update->selected;
notify_selected = TRUE;
}
if (list_item)
gtk_list_item_do_notify (list_item, notify_item, notify_position, notify_selected);
}
void
gtk_list_item_widget_update (GtkListItemWidget *self,
guint position,
@ -481,14 +599,24 @@ gtk_list_item_widget_update (GtkListItemWidget *self,
gboolean selected)
{
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
GtkListItemWidgetUpdate update = { self, position, item, selected };
gboolean was_selected;
was_selected = priv->selected;
if (priv->list_item)
gtk_list_item_factory_update (priv->factory, self, position, item, selected);
{
gtk_list_item_factory_update (priv->factory,
G_OBJECT (priv->list_item),
priv->item != NULL,
item != NULL,
gtk_list_item_widget_update_func,
&update);
}
else
gtk_list_item_widget_default_update (self, NULL, position, item, selected);
{
gtk_list_item_widget_update_func (NULL, &update);
}
/* don't look at selected variable, it's not reentrancy safe */
if (was_selected != priv->selected)
@ -504,80 +632,6 @@ gtk_list_item_widget_update (GtkListItemWidget *self,
}
}
void
gtk_list_item_widget_default_setup (GtkListItemWidget *self,
GtkListItem *list_item)
{
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
priv->list_item = list_item;
list_item->owner = self;
if (list_item->child)
gtk_list_item_widget_add_child (self, list_item->child);
gtk_list_item_widget_set_activatable (self, list_item->activatable);
gtk_list_item_do_notify (list_item,
priv->item != NULL,
priv->position != GTK_INVALID_LIST_POSITION,
priv->selected);
}
void
gtk_list_item_widget_default_teardown (GtkListItemWidget *self,
GtkListItem *list_item)
{
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
g_assert (priv->list_item == list_item);
priv->list_item = NULL;
list_item->owner = NULL;
if (list_item->child)
gtk_list_item_widget_remove_child (self, list_item->child);
gtk_list_item_widget_set_activatable (self, FALSE);
gtk_list_item_do_notify (list_item,
priv->item != NULL,
priv->position != GTK_INVALID_LIST_POSITION,
priv->selected);
}
void
gtk_list_item_widget_default_update (GtkListItemWidget *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
{
/* Track notify manually instead of freeze/thaw_notify for performance reasons. */
gboolean notify_item = FALSE, notify_position = FALSE, notify_selected = FALSE;
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
/* FIXME: It's kinda evil to notify external objects from here... */
if (g_set_object (&priv->item, item))
notify_item = TRUE;
if (priv->position != position)
{
priv->position = position;
notify_position = TRUE;
}
if (priv->selected != selected)
{
priv->selected = selected;
notify_selected = TRUE;
}
if (list_item)
gtk_list_item_do_notify (list_item, notify_item, notify_position, notify_selected);
}
void
gtk_list_item_widget_set_factory (GtkListItemWidget *self,
GtkListItemFactory *factory)
@ -590,7 +644,7 @@ gtk_list_item_widget_set_factory (GtkListItemWidget *self,
if (priv->factory)
{
if (priv->list_item)
gtk_list_item_factory_teardown (factory, self);
gtk_list_item_widget_teardown_factory (self);
g_clear_object (&priv->factory);
}
@ -599,7 +653,7 @@ gtk_list_item_widget_set_factory (GtkListItemWidget *self,
priv->factory = g_object_ref (factory);
if (gtk_widget_get_root (GTK_WIDGET (self)))
gtk_list_item_factory_setup (factory, self);
gtk_list_item_widget_setup_factory (self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);

View File

@ -59,16 +59,6 @@ void gtk_list_item_widget_update (GtkListItemWidg
gboolean selected);
GtkListItem * gtk_list_item_widget_get_list_item (GtkListItemWidget *self);
void gtk_list_item_widget_default_setup (GtkListItemWidget *self,
GtkListItem *list_item);
void gtk_list_item_widget_default_teardown (GtkListItemWidget *self,
GtkListItem *list_item);
void gtk_list_item_widget_default_update (GtkListItemWidget *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected);
void gtk_list_item_widget_set_factory (GtkListItemWidget *self,
GtkListItemFactory *factory);
void gtk_list_item_widget_set_single_click_activate

View File

@ -103,45 +103,49 @@ static guint signals[LAST_SIGNAL] = { 0 };
static void
gtk_signal_list_item_factory_setup (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item)
GObject *item,
gboolean bind,
GFunc func,
gpointer data)
{
g_signal_emit (factory, signals[SETUP], 0, list_item);
g_signal_emit (factory, signals[SETUP], 0, item);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->setup (factory, widget, list_item);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->setup (factory, item, bind, func, data);
if (gtk_list_item_get_item (list_item))
g_signal_emit (factory, signals[BIND], 0, list_item);
if (bind)
g_signal_emit (factory, signals[BIND], 0, item);
}
static void
gtk_signal_list_item_factory_update (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
GObject *item,
gboolean unbind,
gboolean bind,
GFunc func,
gpointer data)
{
if (gtk_list_item_get_item (list_item))
g_signal_emit (factory, signals[UNBIND], 0, list_item);
if (unbind)
g_signal_emit (factory, signals[UNBIND], 0, item);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->update (factory, item, unbind, bind, func, data);
if (item)
g_signal_emit (factory, signals[BIND], 0, list_item);
if (bind)
g_signal_emit (factory, signals[BIND], 0, item);
}
static void
gtk_signal_list_item_factory_teardown (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item)
GObject *item,
gboolean unbind,
GFunc func,
gpointer data)
{
if (gtk_list_item_get_item (list_item))
g_signal_emit (factory, signals[UNBIND], 0, list_item);
if (unbind)
g_signal_emit (factory, signals[UNBIND], 0, item);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->teardown (factory, widget, list_item);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->teardown (factory, item, unbind, func, data);
g_signal_emit (factory, signals[TEARDOWN], 0, list_item);
g_signal_emit (factory, signals[TEARDOWN], 0, item);
}
static void