[gnome-builder/wip/slaf/spellcheck: 8/8] spellchecker: use a match bubble



commit 880493ce8676952f1be6ccd5e4b2b77aff30912b
Author: Sébastien Lafargue <slafargue gnome org>
Date:   Fri Dec 16 20:59:14 2016 +0100

    spellchecker: use a match bubble
    
    Misspelled text selection is replaced by a
    drawn bubble.
    
    colors can be found in style schemes:
    
    For example in Builder.xml:
    
    <style name="misspelled-match" foreground="black" background="selection1"/>

 data/style-schemes/builder-dark.xml        |    3 +
 data/style-schemes/builder.xml             |    3 +
 libide/editor/ide-editor-spell-navigator.c |    8 +-
 libide/sourceview/ide-source-view.c        |  162 ++++++++++++++++++++++++++++
 libide/sourceview/ide-source-view.h        |    3 +
 5 files changed, 177 insertions(+), 2 deletions(-)
---
diff --git a/data/style-schemes/builder-dark.xml b/data/style-schemes/builder-dark.xml
index 668f085..ec0e382 100644
--- a/data/style-schemes/builder-dark.xml
+++ b/data/style-schemes/builder-dark.xml
@@ -96,6 +96,9 @@
   <!-- Search Shadow -->
   <style name="search-shadow"               background="#rgba(0,0,0,0.4)"/>
 
+  <!-- Spellchecker Matching -->
+  <style name="misspelled-match"            foreground="#000000" background="#b3d4fc"/>
+
   <!-- Comments -->
   <style name="def:comment"                 foreground="aluminium4"/>
   <style name="def:shebang"                 foreground="aluminium4" bold="true"/>
diff --git a/data/style-schemes/builder.xml b/data/style-schemes/builder.xml
index 16578e5..4d0ffa0 100644
--- a/data/style-schemes/builder.xml
+++ b/data/style-schemes/builder.xml
@@ -107,6 +107,9 @@
   <!-- Search Shadow -->
   <style name="search-shadow"               background="#rgba(0,0,0,0.2)"/>
 
+  <!-- Spellchecker Matching -->
+  <style name="misspelled-match"            foreground="#000000" background="selection1"/>
+
   <!-- Quick Highlight Plugin -->
   <style name="quick-highlight"             background="#rgba(221,74,104,.15)"/>
 
diff --git a/libide/editor/ide-editor-spell-navigator.c b/libide/editor/ide-editor-spell-navigator.c
index 916d953..e4a5fdd 100644
--- a/libide/editor/ide-editor-spell-navigator.c
+++ b/libide/editor/ide-editor-spell-navigator.c
@@ -143,6 +143,9 @@ ide_editor_spell_navigator_dispose (GObject *object)
 {
   IdeEditorSpellNavigator *self = (IdeEditorSpellNavigator *)object;
 
+  ide_source_view_set_misspelled_word (IDE_SOURCE_VIEW (self->view), NULL, NULL);
+  gtk_widget_queue_draw (GTK_WIDGET (self->view));
+
   g_clear_object (&self->view);
   g_hash_table_unref (self->words_count);
 
@@ -290,12 +293,13 @@ select_misspelled_word (IdeEditorSpellNavigator *self)
   gtk_text_buffer_get_iter_at_mark (self->buffer, &word_start, self->word_start);
   gtk_text_buffer_get_iter_at_mark (self->buffer, &word_end, self->word_end);
 
-  gtk_text_buffer_select_range (self->buffer, &word_start, &word_end);
+  ide_source_view_set_misspelled_word (IDE_SOURCE_VIEW (self->view), &word_start, &word_end);
+  gtk_widget_queue_draw (GTK_WIDGET (self->view));
 
   g_return_if_fail (gtk_text_view_get_buffer (self->view) == self->buffer);
 
   gtk_text_view_scroll_to_mark (self->view,
-                                gtk_text_buffer_get_insert (self->buffer),
+                                self->word_start,
                                 0.25,
                                 FALSE,
                                 0.0,
diff --git a/libide/sourceview/ide-source-view.c b/libide/sourceview/ide-source-view.c
index 695c3a5..a6426de 100644
--- a/libide/sourceview/ide-source-view.c
+++ b/libide/sourceview/ide-source-view.c
@@ -152,6 +152,10 @@ typedef struct
   GdkRGBA                      bubble_color2;
   GdkRGBA                      search_shadow_rgba;
   GdkRGBA                      snippet_area_background_rgba;
+  GdkRGBA                      spellchecker_bubble_bg_color1;
+  GdkRGBA                      spellchecker_bubble_bg_color2;
+  GdkRGBA                      spellchecker_bubble_fg;
+  GtkTextTag                  *spellchecker_bubble_tag;
 
   guint                        font_scale;
 
@@ -164,6 +168,9 @@ typedef struct
   GtkTextMark                 *definition_highlight_start_mark;
   GtkTextMark                 *definition_highlight_end_mark;
 
+  GtkTextMark                 *misspelled_word_begin_mark;
+  GtkTextMark                 *misspelled_word_end_mark;
+
   GRegex                      *include_regex;
 
   guint                        auto_indent : 1;
@@ -987,8 +994,11 @@ ide_source_view__buffer_notify_style_scheme_cb (IdeSourceView *self,
   GtkSourceStyle *search_match_style = NULL;
   GtkSourceStyle *search_shadow_style = NULL;
   GtkSourceStyle *snippet_area_style = NULL;
+  GtkSourceStyle *spellchecker_match_style = NULL;
   g_autofree gchar *snippet_background = NULL;
   g_autofree gchar *search_shadow_background = NULL;
+  GdkRGBA spellchecker_bubble_fg;
+  GdkRGBA spellchecker_bubble_bg;
 
   g_assert (IDE_IS_SOURCE_VIEW (self));
   g_assert (IDE_IS_BUFFER (buffer));
@@ -999,6 +1009,7 @@ ide_source_view__buffer_notify_style_scheme_cb (IdeSourceView *self,
       search_match_style = gtk_source_style_scheme_get_style (scheme, "search-match");
       search_shadow_style = gtk_source_style_scheme_get_style (scheme, "search-shadow");
       snippet_area_style = gtk_source_style_scheme_get_style (scheme, "snippet::area");
+      spellchecker_match_style = gtk_source_style_scheme_get_style (scheme, "misspelled-match");
     }
 
   if (search_match_style)
@@ -1038,6 +1049,42 @@ ide_source_view__buffer_notify_style_scheme_cb (IdeSourceView *self,
       gdk_rgba_parse (&priv->snippet_area_background_rgba, "#204a87");
       priv->snippet_area_background_rgba.alpha = 0.1;
     }
+
+  if (spellchecker_match_style)
+    {
+      g_autofree gchar *background = NULL;
+      g_autofree gchar *foreground = NULL;
+      gboolean ret = TRUE;
+
+      g_object_get (spellchecker_match_style, "background", &background, NULL);
+      g_object_get (spellchecker_match_style, "foreground", &foreground, NULL);
+
+      if (!ide_str_empty0 (background))
+        ret = gdk_rgba_parse (&spellchecker_bubble_bg, background);
+
+      if (!ret)
+        gdk_rgba_parse (&spellchecker_bubble_bg, "#ADD8E6");
+
+      ret = TRUE;
+      if (!ide_str_empty0 (foreground))
+        ret = gdk_rgba_parse (&spellchecker_bubble_fg, foreground);
+
+      if (!ret)
+        gdk_rgba_parse (&spellchecker_bubble_fg, "#0000FF");
+    }
+  else
+    {
+      gdk_rgba_parse (&spellchecker_bubble_bg, "#ADD8E6");
+      gdk_rgba_parse (&spellchecker_bubble_fg, "#0000000");
+    }
+
+  priv->spellchecker_bubble_bg_color1 = spellchecker_bubble_bg;
+  ide_rgba_shade (&spellchecker_bubble_bg, &priv->spellchecker_bubble_bg_color2, 0.8);
+
+  priv->spellchecker_bubble_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (priv->buffer), NULL,
+                                                              "foreground-rgba", &spellchecker_bubble_fg,
+                                                              "background-rgba", 
&priv->spellchecker_bubble_bg_color1,
+                                                              NULL);
 }
 
 static void
@@ -4785,6 +4832,59 @@ ide_source_view_draw_search_bubbles (IdeSourceView *self,
   cairo_region_destroy (match_region);
 }
 
+void
+ide_source_view_draw_spellchecking_bubble (IdeSourceView *self,
+                                           cairo_t       *cr)
+{
+  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+  GtkTextView *text_view = (GtkTextView *)self;
+  cairo_region_t *clip_region;
+  cairo_rectangle_int_t rect;
+  GdkRectangle area;
+  GdkRectangle begin_rect;
+  GdkRectangle end_rect;
+  GtkTextIter begin;
+  GtkTextIter end;
+
+  g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+  g_return_if_fail (cr);
+
+  gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &begin, 
priv->misspelled_word_begin_mark);
+  gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &end, priv->misspelled_word_end_mark);
+
+  if (gtk_text_iter_get_line (&begin) == gtk_text_iter_get_line (&end))
+    {
+      if (!gdk_cairo_get_clip_rectangle (cr, &area))
+        gtk_widget_get_allocation (GTK_WIDGET (self), &area);
+
+      clip_region = cairo_region_create_rectangle (&area);
+
+      gtk_text_view_get_iter_location (text_view, &begin, &begin_rect);
+      gtk_text_view_buffer_to_window_coords (text_view, GTK_TEXT_WINDOW_TEXT,
+                                             begin_rect.x, begin_rect.y,
+                                             &begin_rect.x, &begin_rect.y);
+      gtk_text_view_get_iter_location (text_view, &end, &end_rect);
+      gtk_text_view_buffer_to_window_coords (text_view, GTK_TEXT_WINDOW_TEXT,
+                                             end_rect.x, end_rect.y,
+                                             &end_rect.x, &end_rect.y);
+
+      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_subtract_rectangle (clip_region, &rect);
+      gdk_cairo_region (cr, clip_region);
+      cairo_clip (cr);
+
+      draw_bezel (cr, &rect, 3, &priv->spellchecker_bubble_bg_color2);
+      draw_bezel (cr, &rect, 2, &priv->spellchecker_bubble_bg_color1);
+
+      cairo_region_destroy (clip_region);
+    }
+}
+
 static void
 ide_source_view_real_draw_layer (GtkTextView      *text_view,
                                  GtkTextViewLayer  layer,
@@ -4813,6 +4913,13 @@ ide_source_view_real_draw_layer (GtkTextView      *text_view,
           ide_source_view_draw_search_bubbles (self, cr);
           cairo_restore (cr);
         }
+
+      if (priv->misspelled_word_begin_mark != NULL && priv->misspelled_word_end_mark != NULL)
+        {
+          cairo_save (cr);
+          ide_source_view_draw_spellchecking_bubble (self, cr);
+          cairo_restore (cr);
+        }
     }
 }
 
@@ -5883,6 +5990,7 @@ ide_source_view_finalize (GObject *object)
   g_clear_pointer (&priv->snippets, g_queue_free);
   g_clear_pointer (&priv->include_regex, g_regex_unref);
   g_clear_pointer (&priv->saved_search_text, g_free);
+  g_clear_object (&priv->spellchecker_bubble_tag);
 
   EGG_COUNTER_DEC (instances);
 
@@ -8507,3 +8615,57 @@ ide_source_view_get_current_snippet (IdeSourceView *self)
 
   return g_queue_peek_head (priv->snippets);
 }
+
+/* Set begin and end to NULL to remove the misspelled word marks */
+void
+ide_source_view_set_misspelled_word (IdeSourceView *self,
+                                     GtkTextIter   *begin,
+                                     GtkTextIter   *end)
+{
+  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+  g_return_if_fail ((begin == NULL && end == NULL) || (begin != NULL && end != NULL));
+
+  if (priv->misspelled_word_begin_mark != NULL && priv->misspelled_word_end_mark != NULL)
+    {
+      GtkTextIter previous_begin;
+      GtkTextIter previous_end;
+
+      gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
+                                        &previous_begin,
+                                        priv->misspelled_word_begin_mark);
+
+      gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
+                                        &previous_end,
+                                        priv->misspelled_word_end_mark);
+
+      gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (priv->buffer),
+                                  priv->spellchecker_bubble_tag,
+                                  &previous_begin, &previous_end);
+    }
+
+  if (begin == NULL)
+    {
+      if (priv->misspelled_word_begin_mark != NULL)
+        {
+          gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (priv->buffer), priv->misspelled_word_begin_mark);
+          priv->misspelled_word_begin_mark = NULL;
+        }
+
+      if (priv->misspelled_word_end_mark != NULL)
+        {
+          gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (priv->buffer), priv->misspelled_word_end_mark);
+          priv->misspelled_word_end_mark = NULL;
+        }
+    }
+  else
+    {
+      priv->misspelled_word_begin_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (priv->buffer),
+                                                                      NULL, begin, TRUE);
+      priv->misspelled_word_end_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (priv->buffer),
+                                                                    NULL, end, TRUE);
+
+      gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (priv->buffer), priv->spellchecker_bubble_tag, begin, end);
+    }
+}
diff --git a/libide/sourceview/ide-source-view.h b/libide/sourceview/ide-source-view.h
index 7ea83c4..8684bfe 100644
--- a/libide/sourceview/ide-source-view.h
+++ b/libide/sourceview/ide-source-view.h
@@ -373,6 +373,9 @@ void                        ide_source_view_set_highlight_current_line(IdeSource
                                                                        gboolean                    
highlight_current_line);
 void                        ide_source_view_set_insert_matching_brace (IdeSourceView              *self,
                                                                        gboolean                    
insert_matching_brace);
+void                        ide_source_view_set_misspelled_word       (IdeSourceView              *self,
+                                                                       GtkTextIter                *start,
+                                                                       GtkTextIter                *end);
 void                        ide_source_view_set_overwrite_braces      (IdeSourceView              *self,
                                                                        gboolean                    
overwrite_braces);
 void                        ide_source_view_set_rubberband_search     (IdeSourceView              *self,


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