[gtksourceview/wip/chergert/hoverers] try to port this to better gtk 4 apis



commit 8362dce3ce4258c7bc7639e8b6c9ad7dd53f15f3
Author: Christian Hergert <chergert redhat com>
Date:   Tue Mar 9 23:16:16 2021 -0800

    try to port this to better gtk 4 apis

 gtksourceview/gtksourcehover.c                  | 365 ++++--------------------
 gtksourceview/gtksourcehoverassistant-private.h |  12 +-
 gtksourceview/gtksourcehoverassistant.c         | 204 ++++++++++++-
 gtksourceview/gtksourcehovercontext.c           |  37 ++-
 gtksourceview/gtksourcehoverdisplay-private.h   |  30 ++
 gtksourceview/gtksourcehoverdisplay.c           |  29 +-
 6 files changed, 351 insertions(+), 326 deletions(-)
---
diff --git a/gtksourceview/gtksourcehover.c b/gtksourceview/gtksourcehover.c
index a75c0e96..2b5e8abd 100644
--- a/gtksourceview/gtksourcehover.c
+++ b/gtksourceview/gtksourcehover.c
@@ -44,150 +44,14 @@ struct _GtkSourceHover
 
        GPtrArray          *providers;
 
-       gdouble             motion_x;
-       gdouble             motion_y;
+       double              motion_x;
+       double              motion_y;
 
-       guint               state;
-
-       guint               delay_display_source;
-       guint               dismiss_source;
-};
-
-enum {
-       HOVER_STATE_INITIAL,
-       HOVER_STATE_DISPLAY,
-       HOVER_STATE_IN_POPOVER,
+       guint               settle_source;
 };
 
 G_DEFINE_TYPE (GtkSourceHover, gtk_source_hover, G_TYPE_OBJECT)
 
-static gboolean
-gtk_source_hover_dismiss_cb (gpointer data)
-{
-       GtkSourceHover *self = data;
-
-       g_assert (GTK_SOURCE_IS_HOVER (self));
-
-       g_print ("Hover dismiss\n");
-
-       self->dismiss_source = 0;
-
-       switch (self->state) {
-       case HOVER_STATE_DISPLAY:
-               g_assert (GTK_SOURCE_IS_HOVER_ASSISTANT (self->assistant));
-
-               gtk_widget_hide (GTK_WIDGET (self->assistant));
-
-               g_assert (self->state == HOVER_STATE_INITIAL);
-               g_assert (self->assistant == NULL);
-
-       break;
-
-       case HOVER_STATE_INITIAL:
-       case HOVER_STATE_IN_POPOVER:
-       default:
-               g_clear_handle_id (&self->delay_display_source, g_source_remove);
-       break;
-       }
-
-       return G_SOURCE_REMOVE;
-}
-
-static void
-gtk_source_hover_queue_dismiss (GtkSourceHover *self)
-{
-       g_assert (GTK_SOURCE_IS_HOVER (self));
-
-       g_clear_handle_id (&self->dismiss_source, g_source_remove);
-       self->dismiss_source = g_timeout_add (DISMISS_DELAY_MSEC,
-                                             gtk_source_hover_dismiss_cb,
-                                             self);
-}
-
-static void
-on_assistant_motion_cb (GtkSourceHover           *self,
-                        double                    x,
-                        double                    y,
-                        GtkEventControllerMotion *controller)
-{
-       GtkAllocation alloc;
-       GdkSurface *surface;
-       GtkRoot *toplevel;
-       double abs_x, abs_y;
-       double popup_x, popup_y;
-
-       g_assert (GTK_SOURCE_IS_HOVER (self));
-       g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (controller));
-
-       if (self->assistant == NULL || self->view == NULL)
-       {
-               return;
-       }
-
-       toplevel = gtk_widget_get_root (GTK_WIDGET (self->view));
-       surface = gtk_native_get_surface (GTK_NATIVE (self->assistant));
-
-       /* To translate, because we are crossing surfaces, we need to
-        * take the position of the popup relative to the surface of
-        * the parent view.
-        */
-       popup_x = gdk_popup_get_position_x (GDK_POPUP (surface));
-       popup_y = gdk_popup_get_position_y (GDK_POPUP (surface));
-       gtk_widget_get_allocation (GTK_WIDGET (self->assistant), &alloc);
-       gtk_widget_translate_coordinates (GTK_WIDGET (self->view),
-                                         GTK_WIDGET (toplevel),
-                                         x, y, &abs_x, &abs_y);
-
-       if (abs_x < (popup_x - GRACE_X) ||
-           abs_x > (popup_x + alloc.width + GRACE_X) ||
-           abs_y < (popup_y - GRACE_Y) ||
-           abs_y > (popup_y + alloc.height + GRACE_Y))
-       {
-               gtk_event_controller_reset (GTK_EVENT_CONTROLLER (controller));
-               gtk_widget_hide (GTK_WIDGET (self->assistant));
-
-               g_assert (self->assistant == NULL);
-               g_assert (self->state == HOVER_STATE_INITIAL);
-       }
-
-       g_clear_handle_id (&self->dismiss_source, g_source_remove);
-       g_clear_handle_id (&self->delay_display_source, g_source_remove);
-}
-
-static gboolean
-on_key_pressed_cb (GtkSourceHover        *self,
-                   guint                  keyval,
-                   guint                  keycode,
-                   GdkModifierType        state,
-                   GtkEventControllerKey *controller)
-{
-       g_assert (GTK_SOURCE_IS_HOVER (self));
-       g_assert (GTK_IS_EVENT_CONTROLLER_KEY (controller));
-
-       return GDK_EVENT_PROPAGATE;
-}
-
-static void
-on_motion_enter_cb (GtkSourceHover           *self,
-                    double                    x,
-                    double                    y,
-                    GtkEventControllerMotion *controller)
-{
-       g_assert (GTK_SOURCE_IS_HOVER (self));
-       g_assert (GTK_SOURCE_IS_VIEW (self->view));
-       g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (controller));
-
-}
-
-static void
-on_motion_leave_cb (GtkSourceHover          *self,
-                    GtkEventControllerMotion *controller)
-{
-       g_assert (GTK_SOURCE_IS_HOVER (self));
-       g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (controller));
-
-}
-
 static gboolean
 gtk_source_hover_get_bounds (GtkSourceHover *self,
                              GtkTextIter    *begin,
@@ -240,176 +104,72 @@ gtk_source_hover_get_bounds (GtkSourceHover *self,
 
        if (!_gtk_source_iter_starts_full_word (&iter))
        {
-               _gtk_source_iter_backward_full_word_start (&iter);
+               _gtk_source_iter_backward_extra_natural_word_start (&iter);
        }
 
        *begin = iter;
        *end = iter;
 
-       _gtk_source_iter_forward_full_word_end (end);
+       _gtk_source_iter_forward_extra_natural_word_end (end);
 
        return TRUE;
 }
 
-static void
-on_assistant_closed_cb (GtkSourceHover          *self,
-                        GtkSourceHoverAssistant *assistant)
-{
-       g_assert (GTK_SOURCE_IS_HOVER (self));
-       g_assert (GTK_SOURCE_IS_HOVER_ASSISTANT (assistant));
-
-       self->state = HOVER_STATE_INITIAL;
-
-       g_clear_pointer (&self->assistant, _gtk_source_assistant_destroy);
-
-       g_clear_handle_id (&self->dismiss_source, g_source_remove);
-       g_clear_handle_id (&self->delay_display_source, g_source_remove);
-
-       g_assert (self->assistant == NULL);
-       g_assert (self->state == HOVER_STATE_INITIAL);
-       g_assert (self->dismiss_source == 0);
-       g_assert (self->delay_display_source == 0);
-}
-
 static gboolean
-on_assistant_motion_enter_cb (GtkSourceHover           *self,
-                              double                    x,
-                              double                    y,
-                              GtkEventControllerMotion *motion)
+gtk_source_hover_settled_cb (GtkSourceHover *self)
 {
+       GtkTextIter begin;
+       GtkTextIter end;
+       GtkTextIter location;
+
        g_assert (GTK_SOURCE_IS_HOVER (self));
-       g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
 
-       g_print ("Motion enter\n");
+       self->settle_source = 0;
 
-       /* Possible with DnD dragging? */
-       if (self->state != HOVER_STATE_DISPLAY)
+       if (gtk_source_hover_get_bounds (self, &begin, &end, &location))
        {
-               return GDK_EVENT_PROPAGATE;
+               _gtk_source_hover_assistant_display (GTK_SOURCE_HOVER_ASSISTANT (self->assistant),
+                                                    (GtkSourceHoverProvider **)self->providers->pdata,
+                                                    self->providers->len,
+                                                    &begin, &end, &location);
        }
 
-       self->state = HOVER_STATE_IN_POPOVER;
-       g_clear_handle_id (&self->dismiss_source, g_source_remove);
-
-       return GDK_EVENT_PROPAGATE;
+       return G_SOURCE_REMOVE;
 }
 
-static gboolean
-on_assistant_motion_leave_cb (GtkSourceHover           *self,
-                              GtkEventControllerMotion *motion)
+static void
+gtk_source_hover_queue_settle (GtkSourceHover *self)
 {
        g_assert (GTK_SOURCE_IS_HOVER (self));
-       g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
-
-       if (self->state == HOVER_STATE_IN_POPOVER)
-       {
-               self->state = HOVER_STATE_DISPLAY;
-       }
 
-       gtk_source_hover_queue_dismiss (self);
-
-       return GDK_EVENT_PROPAGATE;
+       g_clear_handle_id (&self->settle_source, g_source_remove);
+       self->settle_source = g_timeout_add (MOTION_SETTLE_TIMEOUT_MSEC,
+                                            (GSourceFunc) gtk_source_hover_settled_cb,
+                                            self);
 }
 
 static gboolean
-gtk_source_hover_motion_timeout_cb (gpointer data)
+gtk_source_hover_key_pressed_cb (GtkSourceHover        *self,
+                                 guint                  keyval,
+                                 guint                  keycode,
+                                 GdkModifierType        state,
+                                 GtkEventControllerKey *controller)
 {
-       GtkSourceHover *self = data;
-       GdkRectangle rect;
-       GdkRectangle begin_rect;
-       GdkRectangle end_rect;
-       GdkRectangle location_rect;
-       GtkTextIter begin;
-       GtkTextIter end;
-       GtkTextIter location;
-
        g_assert (GTK_SOURCE_IS_HOVER (self));
+       g_assert (GTK_IS_EVENT_CONTROLLER_KEY (controller));
 
-       g_print ("Motion timeout\n");
-
-       self->delay_display_source = 0;
-
-       if (self->view == NULL ||
-           self->state != HOVER_STATE_INITIAL ||
-           !gtk_source_hover_get_bounds (self, &begin, &end, &location))
-       {
-               return G_SOURCE_REMOVE;
-       }
-
-       g_assert (GTK_SOURCE_IS_VIEW (self->view));
-
-       if (self->assistant == NULL)
-       {
-               GtkEventController *motion;
-
-               self->assistant = _gtk_source_hover_assistant_new ();
-
-               gtk_popover_set_position (GTK_POPOVER (self->assistant), GTK_POS_TOP);
-               gtk_popover_set_autohide (GTK_POPOVER (self->assistant), TRUE);
-
-               g_signal_connect_object (self->assistant,
-                                        "closed",
-                                        G_CALLBACK (on_assistant_closed_cb),
-                                        self,
-                                        G_CONNECT_SWAPPED);
-
-               motion = gtk_event_controller_motion_new ();
-               gtk_event_controller_set_propagation_phase (motion, GTK_PHASE_CAPTURE);
-               g_signal_connect_object (motion,
-                                        "enter",
-                                        G_CALLBACK (on_assistant_motion_enter_cb),
-                                        self,
-                                        G_CONNECT_SWAPPED);
-               g_signal_connect_object (motion,
-                                        "motion",
-                                        G_CALLBACK (on_assistant_motion_cb),
-                                        self,
-                                        G_CONNECT_SWAPPED);
-               g_signal_connect_object (motion,
-                                        "leave",
-                                        G_CALLBACK (on_assistant_motion_leave_cb),
-                                        self,
-                                        G_CONNECT_SWAPPED);
-               gtk_widget_add_controller (GTK_WIDGET (self->assistant), motion);
-
-               _gtk_source_view_add_assistant (self->view, self->assistant);
-       }
-
-       self->state = HOVER_STATE_DISPLAY;
-
-       gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self->view), &begin, &begin_rect);
-       gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self->view), &end, &end_rect);
-       gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self->view), &location, &location_rect);
-       gdk_rectangle_union (&begin_rect, &end_rect, &rect);
-
-       gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (self->view),
-                                              GTK_TEXT_WINDOW_WIDGET,
-                                              rect.x, rect.y, &rect.x, &rect.y);
-
-       _gtk_source_hover_assistant_set_hovered_at (GTK_SOURCE_HOVER_ASSISTANT (self->assistant),
-                                                   &location_rect);
-
-       if (gtk_text_iter_equal (&begin, &end) &&
-           gtk_text_iter_starts_line (&begin))
-       {
-               rect.width = 1;
-               gtk_popover_set_position (GTK_POPOVER (self->assistant), GTK_POS_RIGHT);
-       }
-       else
-       {
-               gtk_popover_set_position (GTK_POPOVER (self->assistant), GTK_POS_TOP);
-       }
-
-       gtk_widget_show (GTK_WIDGET (self->assistant));
+       g_clear_handle_id (&self->settle_source, g_source_remove);
+       _gtk_source_hover_assistant_dismiss (GTK_SOURCE_HOVER_ASSISTANT (self->assistant));
 
-       return G_SOURCE_REMOVE;
+       return GDK_EVENT_PROPAGATE;
 }
 
+
 static void
-on_motion_cb (GtkSourceHover           *self,
-              double                    x,
-              double                    y,
-              GtkEventControllerMotion *controller)
+gtk_source_hover_motion_cb (GtkSourceHover           *self,
+                            double                    x,
+                            double                    y,
+                            GtkEventControllerMotion *controller)
 {
        g_assert (GTK_SOURCE_IS_HOVER (self));
        g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (controller));
@@ -417,26 +177,30 @@ on_motion_cb (GtkSourceHover           *self,
        self->motion_x = x;
        self->motion_y = y;
 
-       g_clear_handle_id (&self->dismiss_source, g_source_remove);
-       g_clear_handle_id (&self->delay_display_source, g_source_remove);
+       gtk_source_hover_queue_settle (self);
+}
 
-       g_print ("Motion cb\n");
+static void
+gtk_source_hover_leave_cb (GtkSourceHover           *self,
+                           GtkEventControllerMotion *controller)
+{
+       g_assert (GTK_SOURCE_IS_HOVER (self));
+       g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (controller));
 
-       self->delay_display_source = g_timeout_add (MOTION_SETTLE_TIMEOUT_MSEC,
-                                                   gtk_source_hover_motion_timeout_cb,
-                                                   self);
+       g_clear_handle_id (&self->settle_source, g_source_remove);
 }
 
 static gboolean
-on_scroll_cb (GtkSourceHover           *self,
-              double                    dx,
-              double                    dy,
-              GtkEventControllerScroll *controller)
+gtk_source_hover_scroll_cb (GtkSourceHover           *self,
+                            double                    dx,
+                            double                    dy,
+                            GtkEventControllerScroll *controller)
 {
        g_assert (GTK_SOURCE_IS_HOVER (self));
        g_assert (GTK_IS_EVENT_CONTROLLER_SCROLL (controller));
 
-       g_clear_pointer (&self->assistant, _gtk_source_assistant_destroy);
+       g_clear_handle_id (&self->settle_source, g_source_remove);
+       _gtk_source_hover_assistant_dismiss (GTK_SOURCE_HOVER_ASSISTANT (self->assistant));
 
        return GDK_EVENT_PROPAGATE;
 }
@@ -446,13 +210,10 @@ gtk_source_hover_dispose (GObject *object)
 {
        GtkSourceHover *self = (GtkSourceHover *)object;
 
+       g_clear_handle_id (&self->settle_source, g_source_remove);
        g_clear_pointer (&self->assistant, _gtk_source_assistant_destroy);
-
        g_clear_weak_pointer (&self->view);
 
-       g_clear_handle_id (&self->delay_display_source, g_source_remove);
-       g_clear_handle_id (&self->dismiss_source, g_source_remove);
-
        if (self->providers->len > 0)
        {
                g_ptr_array_remove_range (self->providers, 0, self->providers->len);
@@ -468,8 +229,8 @@ gtk_source_hover_finalize (GObject *object)
 
        g_clear_pointer (&self->providers, g_ptr_array_unref);
 
-       g_assert (self->delay_display_source == 0);
-       g_assert (self->dismiss_source == 0);
+       g_assert (self->assistant == NULL);
+       g_assert (self->settle_source == 0);
 
        G_OBJECT_CLASS (gtk_source_hover_parent_class)->finalize (object);
 }
@@ -501,30 +262,26 @@ _gtk_source_hover_new (GtkSourceView *view)
 
        self = g_object_new (GTK_SOURCE_TYPE_HOVER, NULL);
        g_set_weak_pointer (&self->view, view);
+       self->assistant = _gtk_source_hover_assistant_new ();
+       _gtk_source_view_add_assistant (view, self->assistant);
 
        key = gtk_event_controller_key_new ();
        g_signal_connect_object (key,
                                 "key-pressed",
-                                G_CALLBACK (on_key_pressed_cb),
+                                G_CALLBACK (gtk_source_hover_key_pressed_cb),
                                 self,
                                 G_CONNECT_SWAPPED);
        gtk_widget_add_controller (GTK_WIDGET (view), key);
 
        motion = gtk_event_controller_motion_new ();
-       gtk_event_controller_set_propagation_phase (motion, GTK_PHASE_CAPTURE);
-       g_signal_connect_object (motion,
-                                "enter",
-                                G_CALLBACK (on_motion_enter_cb),
-                                self,
-                                G_CONNECT_SWAPPED);
        g_signal_connect_object (motion,
                                 "leave",
-                                G_CALLBACK (on_motion_leave_cb),
+                                G_CALLBACK (gtk_source_hover_leave_cb),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (motion,
                                 "motion",
-                                G_CALLBACK (on_motion_cb),
+                                G_CALLBACK (gtk_source_hover_motion_cb),
                                 self,
                                 G_CONNECT_SWAPPED);
        gtk_widget_add_controller (GTK_WIDGET (view), motion);
@@ -532,7 +289,7 @@ _gtk_source_hover_new (GtkSourceView *view)
        scroll = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
        g_signal_connect_object (scroll,
                                 "scroll",
-                                G_CALLBACK (on_scroll_cb),
+                                G_CALLBACK (gtk_source_hover_scroll_cb),
                                 self,
                                 G_CONNECT_SWAPPED);
        gtk_widget_add_controller (GTK_WIDGET (view), scroll);
diff --git a/gtksourceview/gtksourcehoverassistant-private.h b/gtksourceview/gtksourcehoverassistant-private.h
index 5ee90c94..7925014a 100644
--- a/gtksourceview/gtksourcehoverassistant-private.h
+++ b/gtksourceview/gtksourcehoverassistant-private.h
@@ -31,9 +31,13 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GtkSourceHoverAssistant, gtk_source_hover_assistant, GTK_SOURCE, HOVER_ASSISTANT, 
GtkSourceAssistant)
 
-GtkSourceAssistant    *_gtk_source_hover_assistant_new            (void);
-GtkSourceHoverDisplay *_gtk_source_hover_assistant_get_display    (GtkSourceHoverAssistant *self);
-void                   _gtk_source_hover_assistant_set_hovered_at (GtkSourceHoverAssistant *self,
-                                                                   const GdkRectangle      *rect);
+GtkSourceAssistant *_gtk_source_hover_assistant_new     (void);
+void                _gtk_source_hover_assistant_dismiss (GtkSourceHoverAssistant  *self);
+void                _gtk_source_hover_assistant_display (GtkSourceHoverAssistant  *self,
+                                                         GtkSourceHoverProvider  **providers,
+                                                         guint                     n_providers,
+                                                         const GtkTextIter        *begin,
+                                                         const GtkTextIter        *end,
+                                                         const GtkTextIter        *location);
 
 G_END_DECLS
diff --git a/gtksourceview/gtksourcehoverassistant.c b/gtksourceview/gtksourcehoverassistant.c
index e54cb06a..e183734d 100644
--- a/gtksourceview/gtksourcehoverassistant.c
+++ b/gtksourceview/gtksourcehoverassistant.c
@@ -23,12 +23,18 @@
 
 #include "gtksourceassistant-private.h"
 #include "gtksourcehoverassistant-private.h"
-#include "gtksourcehoverdisplay.h"
+#include "gtksourcehovercontext-private.h"
+#include "gtksourcehoverdisplay-private.h"
+#include "gtksourceview.h"
+
+#define GRACE_X 20
+#define GRACE_Y 20
 
 struct _GtkSourceHoverAssistant
 {
        GtkSourceAssistant parent_instance;
-  GtkSourceHoverDisplay *display;
+       GtkSourceHoverDisplay *display;
+       GCancellable *cancellable;
        GdkRectangle hovered_at;
 };
 
@@ -41,10 +47,72 @@ gtk_source_hover_assistant_get_target_location (GtkSourceAssistant *assistant,
        *rect = GTK_SOURCE_HOVER_ASSISTANT (assistant)->hovered_at;
 }
 
+static void
+gtk_source_hover_assistant_motion_cb (GtkSourceHoverAssistant  *self,
+                                      double                    x,
+                                      double                    y,
+                                      GtkEventControllerMotion *controller)
+{
+       GdkSurface *assistant_surface;
+       GtkWidget *parent;
+       GtkRoot *root;
+       double tx, ty;
+       int width, height;
+
+       g_assert (GTK_SOURCE_IS_HOVER_ASSISTANT (self));
+       g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (controller));
+
+       if (gtk_event_controller_motion_contains_pointer (controller))
+       {
+               return;
+       }
+
+       if (!(parent = gtk_widget_get_parent (GTK_WIDGET (self))) ||
+           !(root = gtk_widget_get_root (parent)) ||
+           !(assistant_surface = gtk_native_get_surface (GTK_NATIVE (self))))
+       {
+               return;
+       }
+
+       gtk_widget_translate_coordinates (parent, GTK_WIDGET (root), x, y, &x, &y);
+       x -= gdk_popup_get_position_x (GDK_POPUP (assistant_surface));
+       y -= gdk_popup_get_position_y (GDK_POPUP (assistant_surface));
+
+       gtk_native_get_surface_transform (GTK_NATIVE (root), &tx, &ty);
+       x += tx;
+       y += ty;
+
+       width = gdk_surface_get_width (assistant_surface);
+       height = gdk_surface_get_height (assistant_surface);
+
+       if (x < -GRACE_X ||
+           x > width + GRACE_Y ||
+           y < -GRACE_Y ||
+           y > height + GRACE_Y)
+       {
+               gtk_widget_hide (GTK_WIDGET (self));
+       }
+}
+
+static void
+gtk_source_hover_assistant_dispose (GObject *object)
+{
+       GtkSourceHoverAssistant *self = (GtkSourceHoverAssistant *)object;
+
+       self->display = NULL;
+
+       g_clear_object (&self->cancellable);
+
+       G_OBJECT_CLASS (gtk_source_hover_assistant_parent_class)->dispose (object);
+}
+
 static void
 gtk_source_hover_assistant_class_init (GtkSourceHoverAssistantClass *klass)
 {
        GtkSourceAssistantClass *assistant_class = GTK_SOURCE_ASSISTANT_CLASS (klass);
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->dispose = gtk_source_hover_assistant_dispose;
 
        assistant_class->get_target_location = gtk_source_hover_assistant_get_target_location;
 }
@@ -52,8 +120,24 @@ gtk_source_hover_assistant_class_init (GtkSourceHoverAssistantClass *klass)
 static void
 gtk_source_hover_assistant_init (GtkSourceHoverAssistant *self)
 {
-       self->display = g_object_new (GTK_SOURCE_TYPE_HOVER_DISPLAY, NULL);
+       GtkEventController *motion;
+
+       gtk_popover_set_autohide (GTK_POPOVER (self), TRUE);
+
+       self->display = g_object_new (GTK_SOURCE_TYPE_HOVER_DISPLAY,
+                                     "width-request", 100,
+                                     "height-request", 48,
+                                     NULL);
        _gtk_source_assistant_set_child (GTK_SOURCE_ASSISTANT (self), GTK_WIDGET (self->display));
+
+       motion = gtk_event_controller_motion_new ();
+       gtk_event_controller_set_propagation_phase (motion, GTK_PHASE_CAPTURE);
+       g_signal_connect_object (motion,
+                                "motion",
+                                G_CALLBACK (gtk_source_hover_assistant_motion_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+       gtk_widget_add_controller (GTK_WIDGET (self), motion);
 }
 
 GtkSourceAssistant *
@@ -62,20 +146,118 @@ _gtk_source_hover_assistant_new (void)
        return g_object_new (GTK_SOURCE_TYPE_HOVER_ASSISTANT, NULL);
 }
 
+static void
+gtk_source_hover_assistant_populate_cb (GObject      *object,
+                                        GAsyncResult *result,
+                                        gpointer      user_data)
+{
+       GtkSourceHoverContext *context = (GtkSourceHoverContext *)object;
+       GtkSourceHoverAssistant *self = user_data;
+       GError *error = NULL;
+
+       g_assert (GTK_SOURCE_IS_HOVER_CONTEXT (context));
+       g_assert (G_IS_ASYNC_RESULT (result));
+       g_assert (GTK_SOURCE_IS_HOVER_ASSISTANT (self));
+
+       if (_gtk_source_hover_context_populate_finish (context, result, &error))
+       {
+               gtk_widget_show (GTK_WIDGET (self));
+       }
+
+       g_clear_object (&self);
+       g_clear_error (&error);
+}
+
 void
-_gtk_source_hover_assistant_set_hovered_at (GtkSourceHoverAssistant *self,
-                                            const GdkRectangle      *hovered_at)
+_gtk_source_hover_assistant_display (GtkSourceHoverAssistant  *self,
+                                     GtkSourceHoverProvider  **providers,
+                                     guint                     n_providers,
+                                     const GtkTextIter        *begin,
+                                     const GtkTextIter        *end,
+                                     const GtkTextIter        *location)
 {
+       GtkSourceHoverContext *context;
+       GtkSourceView *view;
+       GdkRectangle begin_rect;
+       GdkRectangle end_rect;
+       GdkRectangle location_rect;
+
        g_return_if_fail (GTK_SOURCE_IS_HOVER_ASSISTANT (self));
-       g_return_if_fail (hovered_at != NULL);
+       g_return_if_fail (n_providers == 0 || providers != NULL);
+       g_return_if_fail (begin != NULL);
+       g_return_if_fail (end != NULL);
+       g_return_if_fail (location != NULL);
+
+       if (n_providers == 0)
+       {
+               if (gtk_widget_get_visible (GTK_WIDGET (self)))
+               {
+                       gtk_widget_hide (GTK_WIDGET (self));
+               }
+
+               return;
+       }
+
+       if (self->cancellable != NULL)
+       {
+               g_cancellable_cancel (self->cancellable);
+               g_clear_object (&self->cancellable);
+       }
+
+       view = GTK_SOURCE_VIEW (gtk_widget_get_parent (GTK_WIDGET (self)));
+
+       gtk_text_view_get_iter_location (GTK_TEXT_VIEW (view), begin, &begin_rect);
+       gtk_text_view_get_iter_location (GTK_TEXT_VIEW (view), end, &end_rect);
+       gtk_text_view_get_iter_location (GTK_TEXT_VIEW (view), location, &location_rect);
 
-       self->hovered_at = *hovered_at;
+       gdk_rectangle_union (&begin_rect, &end_rect, &location_rect);
+
+       gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (view),
+                                              GTK_TEXT_WINDOW_WIDGET,
+                                              location_rect.x,
+                                              location_rect.y,
+                                              &location_rect.x,
+                                              &location_rect.y);
+
+       if (gtk_text_iter_equal (begin, end) &&
+           gtk_text_iter_starts_line (begin))
+       {
+               location_rect.width = 1;
+               gtk_popover_set_position (GTK_POPOVER (self), GTK_POS_RIGHT);
+       }
+       else
+       {
+               gtk_popover_set_position (GTK_POPOVER (self), GTK_POS_TOP);
+       }
+
+       self->hovered_at = location_rect;
+
+       context = _gtk_source_hover_context_new (view, begin, end, location);
+
+       for (guint i = 0; i < n_providers; i++)
+       {
+               _gtk_source_hover_context_add_provider (context, providers[i]);
+       }
+
+       self->cancellable = g_cancellable_new ();
+
+       _gtk_source_hover_display_clear (self->display);
+
+       _gtk_source_hover_context_populate_async (context,
+                                                 self->display,
+                                                 self->cancellable,
+                                                 gtk_source_hover_assistant_populate_cb,
+                                                 g_object_ref (self));
+
+       g_object_unref (context);
 }
 
-GtkSourceHoverDisplay *
-_gtk_source_hover_assistant_get_display (GtkSourceHoverAssistant *self)
+void
+_gtk_source_hover_assistant_dismiss (GtkSourceHoverAssistant *self)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_HOVER_ASSISTANT (self), NULL);
+       g_return_if_fail (GTK_SOURCE_IS_HOVER_ASSISTANT (self));
 
-       return self->display;
+       g_cancellable_cancel (self->cancellable);
+       gtk_widget_hide (GTK_WIDGET (self));
+       _gtk_source_hover_display_clear (self->display);
 }
diff --git a/gtksourceview/gtksourcehovercontext.c b/gtksourceview/gtksourcehovercontext.c
index 97d5990b..e00d6e12 100644
--- a/gtksourceview/gtksourcehovercontext.c
+++ b/gtksourceview/gtksourcehovercontext.c
@@ -152,11 +152,27 @@ gtk_source_hover_context_get_buffer (GtkSourceHoverContext *self)
        return GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->view)));
 }
 
+static GtkTextMark *
+create_mark (GtkSourceHoverContext *self,
+             const GtkTextIter     *iter,
+             gboolean               left_gravity)
+{
+       GtkTextMark *mark;
+       GtkTextBuffer *buffer;
+
+       g_assert (GTK_SOURCE_IS_HOVER_CONTEXT (self));
+
+       buffer = GTK_TEXT_BUFFER (self->buffer);
+       mark = gtk_text_buffer_create_mark (buffer, NULL, iter, left_gravity);
+
+       return g_object_ref (mark);
+}
+
 GtkSourceHoverContext *
 _gtk_source_hover_context_new (GtkSourceView     *view,
-                              const GtkTextIter *begin,
-                              const GtkTextIter *end,
-                              const GtkTextIter *location)
+                               const GtkTextIter *begin,
+                               const GtkTextIter *end,
+                               const GtkTextIter *location)
 {
        GtkSourceHoverContext *self;
        GtkSourceBuffer *buffer;
@@ -167,11 +183,15 @@ _gtk_source_hover_context_new (GtkSourceView     *view,
        g_return_val_if_fail (location != NULL, NULL);
 
        buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
-       self = g_object_new (GTK_SOURCE_TYPE_VIEW, NULL);
+       self = g_object_new (GTK_SOURCE_TYPE_HOVER_CONTEXT, NULL);
 
        g_set_weak_pointer (&self->view, view);
        g_set_weak_pointer (&self->buffer, buffer);
 
+       self->begin = create_mark (self, begin, TRUE);
+       self->end = create_mark (self, end, FALSE);
+       self->location = create_mark (self, location, FALSE);
+
        return self;
 }
 
@@ -231,7 +251,14 @@ _gtk_source_hover_context_populate_async (GtkSourceHoverContext *self,
        g_task_set_source_tag (task, _gtk_source_hover_context_populate_async);
        g_task_set_task_data (task, state, g_free);
 
-       if (g_task_return_error_if_cancelled (task))
+       if (self->view == NULL || self->buffer == NULL)
+       {
+               g_task_return_new_error (task,
+                                        G_IO_ERROR,
+                                        G_IO_ERROR_CANCELLED,
+                                        "Cannot populate, view destroyed");
+       }
+       else if (g_task_return_error_if_cancelled (task))
        {
                /* Do nothing */
        }
diff --git a/gtksourceview/gtksourcehoverdisplay-private.h b/gtksourceview/gtksourcehoverdisplay-private.h
new file mode 100644
index 00000000..3ee9fd08
--- /dev/null
+++ b/gtksourceview/gtksourcehoverdisplay-private.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include "gtksourcehoverdisplay.h"
+
+G_BEGIN_DECLS
+
+void _gtk_source_hover_display_clear (GtkSourceHoverDisplay *self);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcehoverdisplay.c b/gtksourceview/gtksourcehoverdisplay.c
index c48ac56e..37201911 100644
--- a/gtksourceview/gtksourcehoverdisplay.c
+++ b/gtksourceview/gtksourcehoverdisplay.c
@@ -21,7 +21,7 @@
 
 #include "config.h"
 
-#include "gtksourcehoverdisplay.h"
+#include "gtksourcehoverdisplay-private.h"
 
 struct _GtkSourceHoverDisplay
 {
@@ -56,7 +56,9 @@ gtk_source_hover_display_class_init (GtkSourceHoverDisplayClass *klass)
 static void
 gtk_source_hover_display_init (GtkSourceHoverDisplay *self)
 {
-       self->vbox = g_object_new (GTK_TYPE_BOX, NULL);
+       self->vbox = g_object_new (GTK_TYPE_BOX,
+                                  "orientation", GTK_ORIENTATION_VERTICAL,
+                                  NULL);
        gtk_widget_set_parent (GTK_WIDGET (self->vbox), GTK_WIDGET (self));
 }
 
@@ -66,6 +68,8 @@ gtk_source_hover_display_append (GtkSourceHoverDisplay *self,
 {
        g_return_if_fail (GTK_SOURCE_IS_HOVER_DISPLAY (self));
        g_return_if_fail (GTK_IS_WIDGET (child));
+
+       gtk_box_append (self->vbox, child);
 }
 
 void
@@ -74,6 +78,8 @@ gtk_source_hover_display_prepend (GtkSourceHoverDisplay *self,
 {
        g_return_if_fail (GTK_SOURCE_IS_HOVER_DISPLAY (self));
        g_return_if_fail (GTK_IS_WIDGET (child));
+
+       gtk_box_prepend (self->vbox, child);
 }
 
 void
@@ -89,6 +95,10 @@ gtk_source_hover_display_insert_after (GtkSourceHoverDisplay *self,
        {
                gtk_source_hover_display_append (self, child);
        }
+       else
+       {
+               gtk_box_insert_child_after (self->vbox, child, sibling);
+       }
 }
 
 void
@@ -98,4 +108,19 @@ gtk_source_hover_display_remove (GtkSourceHoverDisplay *self,
        g_return_if_fail (GTK_SOURCE_IS_HOVER_DISPLAY (self));
        g_return_if_fail (GTK_IS_WIDGET (child));
        g_return_if_fail (gtk_widget_get_parent (child) == (GtkWidget *)self->vbox);
+
+       gtk_box_remove (self->vbox, child);
+}
+
+void
+_gtk_source_hover_display_clear (GtkSourceHoverDisplay *self)
+{
+       GtkWidget *child;
+
+       g_return_if_fail (GTK_SOURCE_IS_HOVER_DISPLAY (self));
+
+       while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->vbox))))
+       {
+               gtk_box_remove (self->vbox, child);
+       }
 }


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