diff --git a/gtk3/console_window.c b/gtk3/console_window.c
index d8ee71f..906ca12 100644
--- a/gtk3/console_window.c
+++ b/gtk3/console_window.c
@@ -17,7 +17,9 @@ struct _ConsoleWindow {
bool log_to_sidebar;
bool clear_sidebar;
- GList *command_history;
+ GtkEntryCompletion *command_completion;
+ guint command_history_len;
+ gint command_history_index;
};
G_DEFINE_TYPE(ConsoleWindow, console_window, GTK_TYPE_WINDOW);
@@ -28,6 +30,74 @@ typedef struct {
bool sidebar;
} AttributedMessage;
+static gboolean on_input_key_press(GtkEntry *input, GdkEventKey *event, ConsoleWindow *self) {
+ switch (event->keyval) {
+ case GDK_KEY_Up:
+ if (event->type == GDK_KEY_PRESS) {
+ if (self->command_history_index + 1 == self->command_history_len) {
+ self->command_history_index = self->command_history_len - 1;
+ return true;
+ }
+
+ self->command_history_index += 1;
+
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_entry_completion_get_model(self->command_completion);
+
+ if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, self->command_history_index)) {
+ return true;
+ }
+
+ const char *entry = NULL;
+ gtk_tree_model_get(model, &iter, 0, &entry, -1);
+ gtk_entry_set_text(self->input, entry);
+ gtk_editable_set_position(GTK_EDITABLE(input), -1);
+ }
+
+ return true;
+ break;
+
+ case GDK_KEY_Down:
+ if (event->type == GDK_KEY_PRESS) {
+ if (self->command_history_index <= 0) {
+ gtk_entry_set_text(self->input, "");
+ self->command_history_index = -1;
+ }
+
+ if (self->command_history_index == -1) {
+ return true;
+ }
+
+ self->command_history_index -= 1;
+
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_entry_completion_get_model(self->command_completion);
+
+ if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, self->command_history_index)) {
+ return true;
+ }
+
+ const char *entry = NULL;
+ gtk_tree_model_get(model, &iter, 0, &entry, -1);
+ gtk_entry_set_text(self->input, entry);
+ gtk_editable_set_position(GTK_EDITABLE(input), -1);
+ }
+
+ return true;
+ break;
+
+ case GDK_KEY_Tab:
+ if (event->type == GDK_KEY_PRESS) {
+ gtk_editable_set_position(GTK_EDITABLE(input), -1);
+ }
+
+ return true;
+ break;
+ }
+
+ return false;
+}
+
static void console_window_init(ConsoleWindow *self) {
gtk_widget_init_template(GTK_WIDGET(self));
@@ -49,7 +119,22 @@ static void console_window_init(ConsoleWindow *self) {
self->output_queue = g_async_queue_new();
self->log_to_sidebar = false;
self->clear_sidebar = false;
- self->command_history = NULL;
+ self->command_history_index = -1;
+
+ GtkTreeIter iter;
+ GtkListStore *command_list_store = gtk_list_store_new(1, G_TYPE_STRING);
+ self->command_completion = gtk_entry_completion_new();
+ gtk_entry_completion_set_model(self->command_completion, GTK_TREE_MODEL(command_list_store));
+ gtk_entry_completion_set_text_column(self->command_completion, 0);
+ gtk_entry_completion_set_popup_completion(self->command_completion, false);
+ gtk_entry_completion_set_inline_completion(self->command_completion, true);
+ gtk_entry_set_completion(self->input, self->command_completion);
+ gtk_entry_set_input_hints(self->input, GTK_INPUT_HINT_NO_SPELLCHECK | GTK_INPUT_HINT_NO_EMOJI);
+
+ gtk_widget_add_events(GTK_WIDGET(self), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
+
+ g_signal_connect(self->input, "key-press-event", G_CALLBACK(on_input_key_press), self);
+ g_signal_connect(self->input, "key-release-event", G_CALLBACK(on_input_key_press), self);
}
static void console_window_realize(GtkWidget *widget) {
@@ -69,28 +154,44 @@ static void log_simple(ConsoleWindow *self, const char *message) {
}
// TODO: Use command history (arrow key (↑, ↓) events)
-static void on_input(GtkEntry *input, ConsoleWindow *self) {
+static void on_input_enter(GtkEntry *input, ConsoleWindow *self) {
const gchar *text_ptr = gtk_entry_get_text(input);
const gchar *text = NULL;
+ GtkTreeModel *model = gtk_entry_completion_get_model(self->command_completion);
+
if (g_strcmp0("", text_ptr) == 0) {
- GList *last = g_list_last(self->command_history);
- if (last) text = last->data;
+ const char *last = NULL;
+ GtkTreeIter iter;
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ gtk_tree_model_get(model, &iter, 0, &last, -1);
+ }
+
+ if (last) text = last;
}
else {
text = text_ptr;
}
if (text) {
- GList *last = g_list_last(self->command_history);
+ const char *last = NULL;
+
+ GtkTreeIter iter;
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ gtk_tree_model_get(model, &iter, 0, &last, -1);
+ }
// Add command to queue unless it was the last command issued
- if (!last || g_strcmp0(last->data, text) != 0) {
- self->command_history = g_list_append(self->command_history, g_strdup(text));
+ if (!last || g_strcmp0(last, text) != 0) {
+ gtk_list_store_prepend(GTK_LIST_STORE(model), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, text, -1);
+ self->command_history_len += 1;
}
g_async_queue_push(self->input_queue, (gpointer) g_strdup(text));
gtk_entry_set_text(self->input, "");
+
+ self->command_history_index = -1;
}
}
@@ -225,7 +326,7 @@ static void console_window_class_init(ConsoleWindowClass *class) {
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), ConsoleWindow, sidebar_input);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), ConsoleWindow, sidebar_output);
- gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_input);
+ gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_input_enter);
GTK_WIDGET_CLASS(class)->realize = console_window_realize;
GTK_WIDGET_CLASS(class)->draw = console_window_draw;
diff --git a/gtk3/main.c b/gtk3/main.c
index b2af5ed..9186cbe 100644
--- a/gtk3/main.c
+++ b/gtk3/main.c
@@ -1126,8 +1126,8 @@ static void connect_signal_handlers(GApplication *app) {
gtk_widget_add_events(GTK_WIDGET(gui_data.main_window), GDK_KEY_RELEASE_MASK);
g_signal_connect(gui_data.main_window, "destroy", G_CALLBACK(on_quit_activate), app);
- g_signal_connect(gui_data.main_window, "key_press_event", G_CALLBACK(on_key_press), NULL);
- g_signal_connect(gui_data.main_window, "key_release_event", G_CALLBACK(on_key_press), NULL);
+ g_signal_connect(gui_data.main_window, "key-press-event", G_CALLBACK(on_key_press), NULL);
+ g_signal_connect(gui_data.main_window, "key-release-event", G_CALLBACK(on_key_press), NULL);
g_signal_connect(gui_data.main_window, "window-state-event", G_CALLBACK(on_window_state_change), NULL);
// Just hide our sub-windows when closing them
diff --git a/gtk3/resources/ui/console_window.ui b/gtk3/resources/ui/console_window.ui
index f18c2b6..92009a6 100644
--- a/gtk3/resources/ui/console_window.ui
+++ b/gtk3/resources/ui/console_window.ui
@@ -182,7 +182,7 @@ Author: Maximilian Mader
3
3
Console input
-
+
False