[gnome-text-editor] sourceview: add plumbing to draw search bubbles
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-text-editor] sourceview: add plumbing to draw search bubbles
- Date: Thu, 6 Jan 2022 23:43:33 +0000 (UTC)
commit ae89fd62c06a7a8cad7287d645830d3120cddb81
Author: Christian Hergert <chergert redhat com>
Date: Thu Jan 6 15:42:53 2022 -0800
sourceview: add plumbing to draw search bubbles
Currently, the gtk_text_view_get_iter_location() API does not take into
account the line-height, so this code ported from Builder is not fully
accurate as the line-height grows.
However, it's nice to have a port here so that we can handle that later
once the API is fixed.
src/editor-source-view.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 181 insertions(+)
---
diff --git a/src/editor-source-view.c b/src/editor-source-view.c
index 4ed5f4c..e397fc0 100644
--- a/src/editor-source-view.c
+++ b/src/editor-source-view.c
@@ -29,6 +29,9 @@
#include "editor-spell-menu.h"
#include "editor-utils-private.h"
+#define X_PAD 3
+#define Y_PAD 3
+
struct _EditorSourceView
{
GtkSourceView parent_instance;
@@ -473,6 +476,181 @@ editor_source_view_action_delete_line (GtkWidget *widget,
gdk_clipboard_set_text (gtk_widget_get_primary_clipboard (widget), text);
}
+static GtkSourceSearchContext *
+get_search_context (EditorSourceView *self)
+{
+ EditorPage *page = EDITOR_PAGE (gtk_widget_get_ancestor (GTK_WIDGET (self), EDITOR_TYPE_PAGE));
+
+ if (page->search_bar->context &&
+ gtk_source_search_context_get_highlight (page->search_bar->context))
+ return page->search_bar->context;
+
+ return NULL;
+}
+
+static inline void
+add_match (GtkTextView *text_view,
+ cairo_region_t *region,
+ const GtkTextIter *begin,
+ const GtkTextIter *end)
+{
+ GdkRectangle begin_rect;
+ GdkRectangle end_rect;
+ cairo_rectangle_int_t rect;
+
+ /* NOTE: @end is not inclusive of the match. */
+ if (gtk_text_iter_get_line (begin) == gtk_text_iter_get_line (end))
+ {
+ gtk_text_view_get_iter_location (text_view, begin, &begin_rect);
+ gtk_text_view_get_iter_location (text_view, end, &end_rect);
+ rect.x = begin_rect.x;
+ rect.y = begin_rect.y;
+ rect.width = end_rect.x - begin_rect.x;
+ rect.height = MAX (begin_rect.height, end_rect.height);
+
+ cairo_region_union_rectangle (region, &rect);
+
+ return;
+ }
+
+ /*
+ * TODO: Add support for multi-line matches. When @begin and @end are not
+ * on the same line, we need to add the match region to @region so
+ * ide_source_view_draw_search_bubbles() can draw search bubbles
+ * around it.
+ */
+}
+
+static guint
+add_matches (GtkTextView *text_view,
+ cairo_region_t *region,
+ GtkSourceSearchContext *search_context,
+ const GtkTextIter *begin,
+ const GtkTextIter *end)
+{
+ GtkTextIter first_begin;
+ GtkTextIter new_begin;
+ GtkTextIter match_begin;
+ GtkTextIter match_end;
+ gboolean has_wrapped;
+ guint count = 1;
+
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+ g_assert (region != NULL);
+ g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (search_context));
+ g_assert (begin != NULL);
+ g_assert (end != NULL);
+
+ if (!gtk_source_search_context_forward (search_context,
+ begin,
+ &first_begin,
+ &match_end,
+ &has_wrapped))
+ return 0;
+
+ add_match (text_view, region, &first_begin, &match_end);
+
+ for (;;)
+ {
+ new_begin = match_end;
+
+ if (gtk_source_search_context_forward (search_context,
+ &new_begin,
+ &match_begin,
+ &match_end,
+ &has_wrapped) &&
+ (gtk_text_iter_compare (&match_begin, end) < 0) &&
+ (gtk_text_iter_compare (&first_begin, &match_begin) != 0))
+ {
+ add_match (text_view, region, &match_begin, &match_end);
+ count++;
+ continue;
+ }
+
+ break;
+ }
+
+ return count;
+}
+
+G_GNUC_UNUSED static void
+editor_source_view_draw_search_bubbles (EditorSourceView *self,
+ GtkSnapshot *snapshot)
+{
+ GtkStyleContext *style_context;
+ cairo_region_t *clip_region;
+ cairo_region_t *match_region;
+ GtkSourceSearchContext *context;
+ GdkRectangle area;
+ GtkTextIter begin, end;
+
+ g_assert (EDITOR_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_SNAPSHOT (snapshot));
+
+ if (!(context = get_search_context (self)))
+ return;
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+
+ gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (self), &area);
+ gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (self), &begin,
+ area.x, area.y);
+ gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (self), &end,
+ area.x + area.width,
+ area.y + area.height);
+
+ clip_region = cairo_region_create_rectangle (&area);
+ match_region = cairo_region_create ();
+
+ if (add_matches (GTK_TEXT_VIEW (self), match_region, context, &begin, &end))
+ {
+ int n_rects;
+
+ cairo_region_subtract (clip_region, match_region);
+
+ n_rects = cairo_region_num_rectangles (match_region);
+
+ gtk_style_context_save (style_context);
+ gtk_style_context_add_class (style_context, "search-match");
+ for (guint i = 0; i < n_rects; i++)
+ {
+ cairo_rectangle_int_t r;
+
+ cairo_region_get_rectangle (match_region, i, &r);
+
+ r.x -= X_PAD;
+ r.width += 2*X_PAD;
+ r.y -= Y_PAD;
+ r.height += 2*Y_PAD;
+
+ gtk_snapshot_render_background (snapshot, style_context, r.x, r.y, r.width, r.height);
+ gtk_snapshot_render_frame (snapshot, style_context, r.x, r.y, r.width, r.height);
+ }
+ gtk_style_context_restore (style_context);
+ }
+
+ cairo_region_destroy (clip_region);
+ cairo_region_destroy (match_region);
+}
+
+static void
+editor_source_view_snapshot_layer (GtkTextView *text_view,
+ GtkTextViewLayer layer,
+ GtkSnapshot *snapshot)
+{
+ EditorSourceView *self = (EditorSourceView *)text_view;
+
+ g_assert (EDITOR_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_SNAPSHOT (snapshot));
+
+ GTK_TEXT_VIEW_CLASS (editor_source_view_parent_class)->snapshot_layer (text_view, layer, snapshot);
+
+#if 0
+ if (layer == GTK_TEXT_VIEW_LAYER_BELOW_TEXT)
+ editor_source_view_draw_search_bubbles (self, snapshot);
+#endif
+}
+
static void
editor_source_view_constructed (GObject *object)
{
@@ -552,12 +730,15 @@ editor_source_view_class_init (EditorSourceViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass);
object_class->constructed = editor_source_view_constructed;
object_class->dispose = editor_source_view_dispose;
object_class->get_property = editor_source_view_get_property;
object_class->set_property = editor_source_view_set_property;
+ text_view_class->snapshot_layer = editor_source_view_snapshot_layer;
+
properties [PROP_FONT_DESC] =
g_param_spec_boxed ("font-desc",
"Font Description",
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]