diff --git a/src/gs-app-reviews-dialog.c b/src/gs-app-reviews-dialog.c index e02ab0aa8c61382081ce11c1b29c4205a40fe9ee..1578e800995440ad5d8c91585e165be01132516f 100644 --- a/src/gs-app-reviews-dialog.c +++ b/src/gs-app-reviews-dialog.c @@ -97,13 +97,29 @@ review_button_clicked_cb (GsReviewRow *row, return; } - refresh_reviews (self); + gs_review_row_refresh (row); +} + +static GSList * /* (transfer container) */ +gather_listbox_rows (GtkWidget *listbox) +{ + GSList *rows = NULL; + GtkWidget *widget; + + widget = gtk_widget_get_first_child (listbox); + while (widget) { + rows = g_slist_prepend (rows, widget); + widget = gtk_widget_get_next_sibling (widget); + } + + return g_slist_reverse (rows); } static void populate_reviews (GsAppReviewsDialog *self) { GPtrArray *reviews; + GSList *rows, *link; gboolean show_reviews = FALSE; guint64 possible_actions = 0; guint i; @@ -158,18 +174,32 @@ populate_reviews (GsAppReviewsDialog *self) } /* add all the reviews */ - gs_widget_remove_all (self->listbox, (GsRemoveFunc) gtk_list_box_remove); + rows = gather_listbox_rows (self->listbox); g_ptr_array_sort (reviews, (GCompareFunc) sort_reviews); - for (i = 0; i < reviews->len; i++) { + for (i = 0, link = rows; i < reviews->len; i++, link = g_slist_next (link)) { AsReview *review = g_ptr_array_index (reviews, i); - GtkWidget *row = gs_review_row_new (review); + GtkWidget *row = NULL; guint64 actions; - gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE); - gtk_list_box_append (GTK_LIST_BOX (self->listbox), row); + /* Try to merge with existing rows, to preserve cursor (focused) row + and window scroll position. */ + if (link != NULL) { + GtkWidget *existing_row = link->data; + if (gs_review_row_get_review (GS_REVIEW_ROW (existing_row)) == review) + row = existing_row; + else + gtk_list_box_remove (GTK_LIST_BOX (self->listbox), existing_row); + } + + if (row == NULL) { + row = gs_review_row_new (review); + gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE); + gtk_list_box_append (GTK_LIST_BOX (self->listbox), row); + + g_signal_connect (row, "button-clicked", + G_CALLBACK (review_button_clicked_cb), self); + } - g_signal_connect (row, "button-clicked", - G_CALLBACK (review_button_clicked_cb), self); if (as_review_get_flags (review) & AS_REVIEW_FLAG_SELF) actions = possible_actions & (1 << GS_REVIEW_ACTION_REMOVE); else @@ -179,6 +209,14 @@ populate_reviews (GsAppReviewsDialog *self) GS_IS_PLUGIN_LOADER (self->plugin_loader) && gs_plugin_loader_get_network_available (self->plugin_loader)); } + while (link != NULL) { + GtkWidget *existing_row = link->data; + gtk_list_box_remove (GTK_LIST_BOX (self->listbox), existing_row); + link = g_slist_next (link); + } + + g_slist_free (rows); + gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "reviews"); } diff --git a/src/gs-review-row.c b/src/gs-review-row.c index 9afb4849bcb6762958f8daeb15aca26d9b1c7db1..bc5aa89b05c68503b985de612c60c4c91c23c965 100644 --- a/src/gs-review-row.c +++ b/src/gs-review-row.c @@ -39,8 +39,11 @@ enum { static guint signals [SIGNAL_LAST] = { 0 }; G_DEFINE_TYPE_WITH_PRIVATE (GsReviewRow, gs_review_row, GTK_TYPE_LIST_BOX_ROW) - -static void +/* FIXME: This is currently public to allow #GsAppReviewsDialog to refresh rows. + * That should no longer be needed once #AsReview emits #GObject::notify correctly, + * as then property bindings can be used internally. + * See https://github.com/ximion/appstream/pull/448 */ +void gs_review_row_refresh (GsReviewRow *row) { GsReviewRowPrivate *priv = gs_review_row_get_instance_private (row); diff --git a/src/gs-review-row.h b/src/gs-review-row.h index 48139ce9634ff8a0d43c5a7ff402b6de5d273ae1..4cb20188e709918525b97c5f682211e09962eff8 100644 --- a/src/gs-review-row.h +++ b/src/gs-review-row.h @@ -52,5 +52,6 @@ void gs_review_row_set_actions (GsReviewRow *review_row, guint64 actions); void gs_review_row_set_network_available (GsReviewRow *review_row, gboolean network_available); +void gs_review_row_refresh (GsReviewRow *row); G_END_DECLS