summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Hergert <chergert@redhat.com>2022-11-16 14:54:33 -0800
committerChristian Hergert <chergert@redhat.com>2022-11-16 15:08:00 -0800
commitdc73ece2b96886a778dfe34e808106e89469f1f0 (patch)
tree754aee0bd8327a550c176410c08aec2acb11e770
parent2fce4e13d0cf6469684995c9e2a1c503e3cf889e (diff)
downloadgtksourceview-wip/chergert/fix-completion-snapshots.tar.gz
completionlistbox: lock listmodel items during snapshotwip/chergert/fix-completion-snapshots
This avoids the GtkSourceCompletionContext (a GListModel) changes that could come in during the snapshot cycle from messing with the rows that we want to see snapshotted. Related https://gitlab.gnome.org/GNOME/gnome-builder/-/issues/1840
-rw-r--r--gtksourceview/gtksourcecompletionlistbox.c132
1 files changed, 103 insertions, 29 deletions
diff --git a/gtksourceview/gtksourcecompletionlistbox.c b/gtksourceview/gtksourcecompletionlistbox.c
index a8f7d209..a7f6b810 100644
--- a/gtksourceview/gtksourcecompletionlistbox.c
+++ b/gtksourceview/gtksourcecompletionlistbox.c
@@ -27,12 +27,21 @@
#include "gtksourcecompletionlistboxrow-private.h"
#include "gtksourcecompletionproposal.h"
#include "gtksourcecompletionprovider.h"
+#include "gtksourcelistsnapshot-private.h"
#include "gtksourceview-private.h"
struct _GtkSourceCompletionListBox
{
GtkWidget parent_instance;
+ /* Used to snapshot a GListModel of the visible range during a
+ * frame cycle, so that it cannot change underneath us. We hold()
+ * and release() the snapshot from frame clock callbacks.
+ */
+ GtkSourceListSnapshot *list_snapshot;
+ gulong update_handler;
+ gulong after_paint_handler;
+
/* The box containing the rows. */
GtkBox *box;
@@ -42,11 +51,6 @@ struct _GtkSourceCompletionListBox
/* The completion context that is being displayed. */
GtkSourceCompletionContext *context;
- /* The handler for GtkSourceCompletionContext::items-chaged which should
- * be disconnected when no longer needed.
- */
- gulong items_changed_handler;
-
/* The number of rows we expect to have visible to the user. */
guint n_rows;
@@ -116,12 +120,21 @@ enum {
N_SIGNALS
};
-static void gtk_source_completion_list_box_queue_update (GtkSourceCompletionListBox *self);
-static gboolean gtk_source_completion_list_box_update_cb (GtkWidget *widget,
- GdkFrameClock *frame_clock,
- gpointer user_data);
-static void gtk_source_completion_list_box_do_update (GtkSourceCompletionListBox *self,
- gboolean update_selection);
+static void gtk_source_completion_list_box_queue_update (GtkSourceCompletionListBox *self);
+static gboolean gtk_source_completion_list_box_update_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer user_data);
+static void gtk_source_completion_list_box_after_update_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock);
+static void gtk_source_completion_list_box_after_paint_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock);
+static void gtk_source_completion_list_box_do_update (GtkSourceCompletionListBox *self,
+ gboolean update_selection);
+static void gtk_source_completion_list_box_items_changed_cb (GtkSourceCompletionListBox *self,
+ guint position,
+ guint removed,
+ guint added,
+ GListModel *model);
G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionListBox, gtk_source_completion_list_box, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
@@ -548,6 +561,42 @@ _gtk_source_completion_list_box_key_pressed_cb (GtkSourceCompletionListBox *self
}
static void
+gtk_source_completion_list_box_realize (GtkWidget *widget)
+{
+ GtkSourceCompletionListBox *self = GTK_SOURCE_COMPLETION_LIST_BOX (widget);
+ GdkFrameClock *frame_clock;
+
+ GTK_WIDGET_CLASS (gtk_source_completion_list_box_parent_class)->realize (widget);
+
+ frame_clock = gtk_widget_get_frame_clock (widget);
+
+ self->update_handler =
+ g_signal_connect_data (frame_clock,
+ "update",
+ G_CALLBACK (gtk_source_completion_list_box_after_update_cb),
+ self, NULL,
+ G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+ self->after_paint_handler =
+ g_signal_connect_swapped (frame_clock,
+ "after-paint",
+ G_CALLBACK (gtk_source_completion_list_box_after_paint_cb),
+ self);
+}
+
+static void
+gtk_source_completion_list_box_unrealize (GtkWidget *widget)
+{
+ GtkSourceCompletionListBox *self = GTK_SOURCE_COMPLETION_LIST_BOX (widget);
+ GdkFrameClock *frame_clock;
+
+ frame_clock = gtk_widget_get_frame_clock (widget);
+ g_clear_signal_handler (&self->update_handler, frame_clock);
+ g_clear_signal_handler (&self->after_paint_handler, frame_clock);
+
+ GTK_WIDGET_CLASS (gtk_source_completion_list_box_parent_class)->unrealize (widget);
+}
+
+static void
gtk_source_completion_list_box_constructed (GObject *object)
{
GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)object;
@@ -582,6 +631,7 @@ gtk_source_completion_list_box_dispose (GObject *object)
self->box = NULL;
}
+ g_clear_object (&self->list_snapshot);
g_clear_object (&self->before_size_group);
g_clear_object (&self->typed_text_size_group);
g_clear_object (&self->after_size_group);
@@ -693,6 +743,9 @@ gtk_source_completion_list_box_class_init (GtkSourceCompletionListBoxClass *klas
object_class->get_property = gtk_source_completion_list_box_get_property;
object_class->set_property = gtk_source_completion_list_box_set_property;
+ widget_class->realize = gtk_source_completion_list_box_realize;
+ widget_class->unrealize = gtk_source_completion_list_box_unrealize;
+
properties [PROP_ALTERNATE] =
g_param_spec_int ("alternate",
"Alternate",
@@ -804,6 +857,13 @@ gtk_source_completion_list_box_init (GtkSourceCompletionListBox *self)
self);
gtk_widget_add_controller (GTK_WIDGET (self), key);
+ self->list_snapshot = gtk_source_list_snapshot_new ();
+ g_signal_connect_object (self->list_snapshot,
+ "items-changed",
+ G_CALLBACK (gtk_source_completion_list_box_items_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
self->box = g_object_new (GTK_TYPE_BOX,
"orientation", GTK_ORIENTATION_VERTICAL,
"visible", TRUE,
@@ -1158,25 +1218,10 @@ _gtk_source_completion_list_box_set_context (GtkSourceCompletionListBox *self,
g_return_if_fail (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
g_return_if_fail (!context || GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
- if (self->context == context)
- return;
-
- if (self->context != NULL && self->items_changed_handler != 0)
- {
- g_signal_handler_disconnect (self->context, self->items_changed_handler);
- self->items_changed_handler = 0;
- }
-
- g_set_object (&self->context, context);
-
- if (self->context != NULL)
+ if (g_set_object (&self->context, context))
{
- self->items_changed_handler =
- g_signal_connect_object (self->context,
- "items-changed",
- G_CALLBACK (gtk_source_completion_list_box_items_changed_cb),
- self,
- G_CONNECT_SWAPPED);
+ gtk_source_list_snapshot_set_model (self->list_snapshot,
+ G_LIST_MODEL (context));
}
gtk_source_completion_list_box_set_selected (self, -1);
@@ -1340,3 +1385,32 @@ _gtk_source_completion_list_box_set_show_icons (GtkSourceCompletionListBox *self
gtk_source_completion_list_box_queue_update (self);
}
+
+static void
+gtk_source_completion_list_box_after_update_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock)
+{
+ GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)widget;
+ double lower;
+ double page_size;
+
+ g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+ g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
+
+ lower = gtk_adjustment_get_lower (self->vadjustment);
+ page_size = gtk_adjustment_get_page_size (self->vadjustment);
+
+ gtk_source_list_snapshot_hold (self->list_snapshot, lower, lower + page_size);
+}
+
+static void
+gtk_source_completion_list_box_after_paint_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock)
+{
+ GtkSourceCompletionListBox *self = (GtkSourceCompletionListBox *)widget;
+
+ g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX (self));
+ g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
+
+ gtk_source_list_snapshot_release (self->list_snapshot);
+}