From 873634354406d76dedd5878c727c9f731263f2cc Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 30 Oct 2019 18:03:23 +0100 Subject: [PATCH] columnview: Add listitems for the columns They are not aligned in columns yet, but they do exist. --- docs/reference/gtk/meson.build | 2 + gtk/gtkcolumnlistitemfactory.c | 178 ++++++++++++++++++++++++++ gtk/gtkcolumnlistitemfactoryprivate.h | 62 +++++++++ gtk/gtkcolumnview.c | 70 ++++++++-- gtk/gtkcolumnviewprivate.h | 35 +++++ gtk/gtklistitem.c | 1 + gtk/meson.build | 1 + testsuite/gtk/defaultvalue.c | 4 + 8 files changed, 345 insertions(+), 8 deletions(-) create mode 100644 gtk/gtkcolumnlistitemfactory.c create mode 100644 gtk/gtkcolumnlistitemfactoryprivate.h create mode 100644 gtk/gtkcolumnviewprivate.h diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index 7c6ae15dad..dcc887b87a 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -24,7 +24,9 @@ private_headers = [ 'gtkcolorplaneprivate.h', 'gtkcolorscaleprivate.h', 'gtkcolorswatchprivate.h', + 'gtkcolumnlistitemfactoryprivate.h', 'gtkcolumnviewcolumnprivate.h', + 'gtkcolumnviewprivate.h', 'gtkcomboboxprivate.h', 'gtkconstraintexpressionprivate.h', 'gtkconstraintguideprivate.h', diff --git a/gtk/gtkcolumnlistitemfactory.c b/gtk/gtkcolumnlistitemfactory.c new file mode 100644 index 0000000000..6f14363c06 --- /dev/null +++ b/gtk/gtkcolumnlistitemfactory.c @@ -0,0 +1,178 @@ +/* + * Copyright © 2019 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtkcolumnlistitemfactoryprivate.h" + +#include "gtkboxlayout.h" +#include "gtkcolumnviewcolumnprivate.h" +#include "gtklistitemfactoryprivate.h" +#include "gtklistitemprivate.h" + +struct _GtkColumnListItemFactory +{ + GtkListItemFactory parent_instance; + + GtkColumnView *view; /* no reference, the view references us */ +}; + +struct _GtkColumnListItemFactoryClass +{ + GtkListItemFactoryClass parent_class; +}; + +G_DEFINE_TYPE (GtkColumnListItemFactory, gtk_column_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY) + +static GtkListItemWidget * +get_nth_child (GtkListItemWidget *parent, + guint pos) +{ + GtkWidget *child; + guint i; + + child = gtk_widget_get_first_child (GTK_WIDGET (parent)); + for (i = 1; i < pos && child; i++) + child = gtk_widget_get_next_sibling (child); + + return GTK_LIST_ITEM_WIDGET (child); +} + +static void +gtk_column_list_item_factory_setup (GtkListItemFactory *factory, + GtkListItemWidget *widget, + GtkListItem *list_item) +{ + GtkColumnListItemFactory *self = GTK_COLUMN_LIST_ITEM_FACTORY (factory); + GListModel *columns; + guint i; + + gtk_widget_set_layout_manager (GTK_WIDGET (list_item->owner), + gtk_box_layout_new (GTK_ORIENTATION_HORIZONTAL)); + + GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, widget, list_item); + + columns = gtk_column_view_get_columns (self->view); + + for (i = 0; i < g_list_model_get_n_items (columns); i++) + { + GtkColumnViewColumn *column = g_list_model_get_item (columns, i); + + gtk_column_list_item_factory_add_column (self, + list_item->owner, + column, + FALSE); + } +} + +static void +gtk_column_list_item_factory_teardown (GtkListItemFactory *factory, + GtkListItemWidget *widget, + GtkListItem *list_item) +{ + GtkWidget *child; + + GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->teardown (factory, widget, list_item); + + for (child = gtk_widget_get_first_child (GTK_WIDGET (list_item->owner)); + child; + child = gtk_widget_get_first_child (GTK_WIDGET (list_item->owner))) + { + gtk_list_item_widget_remove_child (list_item->owner, child); + } +} + +static void +gtk_column_list_item_factory_update (GtkListItemFactory *factory, + GtkListItemWidget *widget, + GtkListItem *list_item, + guint position, + gpointer item, + gboolean selected) +{ + GtkWidget *child; + + GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected); + + for (child = gtk_widget_get_first_child (GTK_WIDGET (widget)); + child; + child = gtk_widget_get_next_sibling (child)) + { + gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (child), position, item, selected); + } +} + +static void +gtk_column_list_item_factory_class_init (GtkColumnListItemFactoryClass *klass) +{ + GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass); + + factory_class->setup = gtk_column_list_item_factory_setup; + factory_class->teardown = gtk_column_list_item_factory_teardown; + factory_class->update = gtk_column_list_item_factory_update; +} + +static void +gtk_column_list_item_factory_init (GtkColumnListItemFactory *self) +{ +} + +GtkColumnListItemFactory * +gtk_column_list_item_factory_new (GtkColumnView *view) +{ + GtkColumnListItemFactory *result; + + result = g_object_new (GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, NULL); + + result->view = view; + + return result; +} + +void +gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory, + GtkListItemWidget *list_item, + GtkColumnViewColumn *column, + gboolean check_bind) +{ + GtkListItemFactory *column_factory; + GtkWidget *cell; + + column_factory = gtk_column_view_column_get_factory (column); + + cell = gtk_list_item_widget_new (column_factory, "cell"); + gtk_list_item_widget_add_child (GTK_LIST_ITEM_WIDGET (list_item), GTK_WIDGET (cell)); + gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (cell), + gtk_list_item_widget_get_position (list_item), + gtk_list_item_widget_get_item (list_item), + gtk_list_item_widget_get_selected (list_item)); +} + +void +gtk_column_list_item_factory_remove_column (GtkColumnListItemFactory *factory, + GtkListItemWidget *list_item, + guint col_pos, + GtkColumnViewColumn *column) +{ + GtkListItemWidget *cell; + + cell = get_nth_child (list_item, col_pos); + + gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (list_item), GTK_WIDGET (cell)); +} diff --git a/gtk/gtkcolumnlistitemfactoryprivate.h b/gtk/gtkcolumnlistitemfactoryprivate.h new file mode 100644 index 0000000000..557ca11875 --- /dev/null +++ b/gtk/gtkcolumnlistitemfactoryprivate.h @@ -0,0 +1,62 @@ +/* + * Copyright © 2019 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_COLUMN_LIST_ITEM_FACTORY_H__ +#define __GTK_COLUMN_LIST_ITEM_FACTORY_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_COLUMN_LIST_ITEM_FACTORY (gtk_column_list_item_factory_get_type ()) +#define GTK_COLUMN_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactory)) +#define GTK_COLUMN_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactoryClass)) +#define GTK_IS_COLUMN_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY)) +#define GTK_IS_COLUMN_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY)) +#define GTK_COLUMN_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactoryClass)) + +/** + * GtkColumnListItemFactory: + * + * The object for the #GtkColumnListItemFactory. + **/ +typedef struct _GtkColumnListItemFactory GtkColumnListItemFactory; +typedef struct _GtkColumnListItemFactoryClass GtkColumnListItemFactoryClass; + + +GType gtk_column_list_item_factory_get_type (void) G_GNUC_CONST; + +GtkColumnListItemFactory * + gtk_column_list_item_factory_new (GtkColumnView *view); + +void gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory, + GtkListItemWidget *list_item, + GtkColumnViewColumn *column, + gboolean check_bind); +void gtk_column_list_item_factory_remove_column + (GtkColumnListItemFactory *factory, + GtkListItemWidget *list_item, + guint col_pos, + GtkColumnViewColumn *column); + + +G_END_DECLS + +#endif /* __GTK_COLUMN_LIST_ITEM_FACTORY_H__ */ diff --git a/gtk/gtkcolumnview.c b/gtk/gtkcolumnview.c index 14dec39135..0b96f295e3 100644 --- a/gtk/gtkcolumnview.c +++ b/gtk/gtkcolumnview.c @@ -19,10 +19,11 @@ #include "config.h" -#include "gtkcolumnview.h" +#include "gtkcolumnviewprivate.h" #include "gtkboxlayout.h" #include "gtkbuildable.h" +#include "gtkcolumnlistitemfactoryprivate.h" #include "gtkcolumnviewcolumnprivate.h" #include "gtkintl.h" #include "gtklistview.h" @@ -47,6 +48,7 @@ struct _GtkColumnView GListStore *columns; GtkListView *listview; + GtkColumnListItemFactory *factory; }; struct _GtkColumnViewClass @@ -102,12 +104,37 @@ gtk_column_view_buildable_interface_init (GtkBuildableIface *iface) iface->add_child = gtk_column_view_buildable_add_child; } + G_DEFINE_TYPE_WITH_CODE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_column_view_buildable_interface_init)) static GParamSpec *properties[N_PROPS] = { NULL, }; static guint signals[LAST_SIGNAL] = { 0 }; +/* For now we do the iter with the children. We might switch that + * to use the item manager or track children directly in the factory + * later (depending on how code changes), so having this abstraction makes sense. + */ +GtkColumnViewIter * +gtk_column_view_iter_init (GtkColumnView *self) +{ + return (GtkColumnViewIter *) gtk_widget_get_first_child (GTK_WIDGET (self->listview)); +} + +GtkWidget * +gtk_column_view_iter_get_widget (GtkColumnView *self, + GtkColumnViewIter *iter) +{ + return GTK_WIDGET (iter); +} + +GtkColumnViewIter * +gtk_column_view_iter_next (GtkColumnView *self, + GtkColumnViewIter *iter) +{ + return (GtkColumnViewIter *) gtk_widget_get_next_sibling (GTK_WIDGET (iter)); +} + static void gtk_column_view_activate_cb (GtkListView *listview, guint pos, @@ -129,6 +156,7 @@ gtk_column_view_dispose (GObject *object) } g_clear_pointer ((GtkWidget **) &self->listview, gtk_widget_unparent); + g_clear_object (&self->factory); G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object); } @@ -277,7 +305,11 @@ gtk_column_view_init (GtkColumnView *self) { self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN); - self->listview = GTK_LIST_VIEW (gtk_list_view_new ()); + self->factory = gtk_column_list_item_factory_new (self); + self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory ( + GTK_LIST_ITEM_FACTORY (g_object_ref (self->factory)))); + gtk_widget_set_hexpand (GTK_WIDGET (self->listview), TRUE); + gtk_widget_set_vexpand (GTK_WIDGET (self->listview), TRUE); g_signal_connect (self->listview, "activate", G_CALLBACK (gtk_column_view_activate_cb), self); gtk_widget_set_parent (GTK_WIDGET (self->listview), GTK_WIDGET (self)); } @@ -409,12 +441,24 @@ void gtk_column_view_append_column (GtkColumnView *self, GtkColumnViewColumn *column) { + GtkColumnViewIter *iter; + g_return_if_fail (GTK_IS_COLUMN_VIEW (self)); g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column)); g_return_if_fail (gtk_column_view_column_get_column_view (column) == NULL); gtk_column_view_column_set_column_view (column, self); g_list_store_append (self->columns, column); + + for (iter = gtk_column_view_iter_init (self); + iter != NULL; + iter = gtk_column_view_iter_next (self, iter)) + { + gtk_column_list_item_factory_add_column (self->factory, + GTK_LIST_ITEM_WIDGET (gtk_column_view_iter_get_widget (self, iter)), + column, + TRUE); + } } /** @@ -428,6 +472,7 @@ void gtk_column_view_remove_column (GtkColumnView *self, GtkColumnViewColumn *column) { + GtkColumnViewIter *iter; guint i; g_return_if_fail (GTK_IS_COLUMN_VIEW (self)); @@ -440,13 +485,22 @@ gtk_column_view_remove_column (GtkColumnView *self, g_object_unref (item); if (item == column) - { - gtk_column_view_column_set_column_view (column, NULL); - g_list_store_remove (self->columns, i); - return; - } + break; } - g_assert_not_reached (); + g_assert (i < g_list_model_get_n_items (G_LIST_MODEL (self->columns))); + + for (iter = gtk_column_view_iter_init (self); + iter != NULL; + iter = gtk_column_view_iter_next (self, iter)) + { + gtk_column_list_item_factory_remove_column (self->factory, + GTK_LIST_ITEM_WIDGET (gtk_column_view_iter_get_widget (self, iter)), + i, + column); + } + + gtk_column_view_column_set_column_view (column, NULL); + g_list_store_remove (self->columns, i); } diff --git a/gtk/gtkcolumnviewprivate.h b/gtk/gtkcolumnviewprivate.h new file mode 100644 index 0000000000..a4e0f17885 --- /dev/null +++ b/gtk/gtkcolumnviewprivate.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2019 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_COLUMN_VIEW_PRIVATE_H__ +#define __GTK_COLUMN_VIEW_PRIVATE_H__ + +#include "gtk/gtkcolumnview.h" + +/* This is really just a GtkListItemManagerItem for now, but + * proper layering ftw */ +typedef struct _GtkColumnViewIter GtkColumnViewIter; + +GtkColumnViewIter * gtk_column_view_iter_init (GtkColumnView *self); +GtkWidget * gtk_column_view_iter_get_widget (GtkColumnView *self, + GtkColumnViewIter *iter); +GtkColumnViewIter * gtk_column_view_iter_next (GtkColumnView *self, + GtkColumnViewIter *iter); + +#endif /* __GTK_COLUMN_VIEW_PRIVATE_H__ */ diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c index 01db2cdac4..f9341ab60e 100644 --- a/gtk/gtklistitem.c +++ b/gtk/gtklistitem.c @@ -319,6 +319,7 @@ gtk_list_item_set_child (GtkListItem *self, { g_object_ref_sink (child); self->child = child; + if (self->owner) gtk_list_item_widget_add_child (self->owner, child); } diff --git a/gtk/meson.build b/gtk/meson.build index 785e63699f..20d8e71099 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -193,6 +193,7 @@ gtk_public_sources = files([ 'gtkcolorchooserdialog.c', 'gtkcolorchooserwidget.c', 'gtkcolorutils.c', + 'gtkcolumnlistitemfactory.c', 'gtkcolumnview.c', 'gtkcolumnviewcolumn.c', 'gtkcombobox.c', diff --git a/testsuite/gtk/defaultvalue.c b/testsuite/gtk/defaultvalue.c index 9fe3dab21b..88b60463d5 100644 --- a/testsuite/gtk/defaultvalue.c +++ b/testsuite/gtk/defaultvalue.c @@ -239,6 +239,10 @@ test_type (gconstpointer data) strcmp (pspec->name, "rgba") == 0)) continue; + if (g_type_is_a (type, GTK_TYPE_COLUMN_VIEW) && + strcmp (pspec->name, "columns") == 0) + continue; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS if (g_type_is_a (type, GTK_TYPE_COMBO_BOX) &&