diff --git a/gtk/gtkcolumnlistitemfactory.c b/gtk/gtkcolumnlistitemfactory.c index 6f14363c06..ac5c3e1766 100644 --- a/gtk/gtkcolumnlistitemfactory.c +++ b/gtk/gtkcolumnlistitemfactory.c @@ -40,20 +40,6 @@ struct _GtkColumnListItemFactoryClass 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, @@ -63,7 +49,8 @@ gtk_column_list_item_factory_setup (GtkListItemFactory *factory, GListModel *columns; guint i; - gtk_widget_set_layout_manager (GTK_WIDGET (list_item->owner), + /* FIXME: evil */ + gtk_widget_set_layout_manager (GTK_WIDGET (widget), gtk_box_layout_new (GTK_ORIENTATION_HORIZONTAL)); GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, widget, list_item); @@ -90,11 +77,9 @@ gtk_column_list_item_factory_teardown (GtkListItemFactory *factory, 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))) + while ((child = gtk_widget_get_first_child (GTK_WIDGET (widget)))) { - gtk_list_item_widget_remove_child (list_item->owner, child); + gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (widget), child); } } @@ -151,28 +136,12 @@ gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory, 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"); + cell = gtk_column_view_cell_new (column); 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/gtkcolumnview.c b/gtk/gtkcolumnview.c index 603bde45bf..932b8d74d1 100644 --- a/gtk/gtkcolumnview.c +++ b/gtk/gtkcolumnview.c @@ -117,30 +117,6 @@ G_DEFINE_TYPE_WITH_CODE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET, 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, @@ -513,24 +489,12 @@ 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); - } } /** @@ -544,7 +508,6 @@ void gtk_column_view_remove_column (GtkColumnView *self, GtkColumnViewColumn *column) { - GtkColumnViewIter *iter; guint i; g_return_if_fail (GTK_IS_COLUMN_VIEW (self)); @@ -560,18 +523,6 @@ gtk_column_view_remove_column (GtkColumnView *self, break; } - 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/gtkcolumnviewcell.c b/gtk/gtkcolumnviewcell.c new file mode 100644 index 0000000000..db3085dbe2 --- /dev/null +++ b/gtk/gtkcolumnviewcell.c @@ -0,0 +1,204 @@ +/* + * 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 "gtkcolumnviewcellprivate.h" + +#include "gtkcolumnviewcolumnprivate.h" +#include "gtkintl.h" +#include "gtklistitemwidgetprivate.h" +#include "gtkwidgetprivate.h" + +struct _GtkColumnViewCell +{ + GtkListItemWidget parent_instance; + + GtkColumnViewColumn *column; + + /* This list isn't sorted - next/prev refer to list elements, not rows in the list */ + GtkColumnViewCell *next_cell; + GtkColumnViewCell *prev_cell; +}; + +struct _GtkColumnViewCellClass +{ + GtkListItemWidgetClass parent_class; +}; + +G_DEFINE_TYPE (GtkColumnViewCell, gtk_column_view_cell, GTK_TYPE_LIST_ITEM_WIDGET) + +void +gtk_column_view_cell_measure_contents (GtkColumnViewCell *self, + int *minimum, + int *natural) +{ + GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self)); + + if (child) + gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural, NULL, NULL); +} + +static void +gtk_column_view_cell_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + gtk_column_view_column_measure (self->column, minimum, natural); + } + else + { + GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self)); + + if (child) + gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline); + } +} + +static void +gtk_column_view_cell_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + GtkWidget *child = gtk_widget_get_first_child (widget); + + if (child) + gtk_widget_allocate (child, width, height, baseline, NULL); +} + +static void +gtk_column_view_cell_root (GtkWidget *widget) +{ + GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget); + + GTK_WIDGET_CLASS (gtk_column_view_cell_parent_class)->root (widget); + + self->next_cell = gtk_column_view_column_get_first_cell (self->column); + if (self->next_cell) + self->next_cell->prev_cell = self; + + gtk_column_view_column_add_cell (self->column, self); +} + +static void +gtk_column_view_cell_unroot (GtkWidget *widget) +{ + GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget); + + gtk_column_view_column_remove_cell (self->column, self); + + if (self->prev_cell) + self->prev_cell->next_cell = self->next_cell; + if (self->next_cell) + self->next_cell->prev_cell = self->prev_cell; + + self->prev_cell = NULL; + self->next_cell = NULL; + + GTK_WIDGET_CLASS (gtk_column_view_cell_parent_class)->unroot (widget); +} + +static void +gtk_column_view_cell_dispose (GObject *object) +{ + GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (object); + + g_clear_object (&self->column); + + G_OBJECT_CLASS (gtk_column_view_cell_parent_class)->dispose (object); +} + +static void +gtk_column_view_cell_class_init (GtkColumnViewCellClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + widget_class->root = gtk_column_view_cell_root; + widget_class->unroot = gtk_column_view_cell_unroot; + widget_class->measure = gtk_column_view_cell_measure; + widget_class->size_allocate = gtk_column_view_cell_size_allocate; + + gobject_class->dispose = gtk_column_view_cell_dispose; + + gtk_widget_class_set_css_name (widget_class, I_("cell")); +} + +static void +gtk_column_view_cell_resize_func (GtkWidget *widget) +{ + GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget); + + if (self->column) + gtk_column_view_column_queue_resize (self->column); +} + +static void +gtk_column_view_cell_init (GtkColumnViewCell *self) +{ + GtkWidget *widget = GTK_WIDGET (self); + + gtk_widget_set_can_focus (widget, FALSE); + /* FIXME: Figure out if settting the manager class to INVALID should work */ + gtk_widget_set_layout_manager (widget, NULL); + widget->priv->resize_func = gtk_column_view_cell_resize_func; +} + +GtkWidget * +gtk_column_view_cell_new (GtkColumnViewColumn *column) +{ + GtkColumnViewCell *cell; + + cell = g_object_new (GTK_TYPE_COLUMN_VIEW_CELL, + "factory", gtk_column_view_column_get_factory (column), + NULL); + + cell->column = g_object_ref (column); + + return GTK_WIDGET (cell); +} + +void +gtk_column_view_cell_remove (GtkColumnViewCell *self) +{ + GtkWidget *widget = GTK_WIDGET (self); + + gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (gtk_widget_get_parent (widget)), widget); +} + +GtkColumnViewCell * +gtk_column_view_cell_get_next (GtkColumnViewCell *self) +{ + return self->next_cell; +} + +GtkColumnViewCell * +gtk_column_view_cell_get_prev (GtkColumnViewCell *self) +{ + return self->prev_cell; +} diff --git a/gtk/gtkcolumnviewcellprivate.h b/gtk/gtkcolumnviewcellprivate.h new file mode 100644 index 0000000000..1ebdc710f4 --- /dev/null +++ b/gtk/gtkcolumnviewcellprivate.h @@ -0,0 +1,52 @@ +/* + * 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_CELL_PRIVATE_H__ +#define __GTK_COLUMN_VIEW_CELL_PRIVATE_H__ + +#include "gtkcolumnviewcolumn.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_COLUMN_VIEW_CELL (gtk_column_view_cell_get_type ()) +#define GTK_COLUMN_VIEW_CELL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCell)) +#define GTK_COLUMN_VIEW_CELL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCellClass)) +#define GTK_IS_COLUMN_VIEW_CELL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_CELL)) +#define GTK_IS_COLUMN_VIEW_CELL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_CELL)) +#define GTK_COLUMN_VIEW_CELL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCellClass)) + +typedef struct _GtkColumnViewCell GtkColumnViewCell; +typedef struct _GtkColumnViewCellClass GtkColumnViewCellClass; + +GType gtk_column_view_cell_get_type (void) G_GNUC_CONST; + +GtkWidget * gtk_column_view_cell_new (GtkColumnViewColumn *column); + +void gtk_column_view_cell_remove (GtkColumnViewCell *self); + +GtkColumnViewCell * gtk_column_view_cell_get_next (GtkColumnViewCell *self); +GtkColumnViewCell * gtk_column_view_cell_get_prev (GtkColumnViewCell *self); + +void gtk_column_view_cell_measure_contents (GtkColumnViewCell *self, + int *minimum, + int *natural); + +G_END_DECLS + +#endif /* __GTK_COLUMN_VIEW_CELL_PRIVATE_H__ */ diff --git a/gtk/gtkcolumnviewcolumn.c b/gtk/gtkcolumnviewcolumn.c index 444cb54cb7..03f8d52fa5 100644 --- a/gtk/gtkcolumnviewcolumn.c +++ b/gtk/gtkcolumnviewcolumn.c @@ -23,10 +23,11 @@ #include "gtkintl.h" #include "gtklistbaseprivate.h" -#include "gtklistitemmanagerprivate.h" +#include "gtklistitemwidgetprivate.h" #include "gtkmain.h" #include "gtkprivate.h" #include "gtkrbtreeprivate.h" +#include "gtksizegroup.h" #include "gtkstylecontext.h" #include "gtkwidgetprivate.h" @@ -48,6 +49,12 @@ struct _GtkColumnViewColumn /* data for the view */ GtkColumnView *view; + + int minimum_size_request; + int natural_size_request; + + /* This list isn't sorted - this is just caching for performance */ + GtkColumnViewCell *first_cell; /* no reference, just caching */ }; struct _GtkColumnViewColumnClass @@ -75,6 +82,8 @@ gtk_column_view_column_dispose (GObject *object) GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object); g_assert (self->view == NULL); /* would hold a ref otherwise */ + g_assert (self->first_cell == NULL); /* no view = no children */ + g_clear_object (&self->factory); g_clear_pointer (&self->title, g_free); @@ -184,6 +193,8 @@ gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass) static void gtk_column_view_column_init (GtkColumnViewColumn *self) { + self->minimum_size_request = -1; + self->natural_size_request = -1; } /** @@ -241,6 +252,122 @@ gtk_column_view_column_new_with_factory (const char *title, return result; } +GtkColumnViewCell * +gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self) +{ + return self->first_cell; +} + +void +gtk_column_view_column_add_cell (GtkColumnViewColumn *self, + GtkColumnViewCell *cell) +{ + self->first_cell = cell; + + gtk_column_view_column_queue_resize (self); +} + +void +gtk_column_view_column_remove_cell (GtkColumnViewColumn *self, + GtkColumnViewCell *cell) +{ + if (cell == self->first_cell) + self->first_cell = gtk_column_view_cell_get_next (cell); + + gtk_column_view_column_queue_resize (self); + gtk_widget_queue_resize (GTK_WIDGET (cell)); +} + +void +gtk_column_view_column_queue_resize (GtkColumnViewColumn *self) +{ + GtkColumnViewCell *cell; + + if (self->minimum_size_request < 0) + return; + + self->minimum_size_request = -1; + self->natural_size_request = -1; + + for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell)) + { + gtk_widget_queue_resize (GTK_WIDGET (cell)); + } +} + +void +gtk_column_view_column_measure (GtkColumnViewColumn *self, + int *minimum, + int *natural) +{ + if (self->minimum_size_request < 0) + { + GtkColumnViewCell *cell; + int min, nat, cell_min, cell_nat; + + min = 0; + nat = 0; + + for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell)) + { + gtk_column_view_cell_measure_contents (cell, &cell_min, &cell_nat); + + min = MAX (min, cell_min); + nat = MAX (nat, cell_nat); + } + + self->minimum_size_request = min; + self->natural_size_request = nat; + } + + *minimum = self->minimum_size_request; + *natural = self->natural_size_request; +} + +static void +gtk_column_view_column_create_cells (GtkColumnViewColumn *self) +{ + GtkWidget *row; + + if (self->first_cell) + return; + + for (row = gtk_widget_get_first_child (GTK_WIDGET (self->view)); + row != NULL; + row = gtk_widget_get_next_sibling (row)) + { + GtkListItemWidget *list_item; + GtkWidget *cell; + + if (!gtk_widget_get_root (row)) + continue; + + list_item = GTK_LIST_ITEM_WIDGET (row); + cell = gtk_column_view_cell_new (self); + gtk_list_item_widget_add_child (list_item, 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)); + } +} + +static void +gtk_column_view_column_remove_cells (GtkColumnViewColumn *self) +{ + while (self->first_cell) + gtk_column_view_cell_remove (self->first_cell); +} + +static void +gtk_column_view_column_ensure_cells (GtkColumnViewColumn *self) +{ + if (self->view && gtk_widget_get_root (GTK_WIDGET (self->view))) + gtk_column_view_column_create_cells (self); + else + gtk_column_view_column_remove_cells (self); +} + /** * gtk_column_view_column_get_column_view: * @self: a #GtkColumnViewColumn @@ -268,6 +395,9 @@ gtk_column_view_column_set_column_view (GtkColumnViewColumn *self, self->view = view; + if (view) + gtk_column_view_column_ensure_cells (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLUMN_VIEW]); } @@ -348,3 +478,4 @@ gtk_column_view_column_get_title (GtkColumnViewColumn *self) return self->title; } + diff --git a/gtk/gtkcolumnviewcolumnprivate.h b/gtk/gtkcolumnviewcolumnprivate.h index fcdcdaaf1a..fc0fe07c78 100644 --- a/gtk/gtkcolumnviewcolumnprivate.h +++ b/gtk/gtkcolumnviewcolumnprivate.h @@ -22,8 +22,20 @@ #include "gtk/gtkcolumnviewcolumn.h" +#include "gtk/gtkcolumnviewcellprivate.h" + void gtk_column_view_column_set_column_view (GtkColumnViewColumn *self, GtkColumnView *view); +void gtk_column_view_column_add_cell (GtkColumnViewColumn *self, + GtkColumnViewCell *cell); +void gtk_column_view_column_remove_cell (GtkColumnViewColumn *self, + GtkColumnViewCell *cell); +GtkColumnViewCell * gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self); + +void gtk_column_view_column_queue_resize (GtkColumnViewColumn *self); +void gtk_column_view_column_measure (GtkColumnViewColumn *self, + int *minimum, + int *natural); #endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */ diff --git a/gtk/gtkcolumnviewprivate.h b/gtk/gtkcolumnviewprivate.h index a4e0f17885..417a942ab4 100644 --- a/gtk/gtkcolumnviewprivate.h +++ b/gtk/gtkcolumnviewprivate.h @@ -22,14 +22,5 @@ #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/meson.build b/gtk/meson.build index 5cfb4de7a4..8c2877b913 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -208,6 +208,7 @@ gtk_public_sources = files([ 'gtkcolorutils.c', 'gtkcolumnlistitemfactory.c', 'gtkcolumnview.c', + 'gtkcolumnviewcell.c', 'gtkcolumnviewcolumn.c', 'gtkcombobox.c', 'gtkcomboboxtext.c',