[gnome-builder] libide/sourceview: add prioritized controller override



commit a74a45efbfce1c0442195f503bf3e9d362e4238b
Author: Christian Hergert <chergert redhat com>
Date:   Tue Sep 6 11:44:26 2022 -0700

    libide/sourceview: add prioritized controller override
    
    This allows us to have plugins insert their controllers in more predictable
    ordering which is necessary for things like Vim to work with our own
    extensions like "insert-matching-brace" or "overwrite-braces".

 src/libide/sourceview/ide-source-view-private.h |  3 +
 src/libide/sourceview/ide-source-view.c         | 99 ++++++++++++++++++++++++-
 src/libide/sourceview/ide-source-view.h         |  7 ++
 3 files changed, 105 insertions(+), 4 deletions(-)
---
diff --git a/src/libide/sourceview/ide-source-view-private.h b/src/libide/sourceview/ide-source-view-private.h
index a47ffaaa6..860924b3c 100644
--- a/src/libide/sourceview/ide-source-view-private.h
+++ b/src/libide/sourceview/ide-source-view-private.h
@@ -63,6 +63,9 @@ struct _IdeSourceView
   IdeExtensionSetAdapter *hover_providers;
   IdeExtensionAdapter *indenter;
 
+  /* Prioritized controllers to be reapplied as necessary */
+  GArray *controllers;
+
   /* GSource used to update bottom margin */
   guint overscroll_source;
 
diff --git a/src/libide/sourceview/ide-source-view.c b/src/libide/sourceview/ide-source-view.c
index 335090c09..107dd3ab2 100644
--- a/src/libide/sourceview/ide-source-view.c
+++ b/src/libide/sourceview/ide-source-view.c
@@ -27,6 +27,12 @@
 
 #include "ide-source-view-private.h"
 
+typedef struct
+{
+  GtkEventController *controller;
+  int priority;
+} Controller;
+
 G_DEFINE_TYPE (IdeSourceView, ide_source_view, GTK_SOURCE_TYPE_VIEW)
 
 enum {
@@ -51,6 +57,13 @@ enum {
 static GParamSpec *properties[N_PROPS];
 static guint signals[N_SIGNALS];
 
+static void
+controller_clear (gpointer data)
+{
+  Controller *c = data;
+  g_clear_object (&c->controller);
+}
+
 char *
 _ide_source_view_generate_css (GtkSourceView              *view,
                                const PangoFontDescription *font_desc,
@@ -1024,6 +1037,7 @@ ide_source_view_dispose (GObject *object)
   g_clear_object (&self->joined_menu);
   g_clear_object (&self->css_provider);
   g_clear_pointer (&self->font_desc, pango_font_description_free);
+  g_clear_pointer (&self->controllers, g_array_unref);
   g_clear_pointer ((GtkWidget **)&self->popup_menu, gtk_widget_unparent);
 
   g_assert (self->completion_providers == NULL);
@@ -1228,6 +1242,9 @@ ide_source_view_init (IdeSourceView *self)
   GtkEventController *scroll;
   GtkEventController *key;
 
+  self->controllers = g_array_new (FALSE, FALSE, sizeof (Controller));
+  g_array_set_clear_func (self->controllers, controller_clear);
+
   g_signal_connect (self,
                     "notify::buffer",
                     G_CALLBACK (ide_source_view_notify_buffer_cb),
@@ -1250,7 +1267,7 @@ ide_source_view_init (IdeSourceView *self)
                             "pressed",
                             G_CALLBACK (ide_source_view_click_pressed_cb),
                             self);
-  gtk_widget_add_controller (GTK_WIDGET (self), click);
+  ide_source_view_add_controller (self, 0, click);
 
   /* Key handler for internal features like insert-matching-braces */
   key = gtk_event_controller_key_new ();
@@ -1260,7 +1277,7 @@ ide_source_view_init (IdeSourceView *self)
                             "key-pressed",
                             G_CALLBACK (ide_source_view_key_pressed_cb),
                             self);
-  gtk_widget_add_controller (GTK_WIDGET (self), key);
+  ide_source_view_add_controller (self, 0, key);
 
   /* Setup focus tracking */
   focus = gtk_event_controller_focus_new ();
@@ -1273,7 +1290,7 @@ ide_source_view_init (IdeSourceView *self)
                             "leave",
                             G_CALLBACK (ide_source_view_focus_leave_cb),
                             self);
-  gtk_widget_add_controller (GTK_WIDGET (self), focus);
+  ide_source_view_add_controller (self, 0, focus);
 
   /* Setup ctrl+scroll zoom */
   scroll = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL);
@@ -1283,7 +1300,7 @@ ide_source_view_init (IdeSourceView *self)
                     "scroll",
                     G_CALLBACK (on_scroll_scrolled_cb),
                     self);
-  gtk_widget_add_controller (GTK_WIDGET (self), scroll);
+  ide_source_view_add_controller (self, 0, scroll);
 
   /* This is sort of a layer vioaltion, but it's helpful for us to
    * get the system font name and manage it invisibly.
@@ -1687,3 +1704,77 @@ ide_source_view_set_overwrite_braces (IdeSourceView *self,
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_OVERWRITE_BRACES]);
     }
 }
+
+static int
+sort_by_priority (gconstpointer a,
+                  gconstpointer b)
+{
+  const Controller *ca = a;
+  const Controller *cb = b;
+
+  if (ca->priority < cb->priority)
+    return -1;
+  else if (ca->priority > cb->priority)
+    return 1;
+  else
+    return 0;
+}
+
+/**
+ * ide_source_view_add_controller:
+ * @self: a #IdeSourceView
+ * @priority: the sort priority
+ * @controller: (transfer full): a #GtkEventController
+ *
+ * Adds a controller with a priority so that capture/bubble can be
+ * applied in a known order.
+ */
+void
+ide_source_view_add_controller (IdeSourceView      *self,
+                                int                 priority,
+                                GtkEventController *controller)
+{
+  Controller to_add = { controller, priority };
+
+  g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+  g_return_if_fail (GTK_IS_EVENT_CONTROLLER (controller));
+
+  for (guint i = 0; i < self->controllers->len; i++)
+    {
+      const Controller *c = &g_array_index (self->controllers, Controller, i);
+      gtk_widget_remove_controller (GTK_WIDGET (self), c->controller);
+    }
+
+  g_array_append_val (self->controllers, to_add);
+  g_array_sort (self->controllers, sort_by_priority);
+
+  for (guint i = 0; i < self->controllers->len; i++)
+    {
+      const Controller *c = &g_array_index (self->controllers, Controller, i);
+      gtk_widget_add_controller (GTK_WIDGET (self), g_object_ref (c->controller));
+    }
+}
+
+void
+ide_source_view_remove_controller (IdeSourceView      *self,
+                                   GtkEventController *controller)
+{
+  g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+  g_return_if_fail (GTK_IS_EVENT_CONTROLLER (controller));
+
+  gtk_widget_remove_controller (GTK_WIDGET (self), controller);
+
+  if (self->controllers == NULL)
+    return;
+
+  for (guint i = 0; i < self->controllers->len; i++)
+    {
+      const Controller *c = &g_array_index (self->controllers, Controller, i);
+
+      if (c->controller == controller)
+        {
+          g_array_remove_index (self->controllers, i);
+          break;
+        }
+    }
+}
diff --git a/src/libide/sourceview/ide-source-view.h b/src/libide/sourceview/ide-source-view.h
index a1ab5342c..afab81370 100644
--- a/src/libide/sourceview/ide-source-view.h
+++ b/src/libide/sourceview/ide-source-view.h
@@ -38,6 +38,13 @@ G_DECLARE_FINAL_TYPE (IdeSourceView, ide_source_view, IDE, SOURCE_VIEW, GtkSourc
 IDE_AVAILABLE_IN_ALL
 GtkWidget                  *ide_source_view_new                         (void);
 IDE_AVAILABLE_IN_ALL
+void                        ide_source_view_add_controller              (IdeSourceView              *self,
+                                                                         int                         
priority,
+                                                                         GtkEventController         
*controller);
+IDE_AVAILABLE_IN_ALL
+void                        ide_source_view_remove_controller           (IdeSourceView              *self,
+                                                                         GtkEventController         
*controller);
+IDE_AVAILABLE_IN_ALL
 void                        ide_source_view_scroll_to_insert            (IdeSourceView              *self);
 IDE_AVAILABLE_IN_ALL
 char                       *ide_source_view_dup_position_label          (IdeSourceView              *self);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]