From: Benjamin Otte Date: Fri, 7 Apr 2023 18:50:08 +0000 (+0200) Subject: listitemmanager: Properly handle sections during add/remove X-Git-Tag: archive/raspbian/4.12.3+ds-1+rpi1~1^2^2^2~22^2~1^2~287^2~16 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=8e69c5c89c7601e7851bf1ae5d6e4a5d45f9a7dc;p=gtk4.git listitemmanager: Properly handle sections during add/remove We don't insert sections ourselves yet, but we handle the existing one when items get added or removed. --- diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c index 0f1a9c0995..346464289b 100644 --- a/gtk/gtklistitemmanager.c +++ b/gtk/gtklistitemmanager.c @@ -23,6 +23,7 @@ #include "gtklistitembaseprivate.h" #include "gtklistitemwidgetprivate.h" +#include "gtksectionmodel.h" #include "gtkwidgetprivate.h" struct _GtkListItemManager @@ -159,7 +160,6 @@ gtk_list_item_manager_new (GtkWidget *widget, GtkListItemBase * (* create_widget) (GtkWidget *)) { GtkListItemManager *self; - GtkListTile *header, *footer; g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); @@ -176,13 +176,16 @@ gtk_list_item_manager_new (GtkWidget *widget, gtk_list_item_manager_clear_node, NULL); - header = gtk_rb_tree_insert_after (self->items, NULL); - header->type = GTK_LIST_TILE_HEADER; + return self; +} - footer = gtk_rb_tree_insert_before (self->items, NULL); - footer->type = GTK_LIST_TILE_FOOTER; +static gboolean +gtk_list_item_manager_has_sections (GtkListItemManager *self) +{ + if (self->model == NULL) + return FALSE; - return self; + return GTK_IS_SECTION_MODEL (self->model); } void @@ -272,6 +275,110 @@ gtk_list_item_manager_get_nth (GtkListItemManager *self, return tile; } +static GtkListTile * +gtk_list_tile_get_header (GtkListItemManager *self, + GtkListTile *tile) +{ + GtkListTileAugment *aug; + GtkListTile *other; + gboolean check_right = FALSE; + + while (TRUE) + { + if (check_right) + { + other = gtk_rb_tree_node_get_right (tile); + if (other) + { + aug = gtk_rb_tree_get_augment (self->items, other); + if (aug->has_header) + { + check_right = TRUE; + tile = other; + continue; + } + } + } + + if (tile->type == GTK_LIST_TILE_HEADER || + tile->type == GTK_LIST_TILE_UNMATCHED_HEADER) + return tile; + + other = gtk_rb_tree_node_get_left (tile); + if (other) + { + aug = gtk_rb_tree_get_augment (self->items, other); + if (aug->has_header) + { + check_right = TRUE; + tile = other; + continue; + } + } + + while ((other = gtk_rb_tree_node_get_parent (tile))) + { + if (gtk_rb_tree_node_get_right (other) == tile) + break; + tile = other; + } + tile = other; + check_right = FALSE; + } +} + +static GtkListTile * +gtk_list_tile_get_footer (GtkListItemManager *self, + GtkListTile *tile) +{ + GtkListTileAugment *aug; + GtkListTile *other; + gboolean check_left = FALSE; + + while (TRUE) + { + if (check_left) + { + other = gtk_rb_tree_node_get_left (tile); + if (other) + { + aug = gtk_rb_tree_get_augment (self->items, other); + if (aug->has_footer) + { + check_left = TRUE; + tile = other; + continue; + } + } + } + + if (tile->type == GTK_LIST_TILE_FOOTER || + tile->type == GTK_LIST_TILE_UNMATCHED_FOOTER) + return tile; + + other = gtk_rb_tree_node_get_right (tile); + if (other) + { + aug = gtk_rb_tree_get_augment (self->items, other); + if (aug->has_footer) + { + check_left = TRUE; + tile = other; + continue; + } + } + + while ((other = gtk_rb_tree_node_get_parent (tile))) + { + if (gtk_rb_tree_node_get_left (other) == tile) + break; + tile = other; + } + tile = other; + check_left = FALSE; + } +} + /* This computes Manhattan distance */ static int rectangle_distance (const cairo_rectangle_int_t *rect, @@ -607,7 +714,7 @@ gtk_list_item_manager_remove_items (GtkListItemManager *self, guint position, guint n_items) { - GtkListTile *tile, *next; + GtkListTile *tile, *header; guint offset; if (n_items == 0) @@ -616,25 +723,81 @@ gtk_list_item_manager_remove_items (GtkListItemManager *self, tile = gtk_list_item_manager_get_nth (self, position, &offset); if (offset) tile = gtk_list_item_manager_ensure_split (self, tile, offset); + for (header = gtk_rb_tree_node_get_previous (tile); + header->type != GTK_LIST_TILE_HEADER && header->type != GTK_LIST_TILE_UNMATCHED_HEADER; + header = gtk_rb_tree_node_get_previous (header)) + { + if (header->type == GTK_LIST_TILE_ITEM) + { + header = NULL; + break; + } + } while (n_items > 0) { - if (tile->n_items > n_items) + switch (tile->type) { - gtk_list_item_manager_ensure_split (self, tile, n_items); - g_assert (tile->n_items <= n_items); + case GTK_LIST_TILE_HEADER: + case GTK_LIST_TILE_UNMATCHED_HEADER: + g_assert (header == NULL); + header = tile; + break; + + case GTK_LIST_TILE_FOOTER: + case GTK_LIST_TILE_UNMATCHED_FOOTER: + if (header) + { + header->type = GTK_LIST_TILE_REMOVED; + gtk_rb_tree_node_mark_dirty (header); + tile->type = GTK_LIST_TILE_REMOVED; + gtk_rb_tree_node_mark_dirty (tile); + header = NULL; + } + break; + + case GTK_LIST_TILE_FILLER: + case GTK_LIST_TILE_REMOVED: + break; + + case GTK_LIST_TILE_ITEM: + if (tile->n_items > n_items) + { + gtk_list_item_manager_ensure_split (self, tile, n_items); + g_assert (tile->n_items <= n_items); + } + if (tile->widget) + gtk_list_item_manager_release_list_item (self, change, tile->widget); + tile->widget = NULL; + n_items -= tile->n_items; + tile->n_items = 0; + tile->type = GTK_LIST_TILE_REMOVED; + gtk_rb_tree_node_mark_dirty (tile); + break; + + default: + g_assert_not_reached (); + break; } - next = gtk_rb_tree_node_get_next (tile); - if (tile->widget) - gtk_list_item_manager_release_list_item (self, change, tile->widget); - tile->widget = NULL; - n_items -= tile->n_items; - tile->n_items = 0; - tile->type = GTK_LIST_TILE_REMOVED; - gtk_rb_tree_node_mark_dirty (tile); + tile = gtk_rb_tree_node_get_next (tile); + } - tile = next; + if (header) + { + for (; + tile->type != GTK_LIST_TILE_ITEM; + tile = gtk_rb_tree_node_get_next (tile)) + { + if (tile->type == GTK_LIST_TILE_FOOTER || tile->type == GTK_LIST_TILE_UNMATCHED_FOOTER) + { + header->type = GTK_LIST_TILE_REMOVED; + gtk_rb_tree_node_mark_dirty (header); + tile->type = GTK_LIST_TILE_REMOVED; + gtk_rb_tree_node_mark_dirty (tile); + break; + } + } } gtk_widget_queue_resize (GTK_WIDGET (self->widget)); @@ -647,21 +810,60 @@ gtk_list_item_manager_add_items (GtkListItemManager *self, { GtkListTile *tile; guint offset; + gboolean has_sections; if (n_items == 0) return; + has_sections = gtk_list_item_manager_has_sections (self); + tile = gtk_list_item_manager_get_nth (self, position, &offset); if (tile == NULL) - tile = gtk_rb_tree_get_last (self->items); + { + /* at end of list, pick the footer */ + for (tile = gtk_rb_tree_get_last (self->items); + tile && (tile->type == GTK_LIST_TILE_REMOVED || tile->type == GTK_LIST_TILE_FILLER); + tile = gtk_rb_tree_node_get_previous (tile)) + { } + + if (tile == NULL) + { + /* empty list, there isn't even a footer yet */ + tile = gtk_rb_tree_insert_after (self->items, NULL); + tile->type = has_sections ? GTK_LIST_TILE_UNMATCHED_HEADER : GTK_LIST_TILE_HEADER; + + tile = gtk_rb_tree_insert_after (self->items, tile); + tile->type = has_sections ? GTK_LIST_TILE_UNMATCHED_FOOTER : GTK_LIST_TILE_FOOTER; + } + else if (has_sections && tile->type == GTK_LIST_TILE_FOOTER) + { + gtk_list_tile_set_type (tile, + GTK_LIST_TILE_UNMATCHED_FOOTER); + gtk_list_tile_set_type (gtk_list_tile_get_header (self, tile), + GTK_LIST_TILE_UNMATCHED_HEADER); + } + } if (offset) tile = gtk_list_item_manager_ensure_split (self, tile, offset); - + tile = gtk_rb_tree_insert_before (self->items, tile); tile->type = GTK_LIST_TILE_ITEM; tile->n_items = n_items; gtk_rb_tree_node_mark_dirty (tile); + if (has_sections) + { + GtkListTile *section = gtk_rb_tree_node_get_previous (tile); + + if (section->type == GTK_LIST_TILE_HEADER) + { + gtk_list_tile_set_type (section, + GTK_LIST_TILE_UNMATCHED_HEADER); + gtk_list_tile_set_type (gtk_list_tile_get_footer (self, section), + GTK_LIST_TILE_UNMATCHED_FOOTER); + } + } + gtk_widget_queue_resize (GTK_WIDGET (self->widget)); } @@ -1138,6 +1340,7 @@ gtk_list_item_manager_model_selection_changed_cb (GListModel *model, static void gtk_list_item_manager_clear_model (GtkListItemManager *self) { + GtkListTile *tile; GSList *l; if (self->model == NULL) @@ -1156,6 +1359,15 @@ gtk_list_item_manager_clear_model (GtkListItemManager *self) gtk_list_item_manager_model_items_changed_cb, self); g_clear_object (&self->model); + + /* really empty the tiles */ + for (tile = gtk_list_tile_gc (self, gtk_list_item_manager_get_first (self)); + tile; + tile = gtk_list_tile_gc (self, tile)) + { + g_assert (tile->type == GTK_LIST_TILE_FILLER); + } + g_assert (gtk_rb_tree_get_root (self->items) == NULL); } static void