columnview: Add a custom LayoutManager

The ColumnView now allocates column widths first and then the individual
rows use the new layout manager which looks at the column allocations to
allocate their children.
This commit is contained in:
Benjamin Otte 2019-11-07 23:40:47 +01:00 committed by Matthias Clasen
parent 767e7cb06f
commit 326cb1148b
11 changed files with 354 additions and 32 deletions

View File

@ -27,6 +27,7 @@ private_headers = [
'gtkcolumnlistitemfactoryprivate.h',
'gtkcolumnviewcellprivate.h',
'gtkcolumnviewcolumnprivate.h',
'gtkcolumnviewlayoutprivate.h',
'gtkcolumnviewprivate.h',
'gtkcomboboxprivate.h',
'gtkconstraintexpressionprivate.h',

View File

@ -23,6 +23,7 @@
#include "gtkboxlayout.h"
#include "gtkcolumnviewcolumnprivate.h"
#include "gtkcolumnviewlayoutprivate.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
@ -51,7 +52,7 @@ gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
/* FIXME: evil */
gtk_widget_set_layout_manager (GTK_WIDGET (widget),
gtk_box_layout_new (GTK_ORIENTATION_HORIZONTAL));
gtk_column_view_layout_new (self->view));
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, widget, list_item);

View File

@ -118,6 +118,93 @@ G_DEFINE_TYPE_WITH_CODE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET,
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static void
gtk_column_view_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
gtk_column_view_measure_across (self, minimum, natural);
}
else
{
gtk_widget_measure (GTK_WIDGET (self->listview),
orientation, for_size,
minimum, natural,
minimum_baseline, natural_baseline);
}
}
static int
gtk_column_view_allocate_columns (GtkColumnView *self,
int width)
{
GtkScrollablePolicy scroll_policy;
int col_min, col_nat, widget_min, widget_nat, extra, col_size, x;
guint i;
gtk_column_view_measure_across (self, &col_min, &col_nat);
gtk_widget_measure (GTK_WIDGET (self->listview),
GTK_ORIENTATION_HORIZONTAL, -1,
&widget_min, &widget_nat,
NULL, NULL);
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
if (scroll_policy == GTK_SCROLL_MINIMUM)
{
extra = widget_min - col_min;
col_size = col_min;
}
else
{
extra = widget_nat - col_nat;
col_size = col_nat;
}
width -= extra;
width = MAX (width, col_size);
x = 0;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
{
GtkColumnViewColumn *column;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
gtk_column_view_column_measure (column, &col_min, &col_nat);
if (scroll_policy == GTK_SCROLL_MINIMUM)
col_size = col_min;
else
col_size = col_nat;
gtk_column_view_column_allocate (column, x, col_size);
x += col_size;
g_object_unref (column);
}
return width + extra;
}
static void
gtk_column_view_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
int full_width;
full_width = gtk_column_view_allocate_columns (self, width);
gtk_widget_allocate (GTK_WIDGET (self->listview), full_width, height, baseline, NULL);
}
static void
gtk_column_view_activate_cb (GtkListView *listview,
guint pos,
@ -261,6 +348,9 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gpointer iface;
widget_class->measure = gtk_column_view_measure;
widget_class->size_allocate = gtk_column_view_allocate;
gobject_class->dispose = gtk_column_view_dispose;
gobject_class->finalize = gtk_column_view_finalize;
gobject_class->get_property = gtk_column_view_get_property;
@ -362,7 +452,6 @@ gtk_column_view_init (GtkColumnView *self)
gtk_css_node_add_class (gtk_widget_get_css_node (GTK_WIDGET (self)),
g_quark_from_static_string (I_("view")));
gtk_widget_set_layout_manager (GTK_WIDGET (self), gtk_box_layout_new (GTK_ORIENTATION_VERTICAL));
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
}
@ -531,3 +620,31 @@ gtk_column_view_remove_column (GtkColumnView *self,
g_list_store_remove (self->columns, i);
}
void
gtk_column_view_measure_across (GtkColumnView *self,
int *minimum,
int *natural)
{
guint i;
int min, nat;
min = 0;
nat = 0;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
{
GtkColumnViewColumn *column;
int col_min, col_nat;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
gtk_column_view_column_measure (column, &col_min, &col_nat);
min += col_min;
nat += col_nat;
g_object_unref (column);
}
*minimum = min;
*natural = nat;
}

View File

@ -44,17 +44,6 @@ struct _GtkColumnViewCellClass
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,
@ -64,19 +53,10 @@ gtk_column_view_cell_measure (GtkWidget *widget,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
GtkWidget *child = gtk_widget_get_first_child (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);
}
if (child)
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
}
static void
@ -202,3 +182,9 @@ gtk_column_view_cell_get_prev (GtkColumnViewCell *self)
{
return self->prev_cell;
}
GtkColumnViewColumn *
gtk_column_view_cell_get_column (GtkColumnViewCell *self)
{
return self->column;
}

View File

@ -42,10 +42,7 @@ void gtk_column_view_cell_remove (GtkColumnViewCe
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);
GtkColumnViewColumn * gtk_column_view_cell_get_column (GtkColumnViewCell *self);
G_END_DECLS

View File

@ -52,6 +52,8 @@ struct _GtkColumnViewColumn
int minimum_size_request;
int natural_size_request;
int allocation_offset;
int allocation_size;
/* This list isn't sorted - this is just caching for performance */
GtkColumnViewCell *first_cell; /* no reference, just caching */
@ -310,7 +312,11 @@ gtk_column_view_column_measure (GtkColumnViewColumn *self,
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);
gtk_widget_measure (GTK_WIDGET (cell),
GTK_ORIENTATION_HORIZONTAL,
-1,
&cell_min, &cell_nat,
NULL, NULL);
min = MAX (min, cell_min);
nat = MAX (nat, cell_nat);
@ -324,6 +330,26 @@ gtk_column_view_column_measure (GtkColumnViewColumn *self,
*natural = self->natural_size_request;
}
void
gtk_column_view_column_allocate (GtkColumnViewColumn *self,
int offset,
int size)
{
self->allocation_offset = offset;
self->allocation_size = size;
}
void
gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
int *offset,
int *size)
{
if (offset)
*offset = self->allocation_offset;
if (size)
*size = self->allocation_size;
}
static void
gtk_column_view_column_create_cells (GtkColumnViewColumn *self)
{

View File

@ -37,5 +37,11 @@ void gtk_column_view_column_queue_resize (GtkColu
void gtk_column_view_column_measure (GtkColumnViewColumn *self,
int *minimum,
int *natural);
void gtk_column_view_column_allocate (GtkColumnViewColumn *self,
int offset,
int size);
void gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
int *offset,
int *size);
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */

149
gtk/gtkcolumnviewlayout.c Normal file
View File

@ -0,0 +1,149 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcolumnviewlayoutprivate.h"
#include "gtkcolumnviewcellprivate.h"
#include "gtkcolumnviewcolumnprivate.h"
#include "gtkcolumnviewprivate.h"
#include "gtkwidgetprivate.h"
struct _GtkColumnViewLayout
{
GtkLayoutManager parent_instance;
GtkColumnView *view; /* no reference */
};
G_DEFINE_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK_TYPE_LAYOUT_MANAGER)
static void
gtk_column_view_layout_measure_along (GtkColumnViewLayout *self,
GtkWidget *widget,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkOrientation orientation = GTK_ORIENTATION_VERTICAL;
GtkWidget *child;
for (child = _gtk_widget_get_first_child (widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
int child_min = 0;
int child_nat = 0;
int child_min_baseline = -1;
int child_nat_baseline = -1;
gtk_widget_measure (child, orientation, for_size,
&child_min, &child_nat,
&child_min_baseline, &child_nat_baseline);
*minimum = MAX (*minimum, child_min);
*natural = MAX (*natural, child_nat);
if (child_min_baseline > -1)
*minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
if (child_nat_baseline > -1)
*natural_baseline = MAX (*natural_baseline, child_nat_baseline);
}
}
static void
gtk_column_view_layout_measure (GtkLayoutManager *layout,
GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnViewLayout *self = GTK_COLUMN_VIEW_LAYOUT (layout);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
gtk_column_view_measure_across (GTK_COLUMN_VIEW (self->view),
minimum,
natural);
}
else
{
gtk_column_view_layout_measure_along (self,
widget,
for_size,
minimum,
natural,
minimum_baseline,
natural_baseline);
}
}
static void
gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkWidget *child;
for (child = _gtk_widget_get_first_child (widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkColumnViewCell *cell = GTK_COLUMN_VIEW_CELL (child);
GtkColumnViewColumn *column = gtk_column_view_cell_get_column (cell);
int col_x, col_width;
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
}
}
static void
gtk_column_view_layout_class_init (GtkColumnViewLayoutClass *klass)
{
GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
layout_manager_class->measure = gtk_column_view_layout_measure;
layout_manager_class->allocate = gtk_column_view_layout_allocate;
}
static void
gtk_column_view_layout_init (GtkColumnViewLayout *self)
{
}
GtkLayoutManager *
gtk_column_view_layout_new (GtkColumnView *view)
{
GtkColumnViewLayout *result;
result = g_object_new (GTK_TYPE_COLUMN_VIEW_LAYOUT, NULL);
result->view = view;
return GTK_LAYOUT_MANAGER (result);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
#define __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
#include <gtk/gtkcolumnview.h>
#include <gtk/gtklayoutmanager.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW_LAYOUT (gtk_column_view_layout_get_type ())
G_DECLARE_FINAL_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK, COLUMN_VIEW_LAYOUT, GtkLayoutManager)
GtkLayoutManager * gtk_column_view_layout_new (GtkColumnView *view);
#endif /* __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__ */

View File

@ -22,5 +22,8 @@
#include "gtk/gtkcolumnview.h"
void gtk_column_view_measure_across (GtkColumnView *self,
int *minimum,
int *natural);
#endif /* __GTK_COLUMN_VIEW_PRIVATE_H__ */

View File

@ -37,6 +37,9 @@ gtk_private_sources = files([
'gtkcolorpickershell.c',
'gtkcolorscale.c',
'gtkcolorswatch.c',
'gtkcolumnlistitemfactory.c',
'gtkcolumnviewcell.c',
'gtkcolumnviewlayout.c',
'gtkconstraintexpression.c',
'gtkconstraintsolver.c',
'gtkconstraintvflparser.c',
@ -193,9 +196,7 @@ gtk_public_sources = files([
'gtkcolorchooserdialog.c',
'gtkcolorchooserwidget.c',
'gtkcolorutils.c',
'gtkcolumnlistitemfactory.c',
'gtkcolumnview.c',
'gtkcolumnviewcell.c',
'gtkcolumnviewcolumn.c',
'gtkcombobox.c',
'gtkcomboboxtext.c',