From fdd413c854b446db3432e3fa207688df5739108e Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Sun, 3 Jun 2012 23:17:18 -0400 Subject: [PATCH 1/5] Move scrollback completion into the overlay. I also implemented the logic needed to "simulate" the input to the Vte. As a bonus, the search function also gets completion (this was unintentional but interesting enough that I let it be). Visible issues: - The down key doesn't pop up the completion, it gives focus back to the terminal. - The enter key does not select an item from the drop down list, it closes the entry and accepts the input as is. Maybe the completion popup delay should be configurable and the default value decreased? --- termite.c | 109 +++++++++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/termite.c b/termite.c index a1dd3c9..e31dde8 100644 --- a/termite.c +++ b/termite.c @@ -17,16 +17,21 @@ # define __attribute__(x) #endif +enum overlay_mode { + OVERLAY_HIDDEN, + OVERLAY_SEARCH, + OVERLAY_COMPLETION +}; + typedef struct search_panel_info { GtkWidget *vte; GtkWidget *entry; GtkBin *panel; + enum overlay_mode mode; bool reverse; } search_panel_info; -static gboolean always_selected(__attribute__((unused)) VteTerminal *vte, - __attribute__((unused)) glong column, - __attribute__((unused)) glong row) { +static gboolean always_selected() { return TRUE; } @@ -74,52 +79,6 @@ static GtkTreeModel *create_completion_model(VteTerminal *vte) { return GTK_TREE_MODEL(store); } -// TODO: turn this into an overlay -static GtkWidget *test_window = NULL; -static GtkWidget *do_entry_completion(VteTerminal *vte) { - GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(vte)); - - if (!test_window) { - test_window = gtk_dialog_new_with_buttons("GtkEntryCompletion", - GTK_WINDOW(window), - (GtkDialogFlags)0, - NULL, - NULL); - gtk_window_set_resizable(GTK_WINDOW(test_window), FALSE); - - g_signal_connect(test_window, "response", G_CALLBACK(gtk_widget_destroy), NULL); - g_signal_connect(test_window, "destroy", G_CALLBACK(gtk_widget_destroyed), &test_window); - - GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(test_window)); - - // Create our entry - GtkWidget *entry = gtk_entry_new(); - gtk_container_add(GTK_CONTAINER(content_area), entry); - - // Create the completion object - GtkEntryCompletion *completion = gtk_entry_completion_new(); - - // Assign the completion to the entry - gtk_entry_set_completion(GTK_ENTRY(entry), completion); - g_object_unref(completion); - - // Create a tree model and use it as the completion model - GtkTreeModel *completion_model = create_completion_model(vte); - gtk_entry_completion_set_model(completion, completion_model); - g_object_unref(completion_model); - - // Use model column 0 as the text column - gtk_entry_completion_set_text_column(completion, 0); - } - - if (!gtk_widget_get_visible(test_window)) - gtk_widget_show_all(test_window); - else - gtk_widget_destroy(test_window); - - return test_window; -} - static void search(VteTerminal *vte, const char *pattern, bool reverse) { GRegex *regex = vte_terminal_search_get_gregex(vte); if (regex) g_regex_unref(regex); @@ -134,17 +93,31 @@ static void search(VteTerminal *vte, const char *pattern, bool reverse) { vte_terminal_copy_primary(vte); } -static gboolean search_key_press_cb(GtkEntry *entry, GdkEventKey *event, search_panel_info *info) { +static gboolean entry_key_press_cb(GtkEntry *entry, GdkEventKey *event, search_panel_info *info) { gboolean ret = FALSE; if (event->keyval == GDK_KEY_Escape) { ret = TRUE; } else if (event->keyval == GDK_KEY_Return) { - search(VTE_TERMINAL(info->vte), gtk_entry_get_text(entry), info->reverse); - ret = TRUE; + const gchar *text = gtk_entry_get_text(entry); + + switch (info->mode) { + case OVERLAY_SEARCH: + search(VTE_TERMINAL(info->vte), text, info->reverse); + ret = TRUE; + break; + case OVERLAY_COMPLETION: + vte_terminal_feed(VTE_TERMINAL(info->vte), text, -1); + ret = TRUE; + break; + default: + ret = TRUE; + break; + } } if (ret) { + info->mode = OVERLAY_HIDDEN; gtk_widget_hide(GTK_WIDGET(info->panel)); gtk_widget_grab_focus(info->vte); } @@ -170,11 +143,13 @@ static gboolean key_press_cb(VteTerminal *vte, GdkEventKey *event, search_panel_ vte_terminal_copy_primary(vte); return TRUE; case KEY(KEY_SEARCH): + info->mode = OVERLAY_SEARCH; info->reverse = false; gtk_widget_show(GTK_WIDGET(info->panel)); gtk_widget_grab_focus(info->entry); return TRUE; case KEY(KEY_RSEARCH): + info->mode = OVERLAY_SEARCH; info->reverse = true; gtk_widget_show(GTK_WIDGET(info->panel)); gtk_widget_grab_focus(info->entry); @@ -186,6 +161,26 @@ static gboolean key_press_cb(VteTerminal *vte, GdkEventKey *event, search_panel_ search(vte, url_regex, true); return TRUE; } + } else if (modifiers == GDK_CONTROL_MASK && event->keyval == GDK_KEY_Tab) { + // Create the completion object + GtkEntryCompletion *completion = gtk_entry_completion_new(); + + // Assign the completion to the entry + gtk_entry_set_completion(GTK_ENTRY(info->entry), completion); + g_object_unref(completion); + + // Create a tree model and use it as the completion model + GtkTreeModel *completion_model = create_completion_model(vte); + gtk_entry_completion_set_model(completion, completion_model); + g_object_unref(completion_model); + + // Use model column 0 as the text column + gtk_entry_completion_set_text_column(completion, 0); + + info->mode = OVERLAY_COMPLETION; + gtk_widget_show(GTK_WIDGET(info->panel)); + gtk_widget_grab_focus(info->entry); + return TRUE; } if (modifiers == GDK_CONTROL_MASK && event->keyval == GDK_KEY_Tab) { do_entry_completion(vte); @@ -343,12 +338,18 @@ int main(int argc, char **argv) { gtk_container_add(GTK_CONTAINER(overlay), vte); gtk_container_add(GTK_CONTAINER(window), overlay); - search_panel_info info = {vte, entry, GTK_BIN(alignment), false}; + search_panel_info info = { + .vte = vte, + .entry = entry, + .panel = GTK_BIN(alignment), + .mode = OVERLAY_HIDDEN, + .reverse = false + }; g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(vte, "child-exited", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(vte, "key-press-event", G_CALLBACK(key_press_cb), &info); - g_signal_connect(entry, "key-press-event", G_CALLBACK(search_key_press_cb), &info); + g_signal_connect(entry, "key-press-event", G_CALLBACK(entry_key_press_cb), &info); g_signal_connect(overlay, "get-child-position", G_CALLBACK(position_overlay_cb), NULL); vte_terminal_set_scrollback_lines(VTE_TERMINAL(vte), scrollback_lines); From 63293797f37793d5f233749d801b9b4fd4daf051 Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Sun, 3 Jun 2012 23:37:15 -0400 Subject: [PATCH 2/5] Add overlay_show to reduce repetition. --- termite.c | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/termite.c b/termite.c index e31dde8..f4c0dd5 100644 --- a/termite.c +++ b/termite.c @@ -20,6 +20,7 @@ enum overlay_mode { OVERLAY_HIDDEN, OVERLAY_SEARCH, + OVERLAY_RSEARCH, OVERLAY_COMPLETION }; @@ -28,7 +29,6 @@ typedef struct search_panel_info { GtkWidget *entry; GtkBin *panel; enum overlay_mode mode; - bool reverse; } search_panel_info; static gboolean always_selected() { @@ -103,7 +103,11 @@ static gboolean entry_key_press_cb(GtkEntry *entry, GdkEventKey *event, search_p switch (info->mode) { case OVERLAY_SEARCH: - search(VTE_TERMINAL(info->vte), text, info->reverse); + search(VTE_TERMINAL(info->vte), text, false); + ret = TRUE; + break; + case OVERLAY_RSEARCH: + search(VTE_TERMINAL(info->vte), text, true); ret = TRUE; break; case OVERLAY_COMPLETION: @@ -124,6 +128,24 @@ static gboolean entry_key_press_cb(GtkEntry *entry, GdkEventKey *event, search_p return ret; } +static void overlay_show(search_panel_info *info, enum overlay_mode mode, bool complete) { + if (complete) { + GtkEntryCompletion *completion = gtk_entry_completion_new(); + gtk_entry_set_completion(GTK_ENTRY(info->entry), completion); + g_object_unref(completion); + + GtkTreeModel *completion_model = create_completion_model(VTE_TERMINAL(info->vte)); + gtk_entry_completion_set_model(completion, completion_model); + g_object_unref(completion_model); + + gtk_entry_completion_set_text_column(completion, 0); + } + + info->mode = mode; + gtk_widget_show(GTK_WIDGET(info->panel)); + gtk_widget_grab_focus(info->entry); +} + static gboolean key_press_cb(VteTerminal *vte, GdkEventKey *event, search_panel_info *info) { const guint modifiers = event->state & gtk_accelerator_get_default_mod_mask(); if (modifiers == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) { @@ -143,16 +165,10 @@ static gboolean key_press_cb(VteTerminal *vte, GdkEventKey *event, search_panel_ vte_terminal_copy_primary(vte); return TRUE; case KEY(KEY_SEARCH): - info->mode = OVERLAY_SEARCH; - info->reverse = false; - gtk_widget_show(GTK_WIDGET(info->panel)); - gtk_widget_grab_focus(info->entry); + overlay_show(info, OVERLAY_SEARCH, true); return TRUE; case KEY(KEY_RSEARCH): - info->mode = OVERLAY_SEARCH; - info->reverse = true; - gtk_widget_show(GTK_WIDGET(info->panel)); - gtk_widget_grab_focus(info->entry); + overlay_show(info, OVERLAY_RSEARCH, true); return TRUE; case KEY(KEY_URL): search(vte, url_regex, false); @@ -162,24 +178,7 @@ static gboolean key_press_cb(VteTerminal *vte, GdkEventKey *event, search_panel_ return TRUE; } } else if (modifiers == GDK_CONTROL_MASK && event->keyval == GDK_KEY_Tab) { - // Create the completion object - GtkEntryCompletion *completion = gtk_entry_completion_new(); - - // Assign the completion to the entry - gtk_entry_set_completion(GTK_ENTRY(info->entry), completion); - g_object_unref(completion); - - // Create a tree model and use it as the completion model - GtkTreeModel *completion_model = create_completion_model(vte); - gtk_entry_completion_set_model(completion, completion_model); - g_object_unref(completion_model); - - // Use model column 0 as the text column - gtk_entry_completion_set_text_column(completion, 0); - - info->mode = OVERLAY_COMPLETION; - gtk_widget_show(GTK_WIDGET(info->panel)); - gtk_widget_grab_focus(info->entry); + overlay_show(info, OVERLAY_COMPLETION, true); return TRUE; } if (modifiers == GDK_CONTROL_MASK && event->keyval == GDK_KEY_Tab) { @@ -343,7 +342,6 @@ int main(int argc, char **argv) { .entry = entry, .panel = GTK_BIN(alignment), .mode = OVERLAY_HIDDEN, - .reverse = false }; g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); From 90bb50597a33a8ab980a09a3a56ab91406d6d787 Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Sun, 3 Jun 2012 23:39:19 -0400 Subject: [PATCH 3/5] You prefer this inlined. --- termite.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/termite.c b/termite.c index f4c0dd5..1c4447a 100644 --- a/termite.c +++ b/termite.c @@ -18,7 +18,7 @@ #endif enum overlay_mode { - OVERLAY_HIDDEN, + OVERLAY_HIDDEN = 0, OVERLAY_SEARCH, OVERLAY_RSEARCH, OVERLAY_COMPLETION @@ -337,12 +337,7 @@ int main(int argc, char **argv) { gtk_container_add(GTK_CONTAINER(overlay), vte); gtk_container_add(GTK_CONTAINER(window), overlay); - search_panel_info info = { - .vte = vte, - .entry = entry, - .panel = GTK_BIN(alignment), - .mode = OVERLAY_HIDDEN, - }; + search_panel_info info = {vte, entry, GTK_BIN(alignment), OVERLAY_HIDDEN}; g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(vte, "child-exited", G_CALLBACK(gtk_main_quit), NULL); From cc8bb5224d741ad9489851f31365edd4618d1d15 Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Sun, 3 Jun 2012 23:49:39 -0400 Subject: [PATCH 4/5] Small formatting changes. --- termite.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/termite.c b/termite.c index 1c4447a..8f319a7 100644 --- a/termite.c +++ b/termite.c @@ -52,11 +52,10 @@ static GtkTreeModel *create_completion_model(VteTerminal *vte) { // TODO: get the full buffer gchar *content = vte_terminal_get_text(vte, (VteSelectionFunc)always_selected, - NULL, - NULL); + NULL, NULL); if (!content) { - fputs("no content", stderr); + g_printerr("no content"); exit(EXIT_FAILURE); } @@ -104,20 +103,17 @@ static gboolean entry_key_press_cb(GtkEntry *entry, GdkEventKey *event, search_p switch (info->mode) { case OVERLAY_SEARCH: search(VTE_TERMINAL(info->vte), text, false); - ret = TRUE; break; case OVERLAY_RSEARCH: search(VTE_TERMINAL(info->vte), text, true); - ret = TRUE; break; case OVERLAY_COMPLETION: vte_terminal_feed(VTE_TERMINAL(info->vte), text, -1); - ret = TRUE; break; - default: - ret = TRUE; + case OVERLAY_HIDDEN: break; } + ret = TRUE; } if (ret) { From 586dff08ec543cdc1600a8f360b3ddbbd819d675 Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Mon, 4 Jun 2012 00:24:39 -0400 Subject: [PATCH 5/5] Clear the entry and use vte_terminal_feed_child. vte_terminal_feed write to the underling vte while vte_terminal_feed_child properly simulates keyboard input. The later is needed, otherwise input can't be erased. --- termite.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/termite.c b/termite.c index 8f319a7..c21902b 100644 --- a/termite.c +++ b/termite.c @@ -72,8 +72,6 @@ static GtkTreeModel *create_completion_model(VteTerminal *vte) { } g_tree_foreach(tree, (GTraverseFunc)add_to_list_store, store); - g_tree_destroy(tree); - g_free(content); return GTK_TREE_MODEL(store); } @@ -108,7 +106,7 @@ static gboolean entry_key_press_cb(GtkEntry *entry, GdkEventKey *event, search_p search(VTE_TERMINAL(info->vte), text, true); break; case OVERLAY_COMPLETION: - vte_terminal_feed(VTE_TERMINAL(info->vte), text, -1); + vte_terminal_feed_child(VTE_TERMINAL(info->vte), text, -1); break; case OVERLAY_HIDDEN: break; @@ -137,6 +135,8 @@ static void overlay_show(search_panel_info *info, enum overlay_mode mode, bool c gtk_entry_completion_set_text_column(completion, 0); } + gtk_entry_set_text(GTK_ENTRY(info->entry), ""); + info->mode = mode; gtk_widget_show(GTK_WIDGET(info->panel)); gtk_widget_grab_focus(info->entry); @@ -177,10 +177,6 @@ static gboolean key_press_cb(VteTerminal *vte, GdkEventKey *event, search_panel_ overlay_show(info, OVERLAY_COMPLETION, true); return TRUE; } - if (modifiers == GDK_CONTROL_MASK && event->keyval == GDK_KEY_Tab) { - do_entry_completion(vte); - return TRUE; - } return FALSE; }