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