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?
This commit is contained in:
Simon Gomizelj 2012-06-03 23:17:18 -04:00
parent afdad33118
commit fdd413c854
1 changed files with 55 additions and 54 deletions

107
termite.c
View File

@ -17,16 +17,21 @@
# define __attribute__(x) # define __attribute__(x)
#endif #endif
enum overlay_mode {
OVERLAY_HIDDEN,
OVERLAY_SEARCH,
OVERLAY_COMPLETION
};
typedef struct search_panel_info { typedef struct search_panel_info {
GtkWidget *vte; GtkWidget *vte;
GtkWidget *entry; GtkWidget *entry;
GtkBin *panel; GtkBin *panel;
enum overlay_mode mode;
bool reverse; bool reverse;
} search_panel_info; } search_panel_info;
static gboolean always_selected(__attribute__((unused)) VteTerminal *vte, static gboolean always_selected() {
__attribute__((unused)) glong column,
__attribute__((unused)) glong row) {
return TRUE; return TRUE;
} }
@ -74,52 +79,6 @@ static GtkTreeModel *create_completion_model(VteTerminal *vte) {
return GTK_TREE_MODEL(store); 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) { static void search(VteTerminal *vte, const char *pattern, bool reverse) {
GRegex *regex = vte_terminal_search_get_gregex(vte); GRegex *regex = vte_terminal_search_get_gregex(vte);
if (regex) g_regex_unref(regex); 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); 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; gboolean ret = FALSE;
if (event->keyval == GDK_KEY_Escape) { if (event->keyval == GDK_KEY_Escape) {
ret = TRUE; ret = TRUE;
} else if (event->keyval == GDK_KEY_Return) { } else if (event->keyval == GDK_KEY_Return) {
search(VTE_TERMINAL(info->vte), gtk_entry_get_text(entry), info->reverse); const gchar *text = gtk_entry_get_text(entry);
switch (info->mode) {
case OVERLAY_SEARCH:
search(VTE_TERMINAL(info->vte), text, info->reverse);
ret = TRUE; ret = TRUE;
break;
case OVERLAY_COMPLETION:
vte_terminal_feed(VTE_TERMINAL(info->vte), text, -1);
ret = TRUE;
break;
default:
ret = TRUE;
break;
}
} }
if (ret) { if (ret) {
info->mode = OVERLAY_HIDDEN;
gtk_widget_hide(GTK_WIDGET(info->panel)); gtk_widget_hide(GTK_WIDGET(info->panel));
gtk_widget_grab_focus(info->vte); 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); vte_terminal_copy_primary(vte);
return TRUE; return TRUE;
case KEY(KEY_SEARCH): case KEY(KEY_SEARCH):
info->mode = OVERLAY_SEARCH;
info->reverse = false; info->reverse = false;
gtk_widget_show(GTK_WIDGET(info->panel)); gtk_widget_show(GTK_WIDGET(info->panel));
gtk_widget_grab_focus(info->entry); gtk_widget_grab_focus(info->entry);
return TRUE; return TRUE;
case KEY(KEY_RSEARCH): case KEY(KEY_RSEARCH):
info->mode = OVERLAY_SEARCH;
info->reverse = true; info->reverse = true;
gtk_widget_show(GTK_WIDGET(info->panel)); gtk_widget_show(GTK_WIDGET(info->panel));
gtk_widget_grab_focus(info->entry); 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); search(vte, url_regex, true);
return 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) { if (modifiers == GDK_CONTROL_MASK && event->keyval == GDK_KEY_Tab) {
do_entry_completion(vte); 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(overlay), vte);
gtk_container_add(GTK_CONTAINER(window), overlay); 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(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(vte, "child-exited", 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(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); g_signal_connect(overlay, "get-child-position", G_CALLBACK(position_overlay_cb), NULL);
vte_terminal_set_scrollback_lines(VTE_TERMINAL(vte), scrollback_lines); vte_terminal_set_scrollback_lines(VTE_TERMINAL(vte), scrollback_lines);