[gtk] node editor: Do some simple syntax highlighting



commit b3c4320bc2d9941a72f3fecf139eccab419a8ddd
Author: Timm Bäder <mail baedert org>
Date:   Tue May 7 17:19:58 2019 +0200

    node editor: Do some simple syntax highlighting

 demos/node-editor/node-editor-application.c |  18 ++++
 demos/node-editor/node-editor-window.c      | 135 +++++++++++++++++++++++++++-
 demos/node-editor/node-editor-window.ui     |  16 +---
 3 files changed, 154 insertions(+), 15 deletions(-)
---
diff --git a/demos/node-editor/node-editor-application.c b/demos/node-editor/node-editor-application.c
index a2c9dcfb19..d4d65f964f 100644
--- a/demos/node-editor/node-editor-application.c
+++ b/demos/node-editor/node-editor-application.c
@@ -23,6 +23,16 @@
 
 #include "node-editor-window.h"
 
+static const char *css =
+"textview.editor {"
+"  color: rgb(192, 197, 206);"
+"  caret-color: white;"
+"}"
+"textview.editor text {"
+"  background-color: rgb(43, 48, 59);"
+"}"
+;
+
 struct _NodeEditorApplication
 {
   GtkApplication parent;
@@ -58,6 +68,7 @@ node_editor_application_startup (GApplication *app)
 {
   const char *quit_accels[2] = { "<Ctrl>Q", NULL };
   const char *open_accels[2] = { "<Ctrl>O", NULL };
+  GtkCssProvider *provider;
 
   G_APPLICATION_CLASS (node_editor_application_parent_class)->startup (app);
 
@@ -66,6 +77,13 @@ node_editor_application_startup (GApplication *app)
                                    app);
   gtk_application_set_accels_for_action (GTK_APPLICATION (app), "app.quit", quit_accels);
   gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.open", open_accels);
+
+
+  provider = gtk_css_provider_new ();
+  gtk_css_provider_load_from_data (provider, css, -1);
+  gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+                                              GTK_STYLE_PROVIDER (provider),
+                                              GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 }
 
 static void
diff --git a/demos/node-editor/node-editor-window.c b/demos/node-editor/node-editor-window.c
index ef61e9f20d..cb9f78c349 100644
--- a/demos/node-editor/node-editor-window.c
+++ b/demos/node-editor/node-editor-window.c
@@ -39,6 +39,7 @@ struct _NodeEditorWindow
   GtkWidget *picture;
   GtkWidget *text_view;
   GtkTextBuffer *text_buffer;
+  GtkTextTagTable *tag_table;
 
   GtkWidget *renderer_listbox;
   GListStore *renderers;
@@ -99,6 +100,42 @@ deserialize_error_func (const GtkCssSection *section,
   g_array_append_val (self->errors, text_view_error);
 }
 
+static void
+text_iter_skip_alpha_backward (GtkTextIter *iter)
+{
+  /* Just skip to the previous non-whitespace char */
+
+  while (!gtk_text_iter_is_start (iter))
+    {
+      gunichar c = gtk_text_iter_get_char (iter);
+
+      if (g_unichar_isspace (c))
+        {
+          gtk_text_iter_forward_char (iter);
+          break;
+        }
+
+      gtk_text_iter_backward_char (iter);
+    }
+}
+
+static void
+text_iter_skip_whitespace_backward (GtkTextIter *iter)
+{
+  while (!gtk_text_iter_is_start (iter))
+    {
+      gunichar c = gtk_text_iter_get_char (iter);
+
+      if (g_unichar_isalpha (c))
+        {
+          gtk_text_iter_forward_char (iter);
+          break;
+        }
+
+      gtk_text_iter_backward_char (iter);
+    }
+}
+
 static void
 text_changed (GtkTextBuffer    *buffer,
               NodeEditorWindow *self)
@@ -141,6 +178,71 @@ text_changed (GtkTextBuffer    *buffer,
     {
       gtk_picture_set_paintable (GTK_PICTURE (self->picture), NULL);
     }
+
+  GtkTextIter iter;
+
+  gtk_text_buffer_get_start_iter (self->text_buffer, &iter);
+
+  while (!gtk_text_iter_is_end (&iter))
+    {
+      gunichar c = gtk_text_iter_get_char (&iter);
+
+      if (c == '{')
+        {
+          GtkTextIter word_end = iter;
+          GtkTextIter word_start;
+
+          gtk_text_iter_backward_char (&word_end);
+          text_iter_skip_whitespace_backward (&word_end);
+
+          word_start = word_end;
+          gtk_text_iter_backward_word_start (&word_start);
+          text_iter_skip_alpha_backward (&word_start);
+
+          gtk_text_buffer_apply_tag_by_name (self->text_buffer, "nodename",
+                                             &word_start, &word_end);
+        }
+      else if (c == ':')
+        {
+          GtkTextIter word_end = iter;
+          GtkTextIter word_start;
+
+          gtk_text_iter_backward_char (&word_end);
+          text_iter_skip_whitespace_backward (&word_end);
+
+          word_start = word_end;
+          gtk_text_iter_backward_word_start (&word_start);
+          text_iter_skip_alpha_backward (&word_start);
+
+          gtk_text_buffer_apply_tag_by_name (self->text_buffer, "propname",
+                                             &word_start, &word_end);
+        }
+      else if (c == '"')
+        {
+          GtkTextIter string_start = iter;
+          GtkTextIter string_end = iter;
+
+          gtk_text_iter_forward_char (&iter);
+          while (!gtk_text_iter_is_end (&iter))
+            {
+              c = gtk_text_iter_get_char (&iter);
+
+              if (c == '"')
+                {
+                  gtk_text_iter_forward_char (&iter);
+                  string_end = iter;
+                  break;
+                }
+
+              gtk_text_iter_forward_char (&iter);
+            }
+
+          gtk_text_buffer_apply_tag_by_name (self->text_buffer, "string",
+                                             &string_start, &string_end);
+        }
+
+      gtk_text_iter_forward_char (&iter);
+    }
 }
 
 static gboolean
@@ -493,12 +595,10 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
   widget_class->realize = node_editor_window_realize;
   widget_class->unrealize = node_editor_window_unrealize;
 
-  gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_buffer);
   gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_view);
   gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, picture);
   gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, renderer_listbox);
 
-  gtk_widget_class_bind_template_callback (widget_class, text_changed);
   gtk_widget_class_bind_template_callback (widget_class, text_view_query_tooltip_cb);
   gtk_widget_class_bind_template_callback (widget_class, open_cb);
   gtk_widget_class_bind_template_callback (widget_class, save_cb);
@@ -559,6 +659,37 @@ node_editor_window_init (NodeEditorWindow *self)
   g_array_set_clear_func (self->errors, (GDestroyNotify)text_view_error_free);
 
   g_action_map_add_action_entries (G_ACTION_MAP (self), win_entries, G_N_ELEMENTS (win_entries), self);
+
+  self->tag_table = gtk_text_tag_table_new ();
+  gtk_text_tag_table_add (self->tag_table,
+                          g_object_new (GTK_TYPE_TEXT_TAG,
+                                        "name", "error",
+                                        "underline", PANGO_UNDERLINE_ERROR,
+                                        NULL));
+  gtk_text_tag_table_add (self->tag_table,
+                          g_object_new (GTK_TYPE_TEXT_TAG,
+                                        "name", "nodename",
+                                        "foreground-rgba", &(GdkRGBA) { 0.9, 0.78, 0.53, 1},
+                                        NULL));
+  gtk_text_tag_table_add (self->tag_table,
+                          g_object_new (GTK_TYPE_TEXT_TAG,
+                                        "name", "propname",
+                                        "foreground-rgba", &(GdkRGBA) { 0.7, 0.55, 0.67, 1},
+                                        NULL));
+  gtk_text_tag_table_add (self->tag_table,
+                          g_object_new (GTK_TYPE_TEXT_TAG,
+                                        "name", "string",
+                                        "foreground-rgba", &(GdkRGBA) { 0.63, 0.73, 0.54, 1},
+                                        NULL));
+  gtk_text_tag_table_add (self->tag_table,
+                          g_object_new (GTK_TYPE_TEXT_TAG,
+                                        "name", "number",
+                                        "foreground-rgba", &(GdkRGBA) { 0.8, 0.52, 0.43, 1},
+                                        NULL));
+
+  self->text_buffer = gtk_text_buffer_new (self->tag_table);
+  g_signal_connect (self->text_buffer, "changed", G_CALLBACK (text_changed), self);
+  gtk_text_view_set_buffer (GTK_TEXT_VIEW (self->text_view), self->text_buffer);
 }
 
 NodeEditorWindow *
diff --git a/demos/node-editor/node-editor-window.ui b/demos/node-editor/node-editor-window.ui
index 0ce06be89e..d254d992b3 100644
--- a/demos/node-editor/node-editor-window.ui
+++ b/demos/node-editor/node-editor-window.ui
@@ -1,17 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <object class="GtkTextTagTable" id="tags">
-    <child type="tag">
-      <object class="GtkTextTag">
-        <property name="name">error</property>
-        <property name="underline">error</property>
-      </object>
-    </child>
-  </object>
-  <object class="GtkTextBuffer" id="text_buffer">
-    <property name="tag-table">tags</property>
-    <signal name="changed" handler="text_changed"/>
-  </object>
   <template class="NodeEditorWindow" parent="GtkApplicationWindow">
     <style>
       <class name="devel"/>
@@ -61,7 +49,6 @@
             <property name="expand">1</property>
             <child>
               <object class="GtkTextView" id="text_view">
-                <property name="buffer">text_buffer</property>
                 <property name="wrap-mode">word</property>
                 <property name="monospace">1</property>
                 <property name="has-focus">1</property>
@@ -71,6 +58,9 @@
                 <property name="bottom-margin">6</property>
                 <property name="has-tooltip">1</property>
                 <signal name="query-tooltip" handler="text_view_query_tooltip_cb"/>
+                <style>
+                  <class name="editor" />
+                </style>
               </object>
             </child>
           </object>


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