[gnome-builder] ctags: add icons to ctags



commit 855113941c16f7e7465448e5cef32f138501f22b
Author: Christian Hergert <christian hergert me>
Date:   Sat May 16 00:44:34 2015 -0700

    ctags: add icons to ctags
    
    There is a big of non-obvious magic going on here to ensure that we can
    render the icons with the proper symbolic colors. Additionally, we use
    borrowed references to help keep those expensive atomic ref count
    operations at bay (you can easily get a couple thousand results). It's
    all safe thoughâ„¢.

 libide/ctags/ide-ctags-completion-item.c     |   28 +++++-
 libide/ctags/ide-ctags-completion-item.h     |    9 +-
 libide/ctags/ide-ctags-completion-provider.c |  135 +++++++++++++++++++++++++-
 libide/ctags/ide-ctags-completion-provider.h |    9 +-
 src/resources/gnome-builder.gresource.xml    |    1 +
 5 files changed, 172 insertions(+), 10 deletions(-)
---
diff --git a/libide/ctags/ide-ctags-completion-item.c b/libide/ctags/ide-ctags-completion-item.c
index e648378..0c65204 100644
--- a/libide/ctags/ide-ctags-completion-item.c
+++ b/libide/ctags/ide-ctags-completion-item.c
@@ -25,8 +25,10 @@
 
 struct _IdeCtagsCompletionItem
 {
-  GObject                   parent_instance;
-  const IdeCtagsIndexEntry *entry;
+  GObject                     parent_instance;
+  const IdeCtagsIndexEntry   *entry;
+  IdeCtagsCompletionProvider *provider;
+  GtkSourceCompletionContext *context;
 };
 
 static void proposal_iface_init (GtkSourceCompletionProposalIface *iface);
@@ -36,12 +38,21 @@ G_DEFINE_TYPE_WITH_CODE (IdeCtagsCompletionItem, ide_ctags_completion_item, G_TY
                                                 proposal_iface_init))
 
 GtkSourceCompletionProposal *
-ide_ctags_completion_item_new (const IdeCtagsIndexEntry *entry)
+ide_ctags_completion_item_new (const IdeCtagsIndexEntry   *entry,
+                               IdeCtagsCompletionProvider *provider,
+                               GtkSourceCompletionContext *context)
 {
   IdeCtagsCompletionItem *self;
 
   self= g_object_new (IDE_TYPE_CTAGS_COMPLETION_ITEM, NULL);
+
+  /*
+   * use borrowed references to avoid the massive amount of reference counting.
+   * we don't need them since we know the provider will outlast us.
+   */
   self->entry = entry;
+  self->provider = provider;
+  self->context = context;
 
   return GTK_SOURCE_COMPLETION_PROPOSAL (self);
 }
@@ -79,9 +90,20 @@ get_text (GtkSourceCompletionProposal *proposal)
   return g_strdup (self->entry->name);
 }
 
+static GdkPixbuf *
+get_icon (GtkSourceCompletionProposal *proposal)
+{
+  IdeCtagsCompletionItem *self = (IdeCtagsCompletionItem *)proposal;
+
+  return ide_ctags_completion_provider_get_proposal_icon (self->provider,
+                                                          self->context,
+                                                          self->entry);
+}
+
 static void
 proposal_iface_init (GtkSourceCompletionProposalIface *iface)
 {
   iface->get_label = get_label;
   iface->get_text = get_text;
+  iface->get_icon = get_icon;
 }
diff --git a/libide/ctags/ide-ctags-completion-item.h b/libide/ctags/ide-ctags-completion-item.h
index 5587231..65c3b28 100644
--- a/libide/ctags/ide-ctags-completion-item.h
+++ b/libide/ctags/ide-ctags-completion-item.h
@@ -22,6 +22,7 @@
 #include <gtksourceview/gtksource.h>
 
 #include "ide-ctags-index.h"
+#include "ide-ctags-completion-provider.h"
 
 G_BEGIN_DECLS
 
@@ -29,9 +30,11 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeCtagsCompletionItem, ide_ctags_completion_item, IDE, CTAGS_COMPLETION_ITEM, GObject)
 
-GtkSourceCompletionProposal *ide_ctags_completion_item_new     (const IdeCtagsIndexEntry *entry);
-gint                         ide_ctags_completion_item_compare (IdeCtagsCompletionItem   *itema,
-                                                                IdeCtagsCompletionItem   *itemb);
+GtkSourceCompletionProposal *ide_ctags_completion_item_new     (const IdeCtagsIndexEntry   *entry,
+                                                                IdeCtagsCompletionProvider *provider,
+                                                                GtkSourceCompletionContext *context);
+gint                         ide_ctags_completion_item_compare (IdeCtagsCompletionItem     *itema,
+                                                                IdeCtagsCompletionItem     *itemb);
 
 G_END_DECLS
 
diff --git a/libide/ctags/ide-ctags-completion-provider.c b/libide/ctags/ide-ctags-completion-provider.c
index 83a60e7..3797557 100644
--- a/libide/ctags/ide-ctags-completion-provider.c
+++ b/libide/ctags/ide-ctags-completion-provider.c
@@ -31,6 +31,7 @@ struct _IdeCtagsCompletionProvider
 
   GSettings     *settings;
   GPtrArray     *indexes;
+  GHashTable    *icons;
 
   gint           minimum_word_size;
 };
@@ -52,6 +53,17 @@ ide_ctags_completion_provider_add_index (IdeCtagsCompletionProvider *self,
 }
 
 static void
+theme_changed_cb (IdeCtagsCompletionProvider *self,
+                  GParamSpec                 *pspec,
+                  GtkSettings                *settings)
+{
+  g_assert (IDE_IS_CTAGS_COMPLETION_PROVIDER (self));
+  g_assert (self->icons != NULL);
+
+  g_hash_table_remove_all (self->icons);
+}
+
+static void
 ide_ctags_completion_provider_finalize (GObject *object)
 {
   IdeCtagsCompletionProvider *self = (IdeCtagsCompletionProvider *)object;
@@ -73,9 +85,106 @@ ide_ctags_completion_provider_class_init (IdeCtagsCompletionProviderClass *klass
 static void
 ide_ctags_completion_provider_init (IdeCtagsCompletionProvider *self)
 {
+  GtkSettings *settings;
+
   self->minimum_word_size = 3;
   self->indexes = g_ptr_array_new_with_free_func (g_object_unref);
   self->settings = g_settings_new ("org.gnome.builder.experimental");
+  self->icons = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+  settings = gtk_settings_get_default ();
+  g_signal_connect_object (settings,
+                           "notify::gtk-theme-name",
+                           G_CALLBACK (theme_changed_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
+
+static GdkPixbuf *
+load_pixbuf (IdeCtagsCompletionProvider *self,
+             GtkSourceCompletionContext *context,
+             const gchar                *icon_name,
+             guint                       size)
+{
+  GtkSourceCompletion *completion = NULL;
+  GtkSourceCompletionInfo *window;
+  GtkStyleContext *style_context;
+  GtkIconTheme *icon_theme;
+  GtkIconInfo *icon_info;
+  GdkPixbuf *ret = NULL;
+  gboolean was_symbolic;
+
+  g_assert (IDE_IS_CTAGS_COMPLETION_PROVIDER (self));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+
+  g_object_get (context, "completion", &completion, NULL);
+  window = gtk_source_completion_get_info_window (completion);
+  style_context = gtk_widget_get_style_context (GTK_WIDGET (window));
+  icon_theme = gtk_icon_theme_get_default ();
+  icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, 0);
+  if (icon_info != NULL)
+    ret = gtk_icon_info_load_symbolic_for_context (icon_info, style_context, &was_symbolic, NULL);
+  g_clear_object (&completion);
+  g_clear_object (&icon_info);
+  if (ret != NULL)
+    g_hash_table_insert (self->icons, g_strdup (icon_name), ret);
+
+  return ret;
+}
+
+static GdkPixbuf *
+get_pixbuf (IdeCtagsCompletionProvider *self,
+            GtkSourceCompletionContext *context,
+            const IdeCtagsIndexEntry   *entry)
+{
+  const gchar *icon_name = NULL;
+  GdkPixbuf *pixbuf;
+
+  switch (entry->kind)
+    {
+    case IDE_CTAGS_INDEX_ENTRY_CLASS_NAME:
+      icon_name = "lang-clang-symbolic";
+      break;
+
+    case IDE_CTAGS_INDEX_ENTRY_ENUMERATOR:
+      icon_name = "lang-enum-value-symbolic";
+      break;
+
+    case IDE_CTAGS_INDEX_ENTRY_ENUMERATION_NAME:
+      icon_name = "lang-enum-symbolic";
+      break;
+
+    case IDE_CTAGS_INDEX_ENTRY_PROTOTYPE:
+    case IDE_CTAGS_INDEX_ENTRY_FUNCTION:
+      icon_name = "lang-function-symbolic";
+      break;
+
+    case IDE_CTAGS_INDEX_ENTRY_FILE_NAME:
+      icon_name = "text-x-generic-symbolic";
+      break;
+
+    case IDE_CTAGS_INDEX_ENTRY_MEMBER:
+      icon_name = "lang-struct-field-symbolic";
+      break;
+
+    case IDE_CTAGS_INDEX_ENTRY_UNION:
+    case IDE_CTAGS_INDEX_ENTRY_TYPEDEF:
+    case IDE_CTAGS_INDEX_ENTRY_STRUCTURE:
+      icon_name = "lang-struct-symbolic";
+      break;
+
+    case IDE_CTAGS_INDEX_ENTRY_ANCHOR:
+    case IDE_CTAGS_INDEX_ENTRY_VARIABLE:
+    case IDE_CTAGS_INDEX_ENTRY_DEFINE:
+    default:
+      return NULL;
+    }
+
+  pixbuf = g_hash_table_lookup (self->icons, icon_name);
+  if (!pixbuf)
+    pixbuf = load_pixbuf (self, context, icon_name, 16);
+
+  return pixbuf;
 }
 
 static gchar *
@@ -263,7 +372,21 @@ ide_ctags_completion_provider_populate (GtkSourceCompletionProvider *provider,
 
           if (is_allowed (entry, allowed))
             {
-              item = ide_ctags_completion_item_new (entry);
+              /*
+               * NOTE:
+               *
+               * Autocompletion is very performance sensitive code. The smallest amount of
+               * extra work has a very negative impact on interactivity. We are trying to
+               * avoid a couple things here based on how completion works.
+               *
+               * 1) Avoiding referencing or copying things.
+               *    Since the provider will always outlive the completion item, we use
+               *    borrowed references for as much as we can.
+               * 2) We delay the work of looking up icons until they are requested.
+               *    No sense in doing that work before hand.
+               */
+
+              item = ide_ctags_completion_item_new (entry, self, context);
               g_ptr_array_add (ar, item);
             }
         }
@@ -283,6 +406,16 @@ failure:
   IDE_EXIT;
 }
 
+GdkPixbuf *
+ide_ctags_completion_provider_get_proposal_icon (IdeCtagsCompletionProvider *self,
+                                                 GtkSourceCompletionContext *context,
+                                                 const IdeCtagsIndexEntry   *entry)
+{
+  g_return_val_if_fail (IDE_IS_CTAGS_COMPLETION_PROVIDER (self), NULL);
+
+  return get_pixbuf (self, context, entry);
+}
+
 static void
 provider_iface_init (GtkSourceCompletionProviderIface *iface)
 {
diff --git a/libide/ctags/ide-ctags-completion-provider.h b/libide/ctags/ide-ctags-completion-provider.h
index fa11932..936db98 100644
--- a/libide/ctags/ide-ctags-completion-provider.h
+++ b/libide/ctags/ide-ctags-completion-provider.h
@@ -29,9 +29,12 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeCtagsCompletionProvider, ide_ctags_completion_provider, IDE, 
CTAGS_COMPLETION_PROVIDER, GObject)
 
-GtkSourceCompletionProvider *ide_ctags_completion_provider_new       (void);
-void                         ide_ctags_completion_provider_add_index (IdeCtagsCompletionProvider *self,
-                                                                      IdeCtagsIndex              *index);
+GtkSourceCompletionProvider *ide_ctags_completion_provider_new               (void);
+void                         ide_ctags_completion_provider_add_index         (IdeCtagsCompletionProvider 
*self,
+                                                                              IdeCtagsIndex              
*index);
+GdkPixbuf                   *ide_ctags_completion_provider_get_proposal_icon (IdeCtagsCompletionProvider 
*self,
+                                                                              GtkSourceCompletionContext 
*context,
+                                                                              const IdeCtagsIndexEntry   
*entry);
 
 G_END_DECLS
 
diff --git a/src/resources/gnome-builder.gresource.xml b/src/resources/gnome-builder.gresource.xml
index 55e35ba..3022a04 100644
--- a/src/resources/gnome-builder.gresource.xml
+++ b/src/resources/gnome-builder.gresource.xml
@@ -14,6 +14,7 @@
     <file 
alias="icons/scalable/actions/lang-enum-symbolic.svg">../../data/icons/hicolor/scalable/autocomplete/lang-enum-symbolic.svg</file>
     <file 
alias="icons/scalable/actions/lang-function-symbolic.svg">../../data/icons/hicolor/scalable/autocomplete/lang-function-symbolic.svg</file>
     <file 
alias="icons/scalable/actions/lang-struct-symbolic.svg">../../data/icons/hicolor/scalable/autocomplete/lang-struct-symbolic.svg</file>
+    <file 
alias="icons/scalable/actions/lang-struct-field-symbolic.svg">../../data/icons/hicolor/scalable/autocomplete/struct-field-symbolic.svg</file>
     <file 
alias="icons/scalable/actions/builder-vcs-git-symbolic.svg">../../data/icons/hicolor/scalable/vcs/builder-vcs-git-symbolic.svg</file>
 
     <!-- Non-scalable vcs icons -->


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