diff options
author | Benjamin Otte <otte@redhat.com> | 2023-04-11 04:45:30 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2023-04-12 14:36:50 +0200 |
commit | 2f31fe06a0d914f1aa1ed5f8deac57202ab2f552 (patch) | |
tree | 735056fa15fb52ca0655b6e66f29aaab711716de | |
parent | eb79498f620cec65e2eea2f3ab469c1ade66377d (diff) | |
download | gtk+-2f31fe06a0d914f1aa1ed5f8deac57202ab2f552.tar.gz |
listitemmanager: Create header items for sections
Now, finally, listitemmanager has all the necessary support to make
section headers work.
-rw-r--r-- | gtk/gtklistitemmanager.c | 204 | ||||
-rw-r--r-- | testsuite/gtk/listitemmanager.c | 16 |
2 files changed, 155 insertions, 65 deletions
diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c index 1987cf1e96..2ca7f9eaf8 100644 --- a/gtk/gtklistitemmanager.c +++ b/gtk/gtklistitemmanager.c @@ -62,6 +62,7 @@ struct _GtkListItemChange { GHashTable *deleted_items; GQueue recycled_items; + GQueue recycled_headers; }; G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT) @@ -71,6 +72,7 @@ gtk_list_item_change_init (GtkListItemChange *change) { change->deleted_items = NULL; g_queue_init (&change->recycled_items); + g_queue_init (&change->recycled_headers); } static void @@ -82,6 +84,8 @@ gtk_list_item_change_finish (GtkListItemChange *change) while ((widget = g_queue_pop_head (&change->recycled_items))) gtk_widget_unparent (widget); + while ((widget = g_queue_pop_head (&change->recycled_headers))) + gtk_widget_unparent (widget); } static void @@ -92,6 +96,18 @@ gtk_list_item_change_recycle (GtkListItemChange *change, } static void +gtk_list_item_change_clear_header (GtkListItemChange *change, + GtkWidget **widget) +{ + if (*widget == NULL) + return; + + g_assert (GTK_IS_LIST_HEADER_BASE (*widget)); + g_queue_push_tail (&change->recycled_headers, *widget); + *widget = NULL; +} + +static void gtk_list_item_change_release (GtkListItemChange *change, GtkListItemBase *widget) { @@ -134,6 +150,12 @@ gtk_list_item_change_get (GtkListItemChange *change, return NULL; } +static GtkListHeaderBase * +gtk_list_item_change_get_header (GtkListItemChange *change) +{ + return g_queue_pop_head (&change->recycled_headers); +} + static void potentially_empty_rectangle_union (cairo_rectangle_int_t *self, const cairo_rectangle_int_t *area) @@ -696,6 +718,7 @@ gtk_list_tile_set_type (GtkListTile *tile, if (tile->type == type) return; + g_assert (tile->widget == NULL); tile->type = type; gtk_rb_tree_node_mark_dirty (tile); } @@ -839,6 +862,7 @@ gtk_list_item_manager_remove_items (GtkListItemManager *self, case GTK_LIST_TILE_UNMATCHED_FOOTER: if (header) { + gtk_list_item_change_clear_header (change, &header->widget); gtk_list_tile_set_type (header, GTK_LIST_TILE_REMOVED); gtk_list_tile_set_type (tile, GTK_LIST_TILE_REMOVED); header = NULL; @@ -873,6 +897,7 @@ gtk_list_item_manager_remove_items (GtkListItemManager *self, { if (tile->type == GTK_LIST_TILE_FOOTER || tile->type == GTK_LIST_TILE_UNMATCHED_FOOTER) { + gtk_list_item_change_clear_header (change, &header->widget); gtk_list_tile_set_type (header, GTK_LIST_TILE_REMOVED); gtk_list_tile_set_type (tile, GTK_LIST_TILE_REMOVED); } @@ -883,6 +908,7 @@ gtk_list_item_manager_remove_items (GtkListItemManager *self, static void gtk_list_item_manager_add_items (GtkListItemManager *self, + GtkListItemChange *change, guint position, guint n_items) { @@ -908,17 +934,20 @@ gtk_list_item_manager_add_items (GtkListItemManager *self, { /* 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->type = GTK_LIST_TILE_UNMATCHED_HEADER; tile = gtk_rb_tree_insert_after (self->items, tile); - tile->type = has_sections ? GTK_LIST_TILE_UNMATCHED_FOOTER : GTK_LIST_TILE_FOOTER; + tile->type = GTK_LIST_TILE_UNMATCHED_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); + GtkListTile *header; + + gtk_list_tile_set_type (tile, GTK_LIST_TILE_UNMATCHED_FOOTER); + + header = gtk_list_tile_get_header (self, tile); + gtk_list_item_change_clear_header (change, &header->widget); + gtk_list_tile_set_type (header, GTK_LIST_TILE_UNMATCHED_HEADER); } } if (offset) @@ -935,6 +964,7 @@ gtk_list_item_manager_add_items (GtkListItemManager *self, if (section->type == GTK_LIST_TILE_HEADER) { + gtk_list_item_change_clear_header (change, §ion->widget); gtk_list_tile_set_type (section, GTK_LIST_TILE_UNMATCHED_HEADER); gtk_list_tile_set_type (gtk_list_tile_get_footer (self, section), @@ -1140,6 +1170,7 @@ gtk_list_item_manager_release_items (GtkListItemManager *self, case GTK_LIST_TILE_HEADER: case GTK_LIST_TILE_UNMATCHED_HEADER: g_assert (deleted_section); + gtk_list_item_change_clear_header (change, &tile->widget); G_GNUC_FALLTHROUGH; case GTK_LIST_TILE_FOOTER: case GTK_LIST_TILE_UNMATCHED_FOOTER: @@ -1157,22 +1188,24 @@ gtk_list_item_manager_release_items (GtkListItemManager *self, } if (deleted_section) { - gtk_list_tile_set_type (gtk_list_tile_get_header (self, tile), - GTK_LIST_TILE_UNMATCHED_HEADER); - gtk_list_tile_set_type (gtk_list_tile_get_footer (self, tile), - GTK_LIST_TILE_UNMATCHED_FOOTER); + tile = gtk_list_tile_get_header (self, tile); + gtk_list_item_change_clear_header (change, &tile->widget); + gtk_list_tile_set_type (tile, GTK_LIST_TILE_UNMATCHED_HEADER); + + tile = gtk_list_tile_get_footer (self, tile); + gtk_list_tile_set_type (tile, GTK_LIST_TILE_UNMATCHED_FOOTER); } position += query_n_items; } } -static void +static GtkListTile * gtk_list_item_manager_insert_section (GtkListItemManager *self, guint pos, GtkListTileType footer_type, GtkListTileType header_type) { - GtkListTile *tile, *other; + GtkListTile *tile, *footer, *header; guint offset; tile = gtk_list_item_manager_get_nth (self, pos, &offset); @@ -1180,46 +1213,65 @@ gtk_list_item_manager_insert_section (GtkListItemManager *self, { if (footer_type == GTK_LIST_TILE_FOOTER) { - other = gtk_rb_tree_get_last (self->items); - if (other->type != GTK_LIST_TILE_FOOTER && other->type != GTK_LIST_TILE_UNMATCHED_FOOTER) - other = gtk_list_tile_get_previous_skip (other); - gtk_list_tile_set_type (other, footer_type); + footer = gtk_rb_tree_get_last (self->items); + if (footer->type != GTK_LIST_TILE_FOOTER && footer->type != GTK_LIST_TILE_UNMATCHED_FOOTER) + footer = gtk_list_tile_get_previous_skip (footer); + gtk_list_tile_set_type (footer, footer_type); } - return; + return NULL; } if (offset) tile = gtk_list_item_manager_ensure_split (self, tile, offset); - other = gtk_list_tile_get_previous_skip (tile); - if (other->type == GTK_LIST_TILE_HEADER || other->type == GTK_LIST_TILE_UNMATCHED_HEADER) + header = gtk_list_tile_get_previous_skip (tile); + if (header->type == GTK_LIST_TILE_HEADER || header->type == GTK_LIST_TILE_UNMATCHED_HEADER) { if (header_type == GTK_LIST_TILE_HEADER) - gtk_list_tile_set_type (other, header_type); + gtk_list_tile_set_type (header, header_type); if (footer_type == GTK_LIST_TILE_FOOTER) { - other = gtk_list_tile_get_previous_skip (other); - if (other) - gtk_list_tile_set_type (other, footer_type); + footer = gtk_list_tile_get_previous_skip (header); + if (footer) + gtk_list_tile_set_type (footer, footer_type); } } else { self->prepare_section (self->widget, tile, pos); - other = gtk_rb_tree_insert_before (self->items, tile); - gtk_list_tile_set_type (other, header_type); - other = gtk_rb_tree_insert_before (self->items, other); - gtk_list_tile_set_type (other, footer_type); + header = gtk_rb_tree_insert_before (self->items, tile); + gtk_list_tile_set_type (header, header_type); + footer = gtk_rb_tree_insert_before (self->items, header); + gtk_list_tile_set_type (footer, footer_type); } + + return header; +} + +static GtkWidget * +gtk_list_tile_find_widget_before (GtkListTile *tile) +{ + GtkListTile *other; + + for (other = gtk_rb_tree_node_get_previous (tile); + other; + other = gtk_rb_tree_node_get_previous (other)) + { + if (other->widget) + return other->widget; + } + + return NULL; } static void gtk_list_item_manager_ensure_items (GtkListItemManager *self, GtkListItemChange *change, - guint update_start) + guint update_start, + int update_diff) { - GtkListTile *tile, *other_tile, *header; + GtkListTile *tile, *header; GtkWidget *insert_after; guint position, i, n_items, query_n_items, offset; gboolean tracked, has_sections; @@ -1252,23 +1304,43 @@ gtk_list_item_manager_ensure_items (GtkListItemManager *self, if (header->type == GTK_LIST_TILE_UNMATCHED_HEADER) { guint start, end; + gpointer item; + gtk_section_model_get_section (GTK_SECTION_MODEL (self->model), position, &start, &end); - gtk_list_item_manager_insert_section (self, - start, - GTK_LIST_TILE_UNMATCHED_FOOTER, - GTK_LIST_TILE_HEADER); + header = gtk_list_item_manager_insert_section (self, + start, + GTK_LIST_TILE_UNMATCHED_FOOTER, + GTK_LIST_TILE_HEADER); + g_assert (header->widget == NULL); + header->widget = GTK_WIDGET (gtk_list_item_change_get_header (change)); + if (header->widget == NULL) + header->widget = GTK_WIDGET (self->create_header_widget (self->widget)); + item = g_list_model_get_item (G_LIST_MODEL (self->model), start); + gtk_list_header_base_update (GTK_LIST_HEADER_BASE (header->widget), + item, + start, end); + g_object_unref (item); + gtk_widget_insert_after (header->widget, + self->widget, + gtk_list_tile_find_widget_before (header)); + gtk_list_item_manager_insert_section (self, end, GTK_LIST_TILE_FOOTER, GTK_LIST_TILE_UNMATCHED_HEADER); } + else if (gtk_list_header_base_get_end (GTK_LIST_HEADER_BASE (header->widget)) > update_start) + { + GtkListHeaderBase *base = GTK_LIST_HEADER_BASE (header->widget); + guint start = gtk_list_header_base_get_start (base); + gtk_list_header_base_update (base, + gtk_list_header_base_get_item (base), + start > update_start ? start + update_diff : start, + gtk_list_header_base_get_end (base) + update_diff); + } } - for (other_tile = tile; - other_tile && other_tile->widget == NULL; - other_tile = gtk_rb_tree_node_get_previous (other_tile)) - { /* do nothing */ } - insert_after = other_tile ? other_tile->widget : NULL; + insert_after = gtk_list_tile_find_widget_before (tile); for (i = 0; i < query_n_items;) { @@ -1310,8 +1382,23 @@ gtk_list_item_manager_ensure_items (GtkListItemManager *self, if (has_sections) { guint start, end; + gpointer item; + gtk_section_model_get_section (GTK_SECTION_MODEL (self->model), position + i, &start, &end); + gtk_list_tile_set_type (tile, GTK_LIST_TILE_HEADER); + g_assert (tile->widget == NULL); + tile->widget = GTK_WIDGET (gtk_list_item_change_get_header (change)); + if (tile->widget == NULL) + tile->widget = GTK_WIDGET (self->create_header_widget (self->widget)); + item = g_list_model_get_item (G_LIST_MODEL (self->model), start); + gtk_list_header_base_update (GTK_LIST_HEADER_BASE (tile->widget), + item, + start, end); + g_object_unref (item); + gtk_widget_insert_after (tile->widget, self->widget, insert_after); + insert_after = tile->widget; + gtk_list_item_manager_insert_section (self, end, GTK_LIST_TILE_FOOTER, @@ -1352,7 +1439,7 @@ gtk_list_item_manager_model_items_changed_cb (GListModel *model, n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model)); gtk_list_item_manager_remove_items (self, &change, position, removed); - gtk_list_item_manager_add_items (self, position, added); + gtk_list_item_manager_add_items (self, &change, position, added); /* Check if any tracked item was removed */ for (l = self->trackers; l; l = l->next) @@ -1478,7 +1565,7 @@ gtk_list_item_manager_model_items_changed_cb (GListModel *model, } } - gtk_list_item_manager_ensure_items (self, &change, position + added); + gtk_list_item_manager_ensure_items (self, &change, position + added, added - removed); /* final loop through the trackers: Grab the missing widgets. * For items that had been removed and a new position was set, grab @@ -1615,6 +1702,8 @@ gtk_list_item_manager_set_model (GtkListItemManager *self, if (model) { + GtkListItemChange change; + self->model = g_object_ref (model); g_signal_connect (model, @@ -1626,7 +1715,10 @@ gtk_list_item_manager_set_model (GtkListItemManager *self, G_CALLBACK (gtk_list_item_manager_model_selection_changed_cb), self); - gtk_list_item_manager_add_items (self, 0, g_list_model_get_n_items (G_LIST_MODEL (model))); + gtk_list_item_change_init (&change); + gtk_list_item_manager_add_items (self, &change, 0, g_list_model_get_n_items (G_LIST_MODEL (model))); + gtk_list_item_manager_ensure_items (self, &change, G_MAXUINT, 0); + gtk_list_item_change_finish (&change); } } @@ -1653,21 +1745,9 @@ gtk_list_item_manager_set_has_sections (GtkListItemManager *self, self->has_sections = has_sections; - if (!had_sections && gtk_list_item_manager_has_sections (self)) - { - tile = gtk_rb_tree_get_first (self->items); - if (tile && tile->type != GTK_LIST_TILE_HEADER && tile->type != GTK_LIST_TILE_UNMATCHED_HEADER) - tile = gtk_list_tile_get_next_skip (tile); - if (tile) - gtk_list_tile_set_type (tile, GTK_LIST_TILE_UNMATCHED_HEADER); - - tile = gtk_rb_tree_get_last (self->items); - if (tile && tile->type != GTK_LIST_TILE_FOOTER && tile->type != GTK_LIST_TILE_UNMATCHED_FOOTER) - tile = gtk_list_tile_get_previous_skip (tile); - if (tile) - gtk_list_tile_set_type (tile, GTK_LIST_TILE_UNMATCHED_FOOTER); - } - else if (had_sections && !gtk_list_item_manager_has_sections (self)) + gtk_list_item_change_init (&change); + + if (had_sections && !gtk_list_item_manager_has_sections (self)) { GtkListTile *header = NULL, *footer = NULL; @@ -1679,6 +1759,7 @@ gtk_list_item_manager_set_has_sections (GtkListItemManager *self, { case GTK_LIST_TILE_HEADER: case GTK_LIST_TILE_UNMATCHED_HEADER: + gtk_list_item_change_clear_header (&change, &tile->widget); if (!header) header = tile; else @@ -1701,13 +1782,12 @@ gtk_list_item_manager_set_has_sections (GtkListItemManager *self, } if (header) { - gtk_list_tile_set_type (header, GTK_LIST_TILE_HEADER); - gtk_list_tile_set_type (footer, GTK_LIST_TILE_FOOTER); + gtk_list_tile_set_type (header, GTK_LIST_TILE_UNMATCHED_HEADER); + gtk_list_tile_set_type (footer, GTK_LIST_TILE_UNMATCHED_FOOTER); } } - gtk_list_item_change_init (&change); - gtk_list_item_manager_ensure_items (self, &change, G_MAXUINT); + gtk_list_item_manager_ensure_items (self, &change, G_MAXUINT, 0); gtk_list_item_change_finish (&change); gtk_widget_queue_resize (self->widget); @@ -1748,7 +1828,7 @@ gtk_list_item_tracker_free (GtkListItemManager *self, g_free (tracker); gtk_list_item_change_init (&change); - gtk_list_item_manager_ensure_items (self, &change, G_MAXUINT); + gtk_list_item_manager_ensure_items (self, &change, G_MAXUINT, 0); gtk_list_item_change_finish (&change); gtk_widget_queue_resize (self->widget); @@ -1779,7 +1859,7 @@ gtk_list_item_tracker_set_position (GtkListItemManager *self, tracker->n_after = n_after; gtk_list_item_change_init (&change); - gtk_list_item_manager_ensure_items (self, &change, G_MAXUINT); + gtk_list_item_manager_ensure_items (self, &change, G_MAXUINT, 0); gtk_list_item_change_finish (&change); tile = gtk_list_item_manager_get_nth (self, position, NULL); diff --git a/testsuite/gtk/listitemmanager.c b/testsuite/gtk/listitemmanager.c index d883aca063..ba5fbd54ba 100644 --- a/testsuite/gtk/listitemmanager.c +++ b/testsuite/gtk/listitemmanager.c @@ -97,12 +97,15 @@ check_list_item_manager (GtkListItemManager *items, GtkListTile *tile; guint n_items = 0; guint i; + gboolean has_sections; enum { NO_SECTION, MATCHED_SECTION, UNMATCHED_SECTION } section_state = NO_SECTION; + has_sections = gtk_list_item_manager_get_has_sections (items); + for (tile = gtk_list_item_manager_get_first (items); tile != NULL; tile = gtk_rb_tree_node_get_next (tile)) @@ -112,7 +115,8 @@ check_list_item_manager (GtkListItemManager *items, case GTK_LIST_TILE_HEADER: g_assert_cmpint (section_state, ==, NO_SECTION); g_assert_cmpint (tile->n_items, ==, 0); - g_assert_false (tile->widget); + g_assert_true (has_sections); + g_assert_true (tile->widget); section_state = MATCHED_SECTION; break; @@ -126,6 +130,7 @@ check_list_item_manager (GtkListItemManager *items, case GTK_LIST_TILE_FOOTER: g_assert_cmpint (section_state, ==, MATCHED_SECTION); g_assert_cmpint (tile->n_items, ==, 0); + g_assert_true (has_sections); g_assert_false (tile->widget); section_state = NO_SECTION; break; @@ -142,7 +147,10 @@ check_list_item_manager (GtkListItemManager *items, if (tile->widget) { GObject *item = g_list_model_get_item (model, n_items); - g_assert_cmpint (section_state, ==, MATCHED_SECTION); + if (has_sections) + g_assert_cmpint (section_state, ==, MATCHED_SECTION); + else + g_assert_cmpint (section_state, ==, UNMATCHED_SECTION); g_assert_cmphex (GPOINTER_TO_SIZE (item), ==, GPOINTER_TO_SIZE (gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (tile->widget)))); g_object_unref (item); g_assert_cmpint (n_items, ==, gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (tile->widget))); @@ -200,7 +208,8 @@ check_list_item_manager (GtkListItemManager *items, case GTK_LIST_TILE_HEADER: g_assert_cmpint (section_state, ==, NO_SECTION); g_assert_cmpint (tile->n_items, ==, 0); - g_assert_false (tile->widget); + g_assert_true (has_sections); + g_assert_true (tile->widget); section_state = MATCHED_SECTION; break; @@ -214,6 +223,7 @@ check_list_item_manager (GtkListItemManager *items, case GTK_LIST_TILE_FOOTER: g_assert_cmpint (section_state, ==, MATCHED_SECTION); g_assert_cmpint (tile->n_items, ==, 0); + g_assert_true (has_sections); g_assert_false (tile->widget); section_state = NO_SECTION; break; |