[gtksourceview/wip/chergert/vim: 108/363] add some plumbing to observe keys




commit c58f4e22505d28f30a0b46b9c215c3d21496d481
Author: Christian Hergert <chergert redhat com>
Date:   Thu Oct 28 09:31:34 2021 -0700

    add some plumbing to observe keys
    
    probably want to find a way to make this private eventually

 gtksourceview/gtksourcevimimcontext.c     | 94 +++++++++++++++++++++++++++++++
 gtksourceview/vim/gtk-source-vim-normal.c | 22 ++++++++
 gtksourceview/vim/gtk-source-vim.c        | 18 ++++++
 gtksourceview/vim/gtk-source-vim.h        |  1 +
 tests/test-vim.c                          | 56 +++++++++++++++++-
 5 files changed, 188 insertions(+), 3 deletions(-)
---
diff --git a/gtksourceview/gtksourcevimimcontext.c b/gtksourceview/gtksourcevimimcontext.c
index 7be52fcf..f19fae22 100644
--- a/gtksourceview/gtksourcevimimcontext.c
+++ b/gtksourceview/gtksourcevimimcontext.c
@@ -31,6 +31,7 @@ struct _GtkSourceVimIMContext
 {
        GtkIMContext  parent_instance;
        GtkSourceVim *vim;
+       guint         reset_observer : 1;
 };
 
 G_DEFINE_TYPE (GtkSourceVimIMContext, gtk_source_vim_im_context, GTK_TYPE_IM_CONTEXT)
@@ -44,12 +45,62 @@ enum {
 
 enum {
        EXECUTE_COMMAND,
+       OBSERVE_KEY,
        N_SIGNALS
 };
 
 static GParamSpec *properties[N_PROPS];
 static guint signals[N_SIGNALS];
 
+static inline void
+keyval_to_string (guint           keyval,
+                  GdkModifierType mods,
+                  char            str[16])
+{
+       int pos = 0;
+
+       if (keyval && (mods & GDK_CONTROL_MASK) != 0)
+       {
+               str[pos++] = '^';
+       }
+
+       switch (keyval)
+       {
+               case GDK_KEY_Escape:
+                       str[pos++] = '^';
+                       str[pos++] = '[';
+                       break;
+
+               case GDK_KEY_ISO_Left_Tab:
+               case GDK_KEY_Tab:
+                       str[pos++] = '\t';
+                       break;
+
+               case GDK_KEY_Return:
+               case GDK_KEY_KP_Enter:
+               case GDK_KEY_ISO_Enter:
+                       str[pos++] = '\n';
+                       break;
+
+               default:
+               {
+                       gunichar ch;
+
+                       /* ctrl things like ^M ^L are all uppercase */
+                       if ((mods & GDK_CONTROL_MASK) != 0)
+                               ch = gdk_keyval_to_unicode (gdk_keyval_to_upper (keyval));
+                       else
+                               ch = gdk_keyval_to_unicode (keyval);
+
+                       pos += g_unichar_to_utf8 (ch, &str[pos]);
+
+                       break;
+               }
+       }
+
+       str[pos] = 0;
+}
+
 GtkIMContext *
 gtk_source_vim_im_context_new (void)
 {
@@ -89,6 +140,16 @@ on_vim_execute_command_cb (GtkSourceVimIMContext *self,
        return ret;
 }
 
+static void
+on_vim_ready_cb (GtkSourceVimIMContext *self,
+                 GtkSourceVim          *vim)
+{
+       g_assert (GTK_SOURCE_IS_VIM_IM_CONTEXT (self));
+       g_assert (GTK_SOURCE_IS_VIM (vim));
+
+       self->reset_observer = TRUE;
+}
+
 static void
 gtk_source_vim_im_context_set_client_widget (GtkIMContext *context,
                                              GtkWidget    *widget)
@@ -117,6 +178,12 @@ gtk_source_vim_im_context_set_client_widget (GtkIMContext *context,
                                 self,
                                 G_CONNECT_SWAPPED);
 
+       g_signal_connect_object (self->vim,
+                                "ready",
+                                G_CALLBACK (on_vim_ready_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+
        g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COMMAND_TEXT]);
        g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COMMAND_BAR_TEXT]);
 }
@@ -159,6 +226,21 @@ gtk_source_vim_im_context_filter_keypress (GtkIMContext *context,
                return FALSE;
        }
 
+       if (gdk_event_get_event_type (event) == GDK_KEY_PRESS)
+       {
+               GdkModifierType mods;
+               guint keyval;
+               char str[16];
+
+               mods = gdk_event_get_modifier_state (event);
+               keyval = gdk_key_event_get_keyval (event);
+               keyval_to_string (keyval, mods, str);
+
+               g_signal_emit (self, signals[OBSERVE_KEY], 0, str, self->reset_observer);
+
+               self->reset_observer = FALSE;
+       }
+
        return gtk_source_vim_state_handle_event (GTK_SOURCE_VIM_STATE (self->vim), event);
 }
 
@@ -251,6 +333,18 @@ gtk_source_vim_im_context_class_init (GtkSourceVimIMContextClass *klass)
                              G_TYPE_BOOLEAN,
                              1,
                              G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+       signals[OBSERVE_KEY] =
+               g_signal_new ("observe-key",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE,
+                             2,
+                             G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
+                             G_TYPE_BOOLEAN);
 }
 
 static void
diff --git a/gtksourceview/vim/gtk-source-vim-normal.c b/gtksourceview/vim/gtk-source-vim-normal.c
index bea3909b..71b6dd6e 100644
--- a/gtksourceview/vim/gtk-source-vim-normal.c
+++ b/gtksourceview/vim/gtk-source-vim-normal.c
@@ -21,6 +21,7 @@
 
 #include "config.h"
 
+#include "gtk-source-vim.h"
 #include "gtk-source-vim-command-bar.h"
 #include "gtk-source-vim-delete.h"
 #include "gtk-source-vim-insert.h"
@@ -57,6 +58,21 @@ static gboolean key_handler_initial (GtkSourceVimNormal *self,
 
 G_DEFINE_TYPE (GtkSourceVimNormal, gtk_source_vim_normal, GTK_SOURCE_TYPE_VIM_STATE)
 
+static void
+gtk_source_vim_normal_emit_ready (GtkSourceVimNormal *self)
+{
+       GtkSourceVimState *parent;
+
+       g_assert (GTK_SOURCE_IS_VIM_NORMAL (self));
+
+       parent = gtk_source_vim_state_get_parent (GTK_SOURCE_VIM_STATE (self));
+
+       if (GTK_SOURCE_IS_VIM (parent))
+       {
+               gtk_source_vim_emit_ready (GTK_SOURCE_VIM (parent));
+       }
+}
+
 static gboolean
 gtk_source_vim_normal_bail (GtkSourceVimNormal *self)
 {
@@ -998,4 +1014,10 @@ gtk_source_vim_normal_clear (GtkSourceVimNormal *self)
        self->has_count = FALSE;
 
        g_string_truncate (self->command_text, 0);
+
+       /* Let the toplevel know we're back at steady state. This is
+        * basically just so observers can watch keys which makes it
+        * much easier to debug issues.
+        */
+       gtk_source_vim_normal_emit_ready (self);
 }
diff --git a/gtksourceview/vim/gtk-source-vim.c b/gtksourceview/vim/gtk-source-vim.c
index 9ba1d0d6..20eaaa8d 100644
--- a/gtksourceview/vim/gtk-source-vim.c
+++ b/gtksourceview/vim/gtk-source-vim.c
@@ -44,6 +44,7 @@ enum {
 
 enum {
        EXECUTE_COMMAND,
+       READY,
        SPLIT,
        N_SIGNALS
 };
@@ -193,6 +194,15 @@ gtk_source_vim_class_init (GtkSourceVimClass *klass)
                                            1,
                                            G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
 
+       signals[READY] =
+               g_signal_new ("ready",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE, 0);
+
        /**
         * GtkSourceVim::split:
         * @self: a #GtkSourceVim
@@ -307,3 +317,11 @@ gtk_source_vim_emit_execute_command (GtkSourceVim *self,
 
        return ret;
 }
+
+void
+gtk_source_vim_emit_ready (GtkSourceVim *self)
+{
+       g_return_if_fail (GTK_SOURCE_IS_VIM (self));
+
+       g_signal_emit (self, signals[READY], 0);
+}
diff --git a/gtksourceview/vim/gtk-source-vim.h b/gtksourceview/vim/gtk-source-vim.h
index 3e8efad5..ff8a7e3d 100644
--- a/gtksourceview/vim/gtk-source-vim.h
+++ b/gtksourceview/vim/gtk-source-vim.h
@@ -35,6 +35,7 @@ const char   *gtk_source_vim_get_command_text     (GtkSourceVim   *self);
 const char   *gtk_source_vim_get_command_bar_text (GtkSourceVim   *self);
 gboolean      gtk_source_vim_emit_execute_command (GtkSourceVim   *self,
                                                    const char     *command);
+void          gtk_source_vim_emit_ready           (GtkSourceVim   *self);
 void          gtk_source_vim_emit_split           (GtkSourceVim   *self,
                                                    GtkOrientation  orientation,
                                                    gboolean        new_document,
diff --git a/tests/test-vim.c b/tests/test-vim.c
index e5d86715..f3479e47 100644
--- a/tests/test-vim.c
+++ b/tests/test-vim.c
@@ -24,6 +24,21 @@
 #include <gtksourceview/gtksource.h>
 
 static GMainLoop *main_loop;
+static GString *sequence;
+
+#define OBSERVER_CSS \
+  "label.observer {" \
+  "    color: white;" \
+  "    font-size: 32pt;" \
+  "    font-weight: bold;" \
+  "    background: #2e3436;" \
+  "    border-radius: 15px;" \
+  "    min-width: 72px;" \
+  "    min-height: 72px;" \
+  "    box-shadow: 0 3px 10px 10px rgba(0,0,0,.09);" \
+  "    outline: 1px solid alpha(white, 0.125);" \
+  "    outline-offset: -2px;" \
+  "}"
 
 static gboolean
 execute_command (GtkSourceVimIMContext *context,
@@ -77,6 +92,18 @@ on_close_request (GtkWindow *window)
        return FALSE;
 }
 
+static void
+observe_key (GtkSourceVimIMContext *self,
+             const char            *str,
+             gboolean               reset_observer,
+             GtkLabel              *label)
+{
+       if (reset_observer)
+               g_string_truncate (sequence, 0);
+       g_string_append (sequence, str);
+       gtk_label_set_label (label, sequence->str);
+}
+
 int
 main (int argc,
       char *argv[])
@@ -84,6 +111,7 @@ main (int argc,
        GtkWindow *window;
        GtkSourceStyleSchemeManager *schemes;
        GtkSourceLanguageManager *languages;
+       GtkCssProvider *css;
        GtkScrolledWindow *scroller;
        GtkSourceView *view;
        GtkIMContext *im_context;
@@ -91,12 +119,21 @@ main (int argc,
        GtkSourceBuffer *buffer;
        GtkLabel *command_bar;
        GtkLabel *command;
+       GtkLabel *observe;
+       GtkOverlay *overlay;
        GtkBox *box;
        GFile *file;
 
        gtk_init ();
        gtk_source_init ();
 
+       css = gtk_css_provider_new ();
+       gtk_css_provider_load_from_data (css, OBSERVER_CSS, -1);
+       gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+                                                   GTK_STYLE_PROVIDER (css),
+                                                   GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+       sequence = g_string_new (NULL);
        schemes = gtk_source_style_scheme_manager_get_default ();
        languages = gtk_source_language_manager_get_default ();
 
@@ -105,6 +142,7 @@ main (int argc,
                               "default-width", 800,
                               "default-height", 600,
                               NULL);
+       overlay = g_object_new (GTK_TYPE_OVERLAY, NULL);
        scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW, NULL);
        buffer = gtk_source_buffer_new (NULL);
        gtk_source_buffer_set_language (buffer, gtk_source_language_manager_get_language (languages, "c"));
@@ -134,17 +172,29 @@ main (int argc,
                                "margin-bottom", 6,
                                "margin-end", 12,
                                NULL);
-       gtk_box_append (box, GTK_WIDGET (command_bar));
-       gtk_box_append (box, GTK_WIDGET (command));
+       observe = g_object_new (GTK_TYPE_LABEL,
+                               "halign", GTK_ALIGN_END,
+                               "valign", GTK_ALIGN_START,
+                               "margin-top", 24,
+                               "margin-bottom", 24,
+                               "margin-end", 24,
+                               "margin-start", 24,
+                               NULL);
+       gtk_widget_add_css_class (GTK_WIDGET (observe), "observer");
 
-       gtk_window_set_child (window, GTK_WIDGET (scroller));
+       gtk_window_set_child (window, GTK_WIDGET (overlay));
        gtk_scrolled_window_set_child (scroller, GTK_WIDGET (view));
        gtk_text_view_set_gutter (GTK_TEXT_VIEW (view), GTK_TEXT_WINDOW_BOTTOM, GTK_WIDGET (box));
+       gtk_box_append (box, GTK_WIDGET (command_bar));
+       gtk_box_append (box, GTK_WIDGET (command));
+       gtk_overlay_set_child (overlay, GTK_WIDGET (scroller));
+       gtk_overlay_add_overlay (overlay, GTK_WIDGET (observe));
 
        im_context = gtk_source_vim_im_context_new ();
        g_object_bind_property (im_context, "command-bar-text", command_bar, "label", G_BINDING_SYNC_CREATE);
        g_object_bind_property (im_context, "command-text", command, "label", G_BINDING_SYNC_CREATE);
        g_signal_connect (im_context, "execute-command", G_CALLBACK (execute_command), NULL);
+       g_signal_connect (im_context, "observe-key", G_CALLBACK (observe_key), observe);
        gtk_im_context_set_client_widget (im_context, GTK_WIDGET (view));
 
        key = gtk_event_controller_key_new ();


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