[GTK3] Make use of GB_debugger_complete_substring

This commit is contained in:
Maximilian Mader 2021-01-02 16:05:14 +01:00
parent 5049f400f0
commit 07f6da4c9e
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
4 changed files with 129 additions and 17 deletions

View File

@ -56,7 +56,7 @@ void check_menu_item_group_activate(CheckMenuItemGroup *group, char *arg) {
for (unsigned i = 0; i < group->count; i++) { for (unsigned i = 0; i < group->count; i++) {
GtkCheckMenuItem *cur = GTK_CHECK_MENU_ITEM(group->items[i]); GtkCheckMenuItem *cur = GTK_CHECK_MENU_ITEM(group->items[i]);
if (g_strcmp0(arg, group->handlers[i]->arg) == 0) { if (g_strcmp0(arg, group->handlers[i]->arg) == 0) {
gtk_check_menu_item_set_active(cur, true); gtk_check_menu_item_set_active(cur, true);
} }

View File

@ -2,6 +2,11 @@
#include "util.h" #include "util.h"
#include <stdbool.h> #include <stdbool.h>
struct Selection {
gint start;
gint end;
};
struct _ConsoleWindow { struct _ConsoleWindow {
GtkWindowClass parent_class; GtkWindowClass parent_class;
@ -20,16 +25,49 @@ struct _ConsoleWindow {
GtkEntryCompletion *command_completion; GtkEntryCompletion *command_completion;
guint command_history_len; guint command_history_len;
gint command_history_index; gint command_history_index;
struct Selection auto_complete_range;
uintptr_t auto_complete_context;
bool ignore_auto_complete_context_reset;
GB_gameboy_t *gb;
}; };
G_DEFINE_TYPE(ConsoleWindow, console_window, GTK_TYPE_WINDOW); G_DEFINE_TYPE(ConsoleWindow, console_window, GTK_TYPE_WINDOW);
typedef enum {
PROP_GB_PTR = 1,
N_PROPERTIES
} ConsoleWindowProperty;
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
typedef struct { typedef struct {
const char *message; const char *message;
GB_log_attributes attributes; GB_log_attributes attributes;
bool sidebar; bool sidebar;
} AttributedMessage; } AttributedMessage;
static void console_window_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
ConsoleWindow *self = (ConsoleWindow *) object;
switch ((ConsoleWindowProperty) property_id) {
case PROP_GB_PTR: self->gb = g_value_get_pointer(value); break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
static void console_window_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
ConsoleWindow *self = (ConsoleWindow *) object;
switch ((ConsoleWindowProperty) property_id) {
case PROP_GB_PTR: g_value_set_pointer(value, self->gb); break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
}
static gboolean on_input_key_press(GtkEntry *input, GdkEventKey *event, ConsoleWindow *self) { static gboolean on_input_key_press(GtkEntry *input, GdkEventKey *event, ConsoleWindow *self) {
switch (event->keyval) { switch (event->keyval) {
case GDK_KEY_Up: case GDK_KEY_Up:
@ -53,17 +91,17 @@ static gboolean on_input_key_press(GtkEntry *input, GdkEventKey *event, ConsoleW
gtk_entry_set_text(self->input, entry); gtk_entry_set_text(self->input, entry);
gtk_editable_set_position(GTK_EDITABLE(input), -1); gtk_editable_set_position(GTK_EDITABLE(input), -1);
} }
return true; return true;
break; break;
case GDK_KEY_Down: case GDK_KEY_Down:
if (event->type == GDK_KEY_PRESS) { if (event->type == GDK_KEY_PRESS) {
if (self->command_history_index <= 0) { if (self->command_history_index <= 0) {
gtk_entry_set_text(self->input, ""); gtk_entry_set_text(self->input, "");
self->command_history_index = -1; self->command_history_index = -1;
} }
if (self->command_history_index == -1) { if (self->command_history_index == -1) {
return true; return true;
} }
@ -82,13 +120,54 @@ static gboolean on_input_key_press(GtkEntry *input, GdkEventKey *event, ConsoleW
gtk_entry_set_text(self->input, entry); gtk_entry_set_text(self->input, entry);
gtk_editable_set_position(GTK_EDITABLE(input), -1); gtk_editable_set_position(GTK_EDITABLE(input), -1);
} }
return true; return true;
break; break;
case GDK_KEY_Tab: case GDK_KEY_Tab:
if (event->type == GDK_KEY_PRESS) { if (event->type == GDK_KEY_PRESS) {
gtk_editable_set_position(GTK_EDITABLE(input), -1); if (self->auto_complete_context == 0) {
gint start_pos;
gint end_pos;
gtk_editable_get_selection_bounds(GTK_EDITABLE(input), &start_pos, &end_pos);
if (start_pos != end_pos) {
self->ignore_auto_complete_context_reset = true;
gtk_editable_delete_text(GTK_EDITABLE(input), start_pos, end_pos);
self->ignore_auto_complete_context_reset = false;
}
self->auto_complete_range = (struct Selection){
.start = start_pos,
.end = start_pos
};
}
gchar *substring = gtk_editable_get_chars(GTK_EDITABLE(input), 0, self->auto_complete_range.start);
uintptr_t context = self->auto_complete_context;
char *completion = GB_debugger_complete_substring(self->gb, substring, &context);
g_free(substring);
if (completion) {
self->ignore_auto_complete_context_reset = true;
gtk_editable_select_region(GTK_EDITABLE(input), self->auto_complete_range.start, self->auto_complete_range.end);
gint new_end = self->auto_complete_range.start;
gtk_editable_delete_text(GTK_EDITABLE(input), self->auto_complete_range.start, self->auto_complete_range.end);
gtk_editable_insert_text(GTK_EDITABLE(input), completion, -1, &new_end);
self->auto_complete_range.end = new_end;
gtk_editable_set_position(GTK_EDITABLE(input), self->auto_complete_range.end);
self->ignore_auto_complete_context_reset = false;
}
else {
g_debug("BEEP (no completion found)");
gdk_display_beep(gdk_display_get_default());
}
self->auto_complete_context = context;
} }
return true; return true;
@ -98,6 +177,26 @@ static gboolean on_input_key_press(GtkEntry *input, GdkEventKey *event, ConsoleW
return false; return false;
} }
static void reset_auto_completion_context(GtkEntry *input, ConsoleWindow *self) {
if (self->ignore_auto_complete_context_reset) {
return;
}
self->auto_complete_context = 0;
}
static void on_delete_text(GtkEditable *editable, int start_pos, int end_pos, ConsoleWindow *self) {
reset_auto_completion_context(GTK_ENTRY(editable), self);
}
static void on_insert_text(GtkEditable *editable, char*new_text, int new_text_length, gpointer position, ConsoleWindow *self) {
reset_auto_completion_context(GTK_ENTRY(editable), self);
}
static void on_move_cursor(GtkEntry *input, GtkMovementStep step, int count, gboolean extend_selection, ConsoleWindow *self) {
reset_auto_completion_context(input, self);
}
static void console_window_init(ConsoleWindow *self) { static void console_window_init(ConsoleWindow *self) {
gtk_widget_init_template(GTK_WIDGET(self)); gtk_widget_init_template(GTK_WIDGET(self));
@ -107,7 +206,7 @@ static void console_window_init(ConsoleWindow *self) {
gtk_text_buffer_create_tag(text_buf, "bold", "weight", PANGO_WEIGHT_BOLD, NULL); gtk_text_buffer_create_tag(text_buf, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
gtk_text_buffer_create_tag(text_buf, "underline", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", true, NULL); gtk_text_buffer_create_tag(text_buf, "underline", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", true, NULL);
gtk_text_buffer_create_tag(text_buf, "dashed_underline", "underline", PANGO_UNDERLINE_DOUBLE, "underline-set", true, NULL); gtk_text_buffer_create_tag(text_buf, "dashed_underline", "underline", PANGO_UNDERLINE_DOUBLE, "underline-set", true, NULL);
gtk_text_view_set_buffer( gtk_text_view_set_buffer(
self->sidebar_output, self->sidebar_output,
gtk_text_buffer_new(tag_table) gtk_text_buffer_new(tag_table)
@ -127,7 +226,7 @@ static void console_window_init(ConsoleWindow *self) {
gtk_entry_completion_set_model(self->command_completion, GTK_TREE_MODEL(command_list_store)); 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_text_column(self->command_completion, 0);
gtk_entry_completion_set_popup_completion(self->command_completion, false); gtk_entry_completion_set_popup_completion(self->command_completion, false);
gtk_entry_completion_set_inline_completion(self->command_completion, true); gtk_entry_completion_set_inline_completion(self->command_completion, false);
gtk_entry_set_completion(self->input, self->command_completion); 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_entry_set_input_hints(self->input, GTK_INPUT_HINT_NO_SPELLCHECK | GTK_INPUT_HINT_NO_EMOJI);
@ -135,6 +234,9 @@ static void console_window_init(ConsoleWindow *self) {
g_signal_connect(self->input, "key-press-event", G_CALLBACK(on_input_key_press), self); 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); g_signal_connect(self->input, "key-release-event", G_CALLBACK(on_input_key_press), self);
g_signal_connect(self->input, "delete-text", G_CALLBACK(on_delete_text), self);
g_signal_connect(self->input, "insert-text", G_CALLBACK(on_insert_text), self);
g_signal_connect(self->input, "move-cursor", G_CALLBACK(on_move_cursor), self);
} }
static void console_window_realize(GtkWidget *widget) { static void console_window_realize(GtkWidget *widget) {
@ -175,7 +277,7 @@ static void on_input_enter(GtkEntry *input, ConsoleWindow *self) {
if (text) { if (text) {
const char *last = NULL; const char *last = NULL;
GtkTreeIter iter; GtkTreeIter iter;
if (gtk_tree_model_get_iter_first(model, &iter)) { if (gtk_tree_model_get_iter_first(model, &iter)) {
gtk_tree_model_get(model, &iter, 0, &last, -1); gtk_tree_model_get(model, &iter, 0, &last, -1);
@ -258,7 +360,7 @@ static gboolean console_window_draw(GtkWidget *widget, cairo_t *cr) {
GtkTextIter iter; GtkTextIter iter;
GtkTextIter start; GtkTextIter start;
AttributedMessage *attr_msg = NULL; AttributedMessage *attr_msg = NULL;
GtkTextIter main_scroll_iter; GtkTextIter main_scroll_iter;
bool scroll_main = false; bool scroll_main = false;
@ -320,7 +422,7 @@ static gboolean console_window_draw(GtkWidget *widget, cairo_t *cr) {
static void console_window_class_init(ConsoleWindowClass *class) { static void console_window_class_init(ConsoleWindowClass *class) {
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/console_window.ui"); gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/console_window.ui");
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), ConsoleWindow, input); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), ConsoleWindow, input);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), ConsoleWindow, output); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), ConsoleWindow, output);
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_input);
@ -330,10 +432,20 @@ static void console_window_class_init(ConsoleWindowClass *class) {
GTK_WIDGET_CLASS(class)->realize = console_window_realize; GTK_WIDGET_CLASS(class)->realize = console_window_realize;
GTK_WIDGET_CLASS(class)->draw = console_window_draw; GTK_WIDGET_CLASS(class)->draw = console_window_draw;
obj_properties[PROP_GB_PTR] = g_param_spec_pointer(
"gb", "SameBoy core pointer", "SameBoy Core pointer (GB_gameboy_t)",
G_PARAM_CONSTRUCT | G_PARAM_READWRITE
);
G_OBJECT_CLASS(class)->set_property = console_window_set_property;
G_OBJECT_CLASS(class)->get_property = console_window_get_property;
g_object_class_install_properties(G_OBJECT_CLASS(class), N_PROPERTIES, obj_properties);
} }
ConsoleWindow *console_window_new(void) { ConsoleWindow *console_window_new(GB_gameboy_t *gb) {
return g_object_new(CONSOLE_WINDOW_TYPE, NULL); return g_object_new(CONSOLE_WINDOW_TYPE, "gb", gb, NULL);
} }
// This function gets called every VBlank while the emulation is running. // This function gets called every VBlank while the emulation is running.
@ -341,7 +453,7 @@ char *console_get_async_input(ConsoleWindow *self, GB_gameboy_t *gb) {
self->clear_sidebar = true; self->clear_sidebar = true;
char *command = (char *)g_async_queue_try_pop(self->input_queue); char *command = (char *)g_async_queue_try_pop(self->input_queue);
if (command) { if (command) {
gchar *msg = g_strdup_printf("> %s\n", command); gchar *msg = g_strdup_printf("> %s\n", command);
log_simple(self, msg); log_simple(self, msg);
@ -356,7 +468,7 @@ char *console_get_sync_input(ConsoleWindow *self, GB_gameboy_t *gb) {
update_sidebar(self, gb); update_sidebar(self, gb);
char *command = (char *)g_async_queue_pop(self->input_queue); char *command = (char *)g_async_queue_pop(self->input_queue);
if (command) { if (command) {
gchar *msg = g_strdup_printf("> %s\n", command); gchar *msg = g_strdup_printf("> %s\n", command);
log_simple(self, msg); log_simple(self, msg);

View File

@ -7,7 +7,7 @@
#define CONSOLE_WINDOW_TYPE (console_window_get_type()) #define CONSOLE_WINDOW_TYPE (console_window_get_type())
G_DECLARE_FINAL_TYPE(ConsoleWindow, console_window, SAMEBOY, CONSOLE_WINDOW, GtkWindow) G_DECLARE_FINAL_TYPE(ConsoleWindow, console_window, SAMEBOY, CONSOLE_WINDOW, GtkWindow)
ConsoleWindow *console_window_new(void); ConsoleWindow *console_window_new(GB_gameboy_t *gb);
char *console_get_async_input(ConsoleWindow *self, GB_gameboy_t *gb); char *console_get_async_input(ConsoleWindow *self, GB_gameboy_t *gb);
char *console_get_sync_input(ConsoleWindow *self, GB_gameboy_t *gb); char *console_get_sync_input(ConsoleWindow *self, GB_gameboy_t *gb);
void console_log(ConsoleWindow *self, const char *message, GB_log_attributes attributes); void console_log(ConsoleWindow *self, const char *message, GB_log_attributes attributes);

View File

@ -992,7 +992,7 @@ static void startup(GApplication *app, gpointer null_ptr) {
init_config(app, gui_data.cli_options.config_path, &gui_data.config_modification_date); init_config(app, gui_data.cli_options.config_path, &gui_data.config_modification_date);
gui_data.screen = gb_screen_new(gui_data.cli_options.force_software_renderer); gui_data.screen = gb_screen_new(gui_data.cli_options.force_software_renderer);
gui_data.console = console_window_new(); gui_data.console = console_window_new(&gb);
gui_data.preferences = preferences_window_new(&gb); gui_data.preferences = preferences_window_new(&gb);
gui_data.vram_viewer = vram_viewer_window_new(); gui_data.vram_viewer = vram_viewer_window_new();
gui_data.memory_viewer = GTK_WINDOW(get_object("memory_viewer")); gui_data.memory_viewer = GTK_WINDOW(get_object("memory_viewer"));