[gtksourceview: 2/3] hover: add GtkSourceHover support
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview: 2/3] hover: add GtkSourceHover support
- Date: Wed, 10 Mar 2021 21:04:55 +0000 (UTC)
commit a074ac96297d5172294f09ddc63752bc49610f83
Author: Christian Hergert <chergert redhat com>
Date: Mon Mar 8 16:13:14 2021 -0800
hover: add GtkSourceHover support
This adds support for a GtkSourceHover, GtkSourceHoverProvider,
GtkSourceHoverContext, and GtkSourceHoverDisplay objects. These can
be used to provide interactive tooltips to code similar to those
used by various editors integrating with language servers.
Applications can register GtkSourceHoverProviders which can populate the
contents of a GtkSourceHoverDisplay by adding widgets to it from their
synchronous populate, or asynchronous populate_async() callbacks.
docs/reference/gtksourceview-5.0-sections.txt | 66 ++++
docs/reference/gtksourceview-docs.xml.in | 8 +
gtksourceview/gtksource.h | 4 +
gtksourceview/gtksourcehover-private.h | 30 ++
gtksourceview/gtksourcehover.c | 415 ++++++++++++++++++++++++
gtksourceview/gtksourcehover.h | 46 +++
gtksourceview/gtksourcehoverassistant-private.h | 43 +++
gtksourceview/gtksourcehoverassistant.c | 285 ++++++++++++++++
gtksourceview/gtksourcehovercontext-private.h | 44 +++
gtksourceview/gtksourcehovercontext.c | 351 ++++++++++++++++++++
gtksourceview/gtksourcehovercontext.h | 51 +++
gtksourceview/gtksourcehoverdisplay-private.h | 30 ++
gtksourceview/gtksourcehoverdisplay.c | 142 ++++++++
gtksourceview/gtksourcehoverdisplay.h | 53 +++
gtksourceview/gtksourcehoverprovider.c | 120 +++++++
gtksourceview/gtksourcehoverprovider.h | 70 ++++
gtksourceview/gtksourcetypes.h | 4 +
gtksourceview/gtksourceview.c | 33 ++
gtksourceview/gtksourceview.h | 2 +
gtksourceview/meson.build | 9 +
tests/test-widget.c | 75 ++++-
tests/test-widget.ui | 11 +
22 files changed, 1890 insertions(+), 2 deletions(-)
---
diff --git a/docs/reference/gtksourceview-5.0-sections.txt b/docs/reference/gtksourceview-5.0-sections.txt
index 0c6727e6..3ea1e2e4 100644
--- a/docs/reference/gtksourceview-5.0-sections.txt
+++ b/docs/reference/gtksourceview-5.0-sections.txt
@@ -434,6 +434,72 @@ GTK_SOURCE_TYPE_GUTTER_RENDERER_TEXT
gtk_source_gutter_renderer_text_get_type
</SECTION>
+<SECTION>
+<FILE>hover</FILE>
+GtkSourceHover
+<SUBSECTION>
+gtk_source_hover_add_provider
+gtk_source_hover_remove_provider
+<SUBSECTION Standard>
+GTK_SOURCE_IS_HOVER
+GTK_SOURCE_IS_HOVER_CLASS
+GTK_SOURCE_HOVER
+GTK_SOURCE_HOVER_CLASS
+GTK_SOURCE_HOVER_GET_CLASS
+GTK_SOURCE_TYPE_HOVER
+gtk_source_hover_get_type
+</SECTION>
+
+<SECTION>
+<FILE>hovercontext</FILE>
+GtkSourceHoverContext
+<SUBSECTION>
+gtk_source_hover_context_get_bounds
+gtk_source_hover_context_get_buffer
+gtk_source_hover_context_get_iter
+gtk_source_hover_context_get_view
+<SUBSECTION Standard>
+GTK_SOURCE_IS_HOVER_CONTEXT
+GTK_SOURCE_IS_HOVER_CONTEXT_CLASS
+GTK_SOURCE_HOVER_CONTEXT
+GTK_SOURCE_HOVER_CONTEXT_CLASS
+GTK_SOURCE_HOVER_CONTEXT_GET_CLASS
+GTK_SOURCE_TYPE_HOVER_CONTEXT
+gtk_source_hover_context_get_type
+</SECTION>
+
+<SECTION>
+<FILE>hoverdisplay</FILE>
+GtkSourceHoverDisplay
+<SUBSECTION>
+gtk_source_hover_display_append
+gtk_source_hover_display_prepend
+gtk_source_hover_display_insert_after
+gtk_source_hover_display_remove
+<SUBSECTION Standard>
+GTK_SOURCE_IS_HOVER_DISPLAY
+GTK_SOURCE_IS_HOVER_DISPLAY_CLASS
+GTK_SOURCE_HOVER_DISPLAY
+GTK_SOURCE_HOVER_DISPLAY_CLASS
+GTK_SOURCE_HOVER_DISPLAY_GET_CLASS
+GTK_SOURCE_TYPE_HOVER_DISPLAY
+gtk_source_hover_display_get_type
+</SECTION>
+
+<SECTION>
+<FILE>hoverprovider</FILE>
+GtkSourceHoverProvider
+GtkSourceHoverProviderInterface
+gtk_source_hover_provider_populate_async
+gtk_source_hover_provider_populate_finish
+<SUBSECTION Standard>
+GTK_SOURCE_IS_HOVER_PROVIDER
+GTK_SOURCE_HOVER_PROVIDER
+GTK_SOURCE_HOVER_PROVIDER_GET_IFACE
+GTK_SOURCE_TYPE_HOVER_PROVIDER
+gtk_source_hover_provider_get_type
+</SECTION>
+
<SECTION>
<FILE>init</FILE>
<TITLE>GtkSourceView Initialization and Finalization</TITLE>
diff --git a/docs/reference/gtksourceview-docs.xml.in b/docs/reference/gtksourceview-docs.xml.in
index e6be26f4..067e936d 100644
--- a/docs/reference/gtksourceview-docs.xml.in
+++ b/docs/reference/gtksourceview-docs.xml.in
@@ -71,6 +71,14 @@
<xi:include href="xml/markattributes.xml"/>
</chapter>
+ <chapter id="hover">
+ <title>Interactive Tooltips</title>
+ <xi:include href="xml/hover.xml"/>
+ <xi:include href="xml/hovercontext.xml"/>
+ <xi:include href="xml/hoverdisplay.xml"/>
+ <xi:include href="xml/hoverprovider.xml"/>
+ </chapter>
+
<chapter id="printing">
<title>Printing</title>
<xi:include href="xml/printcompositor.xml"/>
diff --git a/gtksourceview/gtksource.h b/gtksourceview/gtksource.h
index 31942337..45820372 100644
--- a/gtksourceview/gtksource.h
+++ b/gtksourceview/gtksource.h
@@ -34,6 +34,10 @@
#include "gtksourcegutterrenderer.h"
#include "gtksourcegutterrenderertext.h"
#include "gtksourcegutterrendererpixbuf.h"
+#include "gtksourcehover.h"
+#include "gtksourcehovercontext.h"
+#include "gtksourcehoverdisplay.h"
+#include "gtksourcehoverprovider.h"
#include "gtksourceinit.h"
#include "gtksourcelanguage.h"
#include "gtksourcelanguagemanager.h"
diff --git a/gtksourceview/gtksourcehover-private.h b/gtksourceview/gtksourcehover-private.h
new file mode 100644
index 00000000..ad13eddb
--- /dev/null
+++ b/gtksourceview/gtksourcehover-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 "gtksourcehover.h"
+
+G_BEGIN_DECLS
+
+GtkSourceHover *_gtk_source_hover_new (GtkSourceView *view);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcehover.c b/gtksourceview/gtksourcehover.c
new file mode 100644
index 00000000..7cebc680
--- /dev/null
+++ b/gtksourceview/gtksourcehover.c
@@ -0,0 +1,415 @@
+/*
+ * 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
+ */
+
+#include "config.h"
+
+#include "gtksourceassistant-private.h"
+#include "gtksourcehover-private.h"
+#include "gtksourcehoverassistant-private.h"
+#include "gtksourcehovercontext.h"
+#include "gtksourcehoverprovider.h"
+#include "gtksourceiter-private.h"
+#include "gtksourcesignalgroup-private.h"
+#include "gtksourceview-private.h"
+
+#define DEFAULT_HOVER_DELAY 500
+
+/**
+ * SECTION:hover
+ * @Title: GtkSourceHover
+ * @Short_description: Interactive tooltips
+ * @See_also: #GtkSourceHoverProvider, #GtkSourceHoverDisplay, and
+ * #GtkSourceHoverContext.
+ *
+ * #GtkSourceHover allows a #GtkSourceView to provide contextual information.
+ * When enabled, if the user hovers over a word in the text editor, a series
+ * of registered #GtkSourceHoverProvider can populate a #GtkSourceHoverDisplay
+ * with useful information.
+ *
+ * To enable call gtk_source_view_get_hover() and add #GtkSourceHoverProvider
+ * using gtk_source_hover_add_provider(). To disable, remove all registered
+ * providers with gtk_source_hover_remove_provider().
+ *
+ * You can change how long to wait to display the interactive tooltip by
+ * setting the #GtkSourceHover:hover-delay property in milliseconds.
+ *
+ * Since: 5.0
+ */
+
+struct _GtkSourceHover
+{
+ GObject parent_instance;
+
+ GtkSourceView *view;
+ GtkSourceAssistant *assistant;
+
+ GPtrArray *providers;
+
+ double motion_x;
+ double motion_y;
+
+ guint hover_delay;
+ guint settle_source;
+};
+
+G_DEFINE_TYPE (GtkSourceHover, gtk_source_hover, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_HOVER_DELAY,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+gtk_source_hover_get_bounds (GtkSourceHover *self,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ GtkTextIter *location)
+{
+ GtkTextIter iter;
+ int x, y;
+
+ g_assert (GTK_SOURCE_IS_HOVER (self));
+ g_assert (!self->view || GTK_SOURCE_IS_VIEW (self->view));
+ g_assert (begin != NULL);
+ g_assert (end != NULL);
+ g_assert (location != NULL);
+
+ memset (begin, 0, sizeof *begin);
+ memset (end, 0, sizeof *end);
+ memset (location, 0, sizeof *location);
+
+ if (self->view == NULL)
+ {
+ return FALSE;
+ }
+
+ g_assert (GTK_SOURCE_IS_VIEW (self->view));
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (self->view),
+ GTK_TEXT_WINDOW_WIDGET,
+ self->motion_x,
+ self->motion_y,
+ &x, &y);
+
+ if (!gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (self->view), &iter, x, y))
+ {
+ return FALSE;
+ }
+
+ *location = iter;
+
+ if (!_gtk_source_iter_inside_word (&iter))
+ {
+ *begin = iter;
+ gtk_text_iter_set_line_offset (begin, 0);
+
+ *end = iter;
+ gtk_text_iter_forward_to_line_end (end);
+
+ return TRUE;
+ }
+
+ if (!_gtk_source_iter_starts_full_word (&iter))
+ {
+ _gtk_source_iter_backward_extra_natural_word_start (&iter);
+ }
+
+ *begin = iter;
+ *end = iter;
+
+ _gtk_source_iter_forward_extra_natural_word_end (end);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_source_hover_settled_cb (GtkSourceHover *self)
+{
+ GtkTextIter begin;
+ GtkTextIter end;
+ GtkTextIter location;
+
+ g_assert (GTK_SOURCE_IS_HOVER (self));
+
+ self->settle_source = 0;
+
+ if (gtk_source_hover_get_bounds (self, &begin, &end, &location))
+ {
+ _gtk_source_hover_assistant_display (GTK_SOURCE_HOVER_ASSISTANT (self->assistant),
+ (GtkSourceHoverProvider **)self->providers->pdata,
+ self->providers->len,
+ &begin, &end, &location);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+gtk_source_hover_queue_settle (GtkSourceHover *self)
+{
+ g_assert (GTK_SOURCE_IS_HOVER (self));
+
+ g_clear_handle_id (&self->settle_source, g_source_remove);
+ self->settle_source = g_timeout_add (self->hover_delay,
+ (GSourceFunc) gtk_source_hover_settled_cb,
+ self);
+}
+
+static gboolean
+gtk_source_hover_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));
+
+ 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;
+}
+
+
+static void
+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));
+
+ self->motion_x = x;
+ self->motion_y = y;
+
+ gtk_source_hover_queue_settle (self);
+}
+
+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));
+
+ g_clear_handle_id (&self->settle_source, g_source_remove);
+}
+
+static gboolean
+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_handle_id (&self->settle_source, g_source_remove);
+ _gtk_source_hover_assistant_dismiss (GTK_SOURCE_HOVER_ASSISTANT (self->assistant));
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+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);
+
+ if (self->providers->len > 0)
+ {
+ g_ptr_array_remove_range (self->providers, 0, self->providers->len);
+ }
+
+ G_OBJECT_CLASS (gtk_source_hover_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_hover_finalize (GObject *object)
+{
+ GtkSourceHover *self = (GtkSourceHover *)object;
+
+ g_clear_pointer (&self->providers, g_ptr_array_unref);
+
+ g_assert (self->assistant == NULL);
+ g_assert (self->settle_source == 0);
+
+ G_OBJECT_CLASS (gtk_source_hover_parent_class)->finalize (object);
+}
+
+static void
+gtk_source_hover_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSourceHover *self = GTK_SOURCE_HOVER (object);
+
+ switch (prop_id) {
+ case PROP_HOVER_DELAY:
+ g_value_set_uint (value, self->hover_delay);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_source_hover_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSourceHover *self = GTK_SOURCE_HOVER (object);
+
+ switch (prop_id) {
+ case PROP_HOVER_DELAY:
+ self->hover_delay = g_value_get_uint (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_source_hover_class_init (GtkSourceHoverClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gtk_source_hover_dispose;
+ object_class->finalize = gtk_source_hover_finalize;
+ object_class->get_property = gtk_source_hover_get_property;
+ object_class->set_property = gtk_source_hover_set_property;
+
+ /**
+ * GtkSourceHover:hover-delay:
+ *
+ * The "hover-delay" property contains the number of milliseconds to
+ * delay before showing the hover assistant.
+ */
+ properties [PROP_HOVER_DELAY] =
+ g_param_spec_uint ("hover-delay",
+ "Hover Delay",
+ "Number of milliseconds to delay before showing assistant",
+ 1, 5000, DEFAULT_HOVER_DELAY,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gtk_source_hover_init (GtkSourceHover *self)
+{
+ self->providers = g_ptr_array_new_with_free_func (g_object_unref);
+ self->hover_delay = DEFAULT_HOVER_DELAY;
+}
+
+GtkSourceHover *
+_gtk_source_hover_new (GtkSourceView *view)
+{
+ GtkSourceHover *self;
+ GtkEventController *key;
+ GtkEventController *motion;
+ GtkEventController *scroll;
+
+ g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
+
+ 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 (gtk_source_hover_key_pressed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ gtk_widget_add_controller (GTK_WIDGET (view), key);
+
+ motion = gtk_event_controller_motion_new ();
+ g_signal_connect_object (motion,
+ "leave",
+ G_CALLBACK (gtk_source_hover_leave_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (motion,
+ "motion",
+ G_CALLBACK (gtk_source_hover_motion_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ gtk_widget_add_controller (GTK_WIDGET (view), motion);
+
+ scroll = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
+ g_signal_connect_object (scroll,
+ "scroll",
+ G_CALLBACK (gtk_source_hover_scroll_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ gtk_widget_add_controller (GTK_WIDGET (view), scroll);
+
+ return self;
+}
+
+void
+gtk_source_hover_add_provider (GtkSourceHover *self,
+ GtkSourceHoverProvider *provider)
+{
+ g_return_if_fail (GTK_SOURCE_IS_HOVER (self));
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_PROVIDER (provider));
+
+ for (guint i = 0; i < self->providers->len; i++)
+ {
+ if (g_ptr_array_index (self->providers, i) == (gpointer)provider)
+ {
+ return;
+ }
+ }
+
+ g_ptr_array_add (self->providers, g_object_ref (provider));
+}
+
+void
+gtk_source_hover_remove_provider (GtkSourceHover *self,
+ GtkSourceHoverProvider *provider)
+{
+ g_return_if_fail (GTK_SOURCE_IS_HOVER (self));
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_PROVIDER (provider));
+
+ for (guint i = 0; i < self->providers->len; i++)
+ {
+ if (g_ptr_array_index (self->providers, i) == (gpointer)provider)
+ {
+ g_ptr_array_remove_index (self->providers, i);
+ return;
+ }
+ }
+}
diff --git a/gtksourceview/gtksourcehover.h b/gtksourceview/gtksourcehover.h
new file mode 100644
index 00000000..56d94b78
--- /dev/null
+++ b/gtksourceview/gtksourcehover.h
@@ -0,0 +1,46 @@
+/*
+ * 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
+
+#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
+# error "Only <gtksourceview/gtksource.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+#include "gtksourcetypes.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_HOVER (gtk_source_hover_get_type())
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+G_DECLARE_FINAL_TYPE (GtkSourceHover, gtk_source_hover, GTK_SOURCE, HOVER, GObject)
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+void gtk_source_hover_add_provider (GtkSourceHover *self,
+ GtkSourceHoverProvider *provider);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void gtk_source_hover_remove_provider (GtkSourceHover *self,
+ GtkSourceHoverProvider *provider);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcehoverassistant-private.h b/gtksourceview/gtksourcehoverassistant-private.h
new file mode 100644
index 00000000..7925014a
--- /dev/null
+++ b/gtksourceview/gtksourcehoverassistant-private.h
@@ -0,0 +1,43 @@
+/*
+ * 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 "gtksourcetypes.h"
+
+#include "gtksourceassistant-private.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_HOVER_ASSISTANT (gtk_source_hover_assistant_get_type())
+
+G_DECLARE_FINAL_TYPE (GtkSourceHoverAssistant, gtk_source_hover_assistant, GTK_SOURCE, HOVER_ASSISTANT,
GtkSourceAssistant)
+
+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
new file mode 100644
index 00000000..3d9cbf3e
--- /dev/null
+++ b/gtksourceview/gtksourcehoverassistant.c
@@ -0,0 +1,285 @@
+/*
+ * 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
+ */
+
+#include "config.h"
+
+#include "gtksourceassistant-private.h"
+#include "gtksourcehoverassistant-private.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;
+ GCancellable *cancellable;
+ GdkRectangle hovered_at;
+};
+
+G_DEFINE_TYPE (GtkSourceHoverAssistant, gtk_source_hover_assistant, GTK_SOURCE_TYPE_ASSISTANT)
+
+static void
+gtk_source_hover_assistant_get_target_location (GtkSourceAssistant *assistant,
+ GdkRectangle *rect)
+{
+ *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 gboolean
+gtk_source_hover_assistant_scroll_cb (GtkSourceHoverAssistant *self,
+ double dx,
+ double dy,
+ GtkEventControllerScroll *controller)
+{
+ g_assert (GTK_SOURCE_IS_HOVER_ASSISTANT (self));
+ g_assert (GTK_IS_EVENT_CONTROLLER_SCROLL (controller));
+
+ gtk_widget_hide (GTK_WIDGET (self));
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+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;
+}
+
+static void
+gtk_source_hover_assistant_init (GtkSourceHoverAssistant *self)
+{
+ GtkEventController *motion;
+ GtkEventController *scroll;
+
+ gtk_widget_add_css_class (GTK_WIDGET (self), "hover-assistant");
+
+ gtk_popover_set_autohide (GTK_POPOVER (self), TRUE);
+
+ self->display = g_object_new (GTK_SOURCE_TYPE_HOVER_DISPLAY, 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);
+
+ scroll = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
+ g_signal_connect_object (scroll,
+ "scroll",
+ G_CALLBACK (gtk_source_hover_assistant_scroll_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ gtk_widget_add_controller (GTK_WIDGET (self), scroll);
+}
+
+GtkSourceAssistant *
+_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_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 (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);
+
+ 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);
+}
+
+void
+_gtk_source_hover_assistant_dismiss (GtkSourceHoverAssistant *self)
+{
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_ASSISTANT (self));
+
+ g_cancellable_cancel (self->cancellable);
+ gtk_widget_hide (GTK_WIDGET (self));
+ _gtk_source_hover_display_clear (self->display);
+}
diff --git a/gtksourceview/gtksourcehovercontext-private.h b/gtksourceview/gtksourcehovercontext-private.h
new file mode 100644
index 00000000..3e19b69c
--- /dev/null
+++ b/gtksourceview/gtksourcehovercontext-private.h
@@ -0,0 +1,44 @@
+/*
+ * 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 "gtksourcehovercontext.h"
+
+G_BEGIN_DECLS
+
+GtkSourceHoverContext *_gtk_source_hover_context_new (GtkSourceView *view,
+ const GtkTextIter *begin,
+ const GtkTextIter *end,
+ const GtkTextIter *location);
+void _gtk_source_hover_context_add_provider (GtkSourceHoverContext *self,
+ GtkSourceHoverProvider *provider);
+void _gtk_source_hover_context_populate_async (GtkSourceHoverContext *self,
+ GtkSourceHoverDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean _gtk_source_hover_context_populate_finish (GtkSourceHoverContext *self,
+ GAsyncResult *result,
+ GError **error);
+
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcehovercontext.c b/gtksourceview/gtksourcehovercontext.c
new file mode 100644
index 00000000..a122b26e
--- /dev/null
+++ b/gtksourceview/gtksourcehovercontext.c
@@ -0,0 +1,351 @@
+/*
+ * 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
+ */
+
+#include "config.h"
+
+#include "gtksourcebuffer.h"
+#include "gtksourcehovercontext-private.h"
+#include "gtksourcehoverdisplay.h"
+#include "gtksourcehoverprovider.h"
+#include "gtksourceview.h"
+
+/**
+ * SECTION:hovercontext
+ * @Title: GtkSourceHoverContext
+ * @Short_description: Context for populating #GtkSourceHoverDisplay contents
+ * @See_also: #GtkSourceHover, #GtkSourceHoverProvider, #GtkSourceHoverDisplay
+ *
+ * #GtkSourceHoverContext contains information about the request to populate
+ * contents for a #GtkSourceHoverDisplay.
+ *
+ * It can be used to retrieve the #GtkSourceView, #GtkSourceBuffer, and
+ * #GtkTextIter for the regions of text which are being displayed.
+ *
+ * Use gtk_source_hover_context_get_bounds() to get the word that was
+ * requested. gtk_source_hover_contents_get_iter() will get you the location
+ * of the pointer when the request was made.
+ *
+ * Since: 5.0
+ */
+
+struct _GtkSourceHoverContext
+{
+ GObject parent_instance;
+
+ GtkSourceView *view;
+ GtkSourceBuffer *buffer;
+
+ GPtrArray *providers;
+
+ GtkTextMark *begin;
+ GtkTextMark *end;
+ GtkTextMark *location;
+};
+
+G_DEFINE_TYPE (GtkSourceHoverContext, gtk_source_hover_context, G_TYPE_OBJECT)
+
+typedef struct
+{
+ guint n_active;
+} Populate;
+
+static void
+gtk_source_hover_context_dispose (GObject *object)
+{
+ GtkSourceHoverContext *self = (GtkSourceHoverContext *)object;
+
+ if (self->buffer != NULL)
+ {
+ if (self->begin != NULL)
+ gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (self->buffer), self->begin);
+
+ if (self->end != NULL)
+ gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (self->buffer), self->end);
+
+ if (self->location != NULL)
+ gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (self->buffer), self->location);
+ }
+
+ g_clear_object (&self->begin);
+ g_clear_object (&self->end);
+ g_clear_object (&self->location);
+
+ if (self->providers->len > 0)
+ {
+ g_ptr_array_remove_range (self->providers, 0, self->providers->len);
+ }
+
+ g_clear_weak_pointer (&self->buffer);
+ g_clear_weak_pointer (&self->view);
+
+ G_OBJECT_CLASS (gtk_source_hover_context_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_hover_context_finalize (GObject *object)
+{
+ GtkSourceHoverContext *self = (GtkSourceHoverContext *)object;
+
+ g_clear_pointer (&self->providers, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (gtk_source_hover_context_parent_class)->finalize (object);
+}
+
+static void
+gtk_source_hover_context_class_init (GtkSourceHoverContextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gtk_source_hover_context_dispose;
+ object_class->finalize = gtk_source_hover_context_finalize;
+}
+
+static void
+gtk_source_hover_context_init (GtkSourceHoverContext *self)
+{
+ self->providers = g_ptr_array_new_with_free_func (g_object_unref);
+}
+
+void
+_gtk_source_hover_context_add_provider (GtkSourceHoverContext *self,
+ GtkSourceHoverProvider *provider)
+{
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_CONTEXT (self));
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_PROVIDER (provider));
+
+ for (guint i = 0; i < self->providers->len; i++)
+ {
+ if (g_ptr_array_index (self->providers, i) == (gpointer)provider)
+ {
+ return;
+ }
+ }
+
+ g_ptr_array_add (self->providers, g_object_ref (provider));
+}
+
+/**
+ * gtk_source_hover_context_get_view:
+ *
+ * Returns: (transfer none): the #GtkSourceView that owns the context
+ */
+GtkSourceView *
+gtk_source_hover_context_get_view (GtkSourceHoverContext *self)
+{
+ g_return_val_if_fail (GTK_SOURCE_IS_HOVER_CONTEXT (self), NULL);
+
+ return self->view;
+}
+
+/**
+ * gtk_source_hover_context_get_buffer:
+ *
+ * A convenience function to get the buffer.
+ *
+ * Returns: (transfer none): The #GtkSourceBuffer for the view
+ */
+GtkSourceBuffer *
+gtk_source_hover_context_get_buffer (GtkSourceHoverContext *self)
+{
+ g_return_val_if_fail (GTK_SOURCE_IS_HOVER_CONTEXT (self), NULL);
+ g_return_val_if_fail (self->view != NULL, NULL);
+
+ 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)
+{
+ GtkSourceHoverContext *self;
+ GtkSourceBuffer *buffer;
+
+ g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
+ g_return_val_if_fail (begin != NULL, NULL);
+ g_return_val_if_fail (end != NULL, NULL);
+ 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_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;
+}
+
+static void
+gtk_source_hover_context_populate_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GtkSourceHoverProvider *provider = (GtkSourceHoverProvider *)object;
+ GTask *task = user_data;
+ Populate *state;
+ GError *error = NULL;
+
+ g_assert (GTK_SOURCE_IS_HOVER_PROVIDER (provider));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ state = g_task_get_task_data (task);
+
+ if (!gtk_source_hover_provider_populate_finish (provider, result, &error))
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+ {
+ g_debug ("%s population failed", error->message);
+ }
+
+ g_clear_error (&error);
+ }
+
+ if (--state->n_active == 0)
+ {
+ g_task_return_boolean (task, TRUE);
+ }
+
+ g_object_unref (task);
+}
+
+void
+_gtk_source_hover_context_populate_async (GtkSourceHoverContext *self,
+ GtkSourceHoverDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Populate *state;
+ GTask *task;
+
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_CONTEXT (self));
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_DISPLAY (display));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ state = g_new0 (Populate, 1);
+ state->n_active = self->providers->len;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, _gtk_source_hover_context_populate_async);
+ g_task_set_task_data (task, state, g_free);
+
+ 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 */
+ }
+ else if (self->providers->len == 0)
+ {
+ g_task_return_boolean (task, TRUE);
+ }
+ else
+ {
+ for (guint i = 0; i < self->providers->len; i++)
+ {
+ GtkSourceHoverProvider *provider = g_ptr_array_index (self->providers, i);
+
+ g_assert (GTK_SOURCE_IS_HOVER_PROVIDER (provider));
+
+ gtk_source_hover_provider_populate_async (provider,
+ self,
+ display,
+ cancellable,
+ gtk_source_hover_context_populate_cb,
+ g_object_ref (task));
+ }
+ }
+
+ g_object_unref (task);
+}
+
+gboolean
+_gtk_source_hover_context_populate_finish (GtkSourceHoverContext *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_SOURCE_IS_HOVER_CONTEXT (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+gtk_source_hover_context_get_iter (GtkSourceHoverContext *self,
+ GtkTextIter *iter)
+{
+ g_return_val_if_fail (GTK_SOURCE_IS_HOVER_CONTEXT (self), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ if (self->buffer == NULL)
+ return FALSE;
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (self->buffer), iter, self->location);
+
+ return TRUE;
+}
+
+gboolean
+gtk_source_hover_context_get_bounds (GtkSourceHoverContext *self,
+ GtkTextIter *begin,
+ GtkTextIter *end)
+{
+ g_return_val_if_fail (GTK_SOURCE_IS_HOVER_CONTEXT (self), FALSE);
+
+ if (self->buffer == NULL)
+ return FALSE;
+
+ if (begin != NULL)
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (self->buffer), begin, self->begin);
+
+ if (end != NULL)
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (self->buffer), end, self->end);
+
+ return TRUE;
+}
diff --git a/gtksourceview/gtksourcehovercontext.h b/gtksourceview/gtksourcehovercontext.h
new file mode 100644
index 00000000..aee8f535
--- /dev/null
+++ b/gtksourceview/gtksourcehovercontext.h
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
+# error "Only <gtksourceview/gtksource.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+
+#include "gtksourcetypes.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_HOVER_CONTEXT (gtk_source_hover_context_get_type())
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+G_DECLARE_FINAL_TYPE (GtkSourceHoverContext, gtk_source_hover_context, GTK_SOURCE, HOVER_CONTEXT, GObject)
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceView *gtk_source_hover_context_get_view (GtkSourceHoverContext *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceBuffer *gtk_source_hover_context_get_buffer (GtkSourceHoverContext *self);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean gtk_source_hover_context_get_iter (GtkSourceHoverContext *self,
+ GtkTextIter *iter);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean gtk_source_hover_context_get_bounds (GtkSourceHoverContext *self,
+ GtkTextIter *begin,
+ GtkTextIter *end);
+
+G_END_DECLS
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
new file mode 100644
index 00000000..a68f40b5
--- /dev/null
+++ b/gtksourceview/gtksourcehoverdisplay.c
@@ -0,0 +1,142 @@
+/*
+ * 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
+ */
+
+#include "config.h"
+
+#include "gtksourcehoverdisplay-private.h"
+
+/**
+ * SECTION:hoverdisplay
+ * @Title: GtkSourceHoverDisplay
+ * @Short_description: display for interactive tooltips
+ * @See_also: #GtkSourceHoverProvider
+ *
+ * #GtkSourceHoverDisplay is a #GtkWidget that may be populated with widgets
+ * to be displayed to the user in interactive tooltips. The children widgets
+ * are packed vertically using a #GtkBox.
+ *
+ * Implement the #GtkSourceHoverProvider interface to be notified of when
+ * to populate a #GtkSourceHoverDisplay on behalf of the user.
+ *
+ * Since: 5.0
+ */
+
+struct _GtkSourceHoverDisplay
+{
+ GtkWidget parent_instance;
+
+ GtkBox *vbox;
+};
+
+G_DEFINE_TYPE (GtkSourceHoverDisplay, gtk_source_hover_display, GTK_TYPE_WIDGET)
+
+static void
+gtk_source_hover_display_dispose (GObject *object)
+{
+ GtkSourceHoverDisplay *self = (GtkSourceHoverDisplay *)object;
+
+ g_clear_pointer ((GtkWidget **)&self->vbox, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (gtk_source_hover_display_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_hover_display_class_init (GtkSourceHoverDisplayClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gtk_source_hover_display_dispose;
+
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+}
+
+static void
+gtk_source_hover_display_init (GtkSourceHoverDisplay *self)
+{
+ self->vbox = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ NULL);
+ gtk_widget_set_parent (GTK_WIDGET (self->vbox), GTK_WIDGET (self));
+}
+
+void
+gtk_source_hover_display_append (GtkSourceHoverDisplay *self,
+ GtkWidget *child)
+{
+ 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
+gtk_source_hover_display_prepend (GtkSourceHoverDisplay *self,
+ GtkWidget *child)
+{
+ 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
+gtk_source_hover_display_insert_after (GtkSourceHoverDisplay *self,
+ GtkWidget *child,
+ GtkWidget *sibling)
+{
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_DISPLAY (self));
+ g_return_if_fail (GTK_IS_WIDGET (child));
+ g_return_if_fail (!sibling || GTK_IS_WIDGET (sibling));
+
+ if (sibling == NULL)
+ {
+ gtk_source_hover_display_append (self, child);
+ }
+ else
+ {
+ gtk_box_insert_child_after (self->vbox, child, sibling);
+ }
+}
+
+void
+gtk_source_hover_display_remove (GtkSourceHoverDisplay *self,
+ GtkWidget *child)
+{
+ 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);
+ }
+}
diff --git a/gtksourceview/gtksourcehoverdisplay.h b/gtksourceview/gtksourcehoverdisplay.h
new file mode 100644
index 00000000..6a06b761
--- /dev/null
+++ b/gtksourceview/gtksourcehoverdisplay.h
@@ -0,0 +1,53 @@
+/*
+ * 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
+
+#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
+# error "Only <gtksourceview/gtksource.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+
+#include "gtksourcetypes.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_HOVER_DISPLAY (gtk_source_hover_display_get_type())
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+G_DECLARE_FINAL_TYPE (GtkSourceHoverDisplay, gtk_source_hover_display, GTK_SOURCE, HOVER_DISPLAY, GtkWidget)
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+void gtk_source_hover_display_append (GtkSourceHoverDisplay *self,
+ GtkWidget *child);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void gtk_source_hover_display_prepend (GtkSourceHoverDisplay *self,
+ GtkWidget *child);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void gtk_source_hover_display_insert_after (GtkSourceHoverDisplay *self,
+ GtkWidget *child,
+ GtkWidget *sibling);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void gtk_source_hover_display_remove (GtkSourceHoverDisplay *self,
+ GtkWidget *child);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcehoverprovider.c b/gtksourceview/gtksourcehoverprovider.c
new file mode 100644
index 00000000..40ce1ef2
--- /dev/null
+++ b/gtksourceview/gtksourcehoverprovider.c
@@ -0,0 +1,120 @@
+/*
+ * 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
+ */
+
+#include "config.h"
+
+#include "gtksourcehovercontext.h"
+#include "gtksourcehoverdisplay.h"
+#include "gtksourcehoverprovider.h"
+
+/**
+ * SETION:hoverprovider
+ * @Title: GtkSourceHoverProvider
+ * @Short_description: interface to populate interactive tooltips
+ * @See_also: #GtkSourceHover, #GtkSourceHoverDisplay, #GtkSourceHoverContext
+ *
+ * #GtkSourceHoverProvider is an interface that should be implemented to extend
+ * the contents of a #GtkSourceHoverDisplay. This is typical in editors that
+ * interact external tooling such as those utilizing Language Server Protocol.
+ *
+ * If you can populate the #GtkSourceHoverDisplay synchronously, use
+ * #GtkSourceHoverProvider.populate. Otherwise, interface implementations that
+ * may take additional time should use #GtkSourceHoverProvider.populate_async
+ * to avoid blocking the main loop.
+ *
+ * Since: 5.0
+ */
+
+G_DEFINE_INTERFACE (GtkSourceHoverProvider, gtk_source_hover_provider, G_TYPE_OBJECT)
+
+static gboolean
+gtk_source_hover_provider_real_populate (GtkSourceHoverProvider *provider,
+ GtkSourceHoverContext *context,
+ GtkSourceHoverDisplay *display,
+ GError **error)
+{
+ return TRUE;
+}
+
+static void
+gtk_source_hover_provider_real_populate_async (GtkSourceHoverProvider *provider,
+ GtkSourceHoverContext *context,
+ GtkSourceHoverDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GTask *task;
+
+ task = g_task_new (provider, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gtk_source_hover_provider_real_populate_async);
+
+ if (!GTK_SOURCE_HOVER_PROVIDER_GET_IFACE (provider)->populate (provider, context, display, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static gboolean
+gtk_source_hover_provider_real_populate_finish (GtkSourceHoverProvider *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+gtk_source_hover_provider_default_init (GtkSourceHoverProviderInterface *iface)
+{
+ iface->populate_async = gtk_source_hover_provider_real_populate_async;
+ iface->populate_finish = gtk_source_hover_provider_real_populate_finish;
+ iface->populate = gtk_source_hover_provider_real_populate;
+}
+
+void
+gtk_source_hover_provider_populate_async (GtkSourceHoverProvider *provider,
+ GtkSourceHoverContext *context,
+ GtkSourceHoverDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_PROVIDER (provider));
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_CONTEXT (context));
+ g_return_if_fail (GTK_SOURCE_IS_HOVER_DISPLAY (display));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ GTK_SOURCE_HOVER_PROVIDER_GET_IFACE (provider)->populate_async (provider, context, display,
cancellable, callback, user_data);
+}
+
+gboolean
+gtk_source_hover_provider_populate_finish (GtkSourceHoverProvider *provider,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_SOURCE_IS_HOVER_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return GTK_SOURCE_HOVER_PROVIDER_GET_IFACE (provider)->populate_finish (provider, result, error);
+}
diff --git a/gtksourceview/gtksourcehoverprovider.h b/gtksourceview/gtksourcehoverprovider.h
new file mode 100644
index 00000000..c662c0a2
--- /dev/null
+++ b/gtksourceview/gtksourcehoverprovider.h
@@ -0,0 +1,70 @@
+/*
+ * 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
+
+#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
+# error "Only <gtksourceview/gtksource.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+
+#include "gtksourcetypes.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_HOVER_PROVIDER (gtk_source_hover_provider_get_type())
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+G_DECLARE_INTERFACE (GtkSourceHoverProvider, gtk_source_hover_provider, GTK_SOURCE, HOVER_PROVIDER, GObject)
+
+struct _GtkSourceHoverProviderInterface
+{
+ GTypeInterface parent_iface;
+
+ gboolean (*populate) (GtkSourceHoverProvider *self,
+ GtkSourceHoverContext *context,
+ GtkSourceHoverDisplay *display,
+ GError **error);
+ void (*populate_async) (GtkSourceHoverProvider *self,
+ GtkSourceHoverContext *context,
+ GtkSourceHoverDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*populate_finish) (GtkSourceHoverProvider *self,
+ GAsyncResult *result,
+ GError **error);
+};
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+void gtk_source_hover_provider_populate_async (GtkSourceHoverProvider *self,
+ GtkSourceHoverContext *context,
+ GtkSourceHoverDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean gtk_source_hover_provider_populate_finish (GtkSourceHoverProvider *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcetypes.h b/gtksourceview/gtksourcetypes.h
index 85156f61..6f1530cc 100644
--- a/gtksourceview/gtksourcetypes.h
+++ b/gtksourceview/gtksourcetypes.h
@@ -50,6 +50,10 @@ typedef struct _GtkSourceGutterLines GtkSourceGutterLines;
typedef struct _GtkSourceGutterRenderer GtkSourceGutterRenderer;
typedef struct _GtkSourceGutterRendererPixbuf GtkSourceGutterRendererPixbuf;
typedef struct _GtkSourceGutterRendererText GtkSourceGutterRendererText;
+typedef struct _GtkSourceHover GtkSourceHover;
+typedef struct _GtkSourceHoverContext GtkSourceHoverContext;
+typedef struct _GtkSourceHoverDisplay GtkSourceHoverDisplay;
+typedef struct _GtkSourceHoverProvider GtkSourceHoverProvider;
typedef struct _GtkSourceLanguage GtkSourceLanguage;
typedef struct _GtkSourceLanguageManager GtkSourceLanguageManager;
typedef struct _GtkSourceMap GtkSourceMap;
diff --git a/gtksourceview/gtksourceview.c b/gtksourceview/gtksourceview.c
index e4292dd2..e2abe64b 100644
--- a/gtksourceview/gtksourceview.c
+++ b/gtksourceview/gtksourceview.c
@@ -39,6 +39,7 @@
#include "gtksourcegutter-private.h"
#include "gtksourcegutterrendererlines-private.h"
#include "gtksourcegutterrenderermarks-private.h"
+#include "gtksourcehover-private.h"
#include "gtksource-enumtypes.h"
#include "gtksourcemark.h"
#include "gtksourcemarkattributes.h"
@@ -189,6 +190,7 @@ typedef struct
GdkRGBA right_margin_overlay_color;
GtkSourceCompletion *completion;
+ GtkSourceHover *hover;
guint right_margin_pos;
gint cached_right_margin_pos;
@@ -1455,6 +1457,12 @@ gtk_source_view_dispose (GObject *object)
g_clear_object (&priv->completion);
}
+ if (priv->hover != NULL)
+ {
+ g_object_run_dispose (G_OBJECT (priv->hover));
+ g_clear_object (&priv->hover);
+ }
+
g_clear_object (&priv->style_scheme);
g_clear_object (&priv->space_drawer);
@@ -4805,6 +4813,31 @@ gtk_source_view_get_completion (GtkSourceView *view)
return priv->completion;
}
+/**
+ * gtk_source_view_get_hover:
+ * @view: a #GtkSourceView.
+ *
+ * Gets the #GtkSourceHover associated with @view. The returned object is
+ * guaranteed to be the same for the lifetime of @view. Each #GtkSourceView
+ * object has a different #GtkSourceHover.
+ *
+ * Returns: (transfer none): a #GtkSourceHover associated with @view.
+ */
+GtkSourceHover *
+gtk_source_view_get_hover (GtkSourceView *view)
+{
+ GtkSourceViewPrivate *priv = gtk_source_view_get_instance_private (view);
+
+ g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
+
+ if (priv->hover == NULL)
+ {
+ priv->hover = _gtk_source_hover_new (view);
+ }
+
+ return priv->hover;
+}
+
/**
* gtk_source_view_get_gutter:
* @view: a #GtkSourceView.
diff --git a/gtksourceview/gtksourceview.h b/gtksourceview/gtksourceview.h
index 4b448722..ab154a39 100644
--- a/gtksourceview/gtksourceview.h
+++ b/gtksourceview/gtksourceview.h
@@ -195,6 +195,8 @@ guint gtk_source_view_get_visual_column
const GtkTextIter
*iter);
GTK_SOURCE_AVAILABLE_IN_ALL
GtkSourceCompletion *gtk_source_view_get_completion (GtkSourceView
*view);
+GTK_SOURCE_AVAILABLE_IN_5_0
+GtkSourceHover *gtk_source_view_get_hover (GtkSourceView
*view);
GTK_SOURCE_AVAILABLE_IN_ALL
GtkSourceGutter *gtk_source_view_get_gutter (GtkSourceView
*view,
GtkTextWindowType
window_type);
diff --git a/gtksourceview/meson.build b/gtksourceview/meson.build
index 4bc7041f..4abec0c0 100644
--- a/gtksourceview/meson.build
+++ b/gtksourceview/meson.build
@@ -21,6 +21,10 @@ core_public_h = files([
'gtksourcegutterrenderer.h',
'gtksourcegutterrendererpixbuf.h',
'gtksourcegutterrenderertext.h',
+ 'gtksourcehover.h',
+ 'gtksourcehovercontext.h',
+ 'gtksourcehoverprovider.h',
+ 'gtksourcehoverdisplay.h',
'gtksourceinit.h',
'gtksourcelanguage.h',
'gtksourcelanguagemanager.h',
@@ -64,6 +68,10 @@ core_public_c = files([
'gtksourcegutterrenderer.c',
'gtksourcegutterrendererpixbuf.c',
'gtksourcegutterrenderertext.c',
+ 'gtksourcehover.c',
+ 'gtksourcehovercontext.c',
+ 'gtksourcehoverdisplay.c',
+ 'gtksourcehoverprovider.c',
'gtksourceinit.c',
'gtksourcelanguage.c',
'gtksourcelanguagemanager.c',
@@ -107,6 +115,7 @@ core_private_c = files([
'gtksourceengine.c',
'gtksourcegutterrendererlines.c',
'gtksourcegutterrenderermarks.c',
+ 'gtksourcehoverassistant.c',
'gtksourceinformative.c',
'gtksourceiter.c',
'gtksourcelanguage-parser-2.c',
diff --git a/tests/test-widget.c b/tests/test-widget.c
index 2ee4521f..ec00016c 100644
--- a/tests/test-widget.c
+++ b/tests/test-widget.c
@@ -22,7 +22,11 @@
#include <string.h>
#include <gtksourceview/gtksource.h>
+#define TEST_TYPE_WIDGET (test_widget_get_type())
+#define TEST_TYPE_HOVER_PROVIDER (test_hover_provider_get_type())
+
G_DECLARE_FINAL_TYPE (TestWidget, test_widget, TEST, WIDGET, GtkGrid)
+G_DECLARE_FINAL_TYPE (TestHoverProvider, test_hover_provider, TEST, HOVER_PROVIDER, GObject)
struct _TestWidget
{
@@ -44,9 +48,16 @@ struct _TestWidget
GtkWidget *top;
};
-GType test_widget_get_type (void);
+struct _TestHoverProvider
+{
+ GObject parent_instance;
+};
+
+static void hover_provider_iface_init (GtkSourceHoverProviderInterface *iface);
G_DEFINE_TYPE (TestWidget, test_widget, GTK_TYPE_GRID)
+G_DEFINE_TYPE_WITH_CODE (TestHoverProvider, test_hover_provider, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_HOVER_PROVIDER, hover_provider_iface_init))
#define MARK_TYPE_1 "one"
#define MARK_TYPE_2 "two"
@@ -946,6 +957,31 @@ enable_snippets_toggled_cb (TestWidget *self,
gtk_source_view_set_enable_snippets (self->view, enabled);
}
+static GtkSourceHoverProvider *
+create_hover_provider (void)
+{
+ return g_object_new (TEST_TYPE_HOVER_PROVIDER, NULL);
+}
+
+static void
+enable_hover_toggled_cb (TestWidget *self,
+ GtkCheckButton *button)
+{
+ static GtkSourceHoverProvider *test_hover_provider;
+ GtkSourceHover *hover = gtk_source_view_get_hover (self->view);
+ gboolean enabled = gtk_check_button_get_active (button);
+
+ if (test_hover_provider == NULL)
+ {
+ test_hover_provider = create_hover_provider ();
+ }
+
+ if (enabled)
+ gtk_source_hover_add_provider (hover, test_hover_provider);
+ else
+ gtk_source_hover_remove_provider (hover, test_hover_provider);
+}
+
static void
test_widget_dispose (GObject *object)
{
@@ -985,6 +1021,7 @@ test_widget_class_init (TestWidgetClass *klass)
gtk_widget_class_bind_template_callback (widget_class, forward_string_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, smart_home_end_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, enable_snippets_toggled_cb);
+ gtk_widget_class_bind_template_callback (widget_class, enable_hover_toggled_cb);
gtk_widget_class_bind_template_child (widget_class, TestWidget, view);
gtk_widget_class_bind_template_child (widget_class, TestWidget, map);
@@ -1120,6 +1157,41 @@ test_widget_new (void)
return g_object_new (test_widget_get_type (), NULL);
}
+static gboolean
+test_hover_provider_populate (GtkSourceHoverProvider *provider,
+ GtkSourceHoverContext *context,
+ GtkSourceHoverDisplay *display,
+ GError **error)
+{
+ GtkTextIter begin, end;
+
+ if (gtk_source_hover_context_get_bounds (context, &begin, &end))
+ {
+ gchar *text = gtk_text_iter_get_slice (&begin, &end);
+ GtkWidget *label = gtk_label_new (text);
+ gtk_source_hover_display_append (display, label);
+ g_free (text);
+ }
+
+ return TRUE;
+}
+
+static void
+hover_provider_iface_init (GtkSourceHoverProviderInterface *iface)
+{
+ iface->populate = test_hover_provider_populate;
+}
+
+static void
+test_hover_provider_class_init (TestHoverProviderClass *klass)
+{
+}
+
+static void
+test_hover_provider_init (TestHoverProvider *self)
+{
+}
+
static void
setup_search_paths (void)
{
@@ -1140,7 +1212,6 @@ setup_search_paths (void)
gtk_source_language_manager_set_search_path (languages, langs_path);
}
-
int
main (int argc, char *argv[])
{
diff --git a/tests/test-widget.ui b/tests/test-widget.ui
index 1d5f7460..ca6d7d10 100644
--- a/tests/test-widget.ui
+++ b/tests/test-widget.ui
@@ -228,6 +228,17 @@
</layout>
</object>
</child>
+ <child>
+ <object class="GtkCheckButton" id="enable_hover">
+ <property name="label">Enable hoverers</property>
+ <property name="can-focus">1</property>
+ <signal name="toggled" handler="enable_hover_toggled_cb" object="TestWidget" swapped="yes"/>
+ <layout>
+ <property name="row">14</property>
+ <property name="column">0</property>
+ </layout>
+ </object>
+ </child>
<child>
<object class="GtkGrid" id="grid10">
<layout>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]