diff --git a/gtk3/gb_screen.c b/gtk3/gb_screen.c index 3e2ee9e..3ff0cb9 100644 --- a/gtk3/gb_screen.c +++ b/gtk3/gb_screen.c @@ -35,7 +35,7 @@ static void gb_screen_finalize(GObject *object) { if (self->image_buffers[0]) g_free(self->image_buffers[0]); if (self->image_buffers[1]) g_free(self->image_buffers[1]); if (self->image_buffers[2]) g_free(self->image_buffers[2]); - + free_shader(&self->shader); free_master_shader(); @@ -282,7 +282,7 @@ static void gb_screen_class_init(GbScreenClass *class) { GTK_WIDGET_CLASS(class)->draw = gb_screen_draw; GTK_WIDGET_CLASS(class)->get_preferred_width = gb_screen_get_preferred_width; GTK_WIDGET_CLASS(class)->get_preferred_height = gb_screen_get_preferred_height; - + g_object_class_install_properties(G_OBJECT_CLASS(class), N_PROPERTIES, obj_properties); } diff --git a/gtk3/main_menu.c b/gtk3/main_menu.c new file mode 100644 index 0000000..dfc0e4d --- /dev/null +++ b/gtk3/main_menu.c @@ -0,0 +1,87 @@ +#include "main_menu.h" +#include +#include "util.h" +#include "gb_screen.h" +#include "check_menu_radio_group.h" + +struct _MainMenu { + GtkMenuBar parent_instance; + GtkMenuBarClass parent_class; + + GtkSeparatorMenuItem *before_model_changer; + GtkMenu *link_menu; +}; + +G_DEFINE_TYPE(MainMenu, main_menu, GTK_TYPE_MENU_BAR); + +static void main_menu_init(MainMenu *self) { + gtk_widget_init_template(GTK_WIDGET(self)); +} + +static void main_menu_class_init(MainMenuClass *class) { + gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/main_menu.ui"); + + gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainMenu, before_model_changer); + gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainMenu, link_menu); +} + +MainMenu *main_menu_new() { + return g_object_new(MAIN_MENU_TYPE, NULL); +} + +// Create our application’s menu. +// +// This function tries to stick to the desktop environment’s conventions. +// For the GNOME Shell it uses a hamburger menu, otherwise it either lets +// the desktop environment shell handle the menu if it signals support for it +// or uses a standard menubar inside the menu. +void main_menu_setup(MainMenu *self, char *model_string) { + // Creating these items in the UI defintion files was buggy in some desktop + // environments and the manual call of `g_signal_connect` was needed anyway + // because the UI definition can’t define string arguments for signal handlers. + bool on_change_model(GtkWidget *, gpointer); + bool on_change_linked_device(GtkWidget *, gpointer); + + static const char *const model_names[] = { + "Game Boy", + "Super Game Boy", + "Game Boy Color", + "Game Boy Advance", + NULL + }; + + static const char *const model_codes[] = { + "DMG", + "SGB", + "CGB", + "GBA", + NULL + }; + + // Find the menu item index of the previous sibling of the new menu items + GtkContainer *parent = GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(self->before_model_changer))); + g_autoptr(GList) list = gtk_container_get_children(parent); + gint position = g_list_index(list, self->before_model_changer); + + CheckMenuItemGroup *model_group = check_menu_item_group_new((char **) model_names, (char **) model_codes); + check_menu_item_group_insert_into_menu_shell(model_group, GTK_MENU_SHELL(parent), position + 1); + check_menu_item_group_connect_toggle_signal(model_group, on_change_model); + check_menu_item_group_activate(model_group, model_string); + + static const char *const peripheral_names[] = { + "None", + "Game Boy Printer", + NULL + }; + + static const char *const peripheral_codes[] = { + "NONE", + "PRINTER", + NULL, + }; + + CheckMenuItemGroup *link_group = check_menu_item_group_new((char **) peripheral_names, (char **) peripheral_codes); + check_menu_item_group_insert_into_menu_shell(link_group, GTK_MENU_SHELL(self->link_menu), 0); + check_menu_item_group_connect_toggle_signal(link_group, on_change_linked_device); + check_menu_item_group_activate(link_group, "NONE"); +} diff --git a/gtk3/main_menu.h b/gtk3/main_menu.h new file mode 100644 index 0000000..7e4eac3 --- /dev/null +++ b/gtk3/main_menu.h @@ -0,0 +1,14 @@ +#ifndef main_menu_h +#define main_menu_h + +#include +#include +#include "shader.h" + +#define MAIN_MENU_TYPE (main_menu_get_type()) +G_DECLARE_FINAL_TYPE(MainMenu, main_menu, SAMEBOY, MAIN_MENU, GtkMenuBar) + +MainMenu *main_menu_new(); +void main_menu_setup(MainMenu *self, char *model_string); + +#endif diff --git a/gtk3/main_window.c b/gtk3/main_window.c index c8f7efe..ba53305 100644 --- a/gtk3/main_window.c +++ b/gtk3/main_window.c @@ -2,18 +2,16 @@ #include #include "util.h" #include "gb_screen.h" +#include "main_menu.h" #include "check_menu_radio_group.h" struct _MainWindow { - GtkApplicationWindow parent_instance; GtkApplicationWindowClass parent_class; GtkBox *container; GbScreen *screen; bool force_software_renderer; - GtkMenuBar *main_menu; - GtkSeparatorMenuItem *before_model_changer; - GtkMenu *link_menu; + MainMenu *main_menu; }; G_DEFINE_TYPE(MainWindow, main_window, GTK_TYPE_APPLICATION_WINDOW); @@ -47,9 +45,7 @@ static void main_window_get_property(GObject *object, guint property_id, GValue static void main_window_constructed(GObject *object) { MainWindow *self = (MainWindow *) object; - self->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0)); self->screen = gb_screen_new(self->force_software_renderer); - gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(self->container)); gtk_box_pack_end(GTK_BOX(self->container), GTK_WIDGET(self->screen), true, true, 0); } @@ -80,20 +76,11 @@ static void main_window_init(MainWindow *self) { gtk_widget_add_accelerator(GTK_WIDGET(self), "break-debugger-keyboard", accelGroup, GDK_KEY_C, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); } -static void main_window_finalize(GObject *object) { - MainWindow *self = (MainWindow *) object; - - G_OBJECT_CLASS(main_window_parent_class)->finalize(object); -} - static void main_window_class_init(MainWindowClass *class) { gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/main_window.ui"); + gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, container); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, main_menu); - gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, before_model_changer); - gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), MainWindow, link_menu); - - G_OBJECT_CLASS(class)->finalize = main_window_finalize; obj_properties[PROP_FORCE_SOFTWARE_RENDERER] = g_param_spec_boolean( "force_software_renderer", "Software Renderer", "Forces the use of software rendering via Cairo", @@ -121,67 +108,8 @@ void main_window_fullscreen(MainWindow *self, bool make_fullscreen) { } } -// Creating these items in the UI defintion files was buggy in some desktop -// environments and the manual call of `g_signal_connect` was needed anyway -// because the UI definition can’t define string arguments for signal handlers. -static void create_model_menu_items(MainWindow *self, char *model_string) { - bool on_change_model(GtkWidget *, gpointer); - bool on_change_linked_device(GtkWidget *, gpointer); - - static const char *const model_names[] = { - "Game Boy", - "Super Game Boy", - "Game Boy Color", - "Game Boy Advance", - NULL - }; - - static const char *const model_codes[] = { - "DMG", - "SGB", - "CGB", - "GBA", - NULL - }; - - // Find the menu item index of the previous sibling of the new menu items - GtkContainer *parent = GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(self->before_model_changer))); - g_autoptr(GList) list = gtk_container_get_children(parent); - gint position = g_list_index(list, self->before_model_changer); - - CheckMenuItemGroup *model_group = check_menu_item_group_new((char **) model_names, (char **) model_codes); - check_menu_item_group_insert_into_menu_shell(model_group, GTK_MENU_SHELL(parent), position + 1); - check_menu_item_group_connect_toggle_signal(model_group, on_change_model); - check_menu_item_group_activate(model_group, model_string); - - static const char *const peripheral_names[] = { - "None", - "Game Boy Printer", - NULL - }; - - static const char *const peripheral_codes[] = { - "NONE", - "PRINTER", - NULL, - }; - - CheckMenuItemGroup *link_group = check_menu_item_group_new((char **) peripheral_names, (char **) peripheral_codes); - check_menu_item_group_insert_into_menu_shell(link_group, GTK_MENU_SHELL(self->link_menu), 0); - check_menu_item_group_connect_toggle_signal(link_group, on_change_linked_device); - check_menu_item_group_activate(link_group, "NONE"); -} - -// Create our application’s menu. -// -// This function tries to stick to the desktop environment’s conventions. -// For the GNOME Shell it uses a hamburger menu, otherwise it either lets -// the desktop environment shell handle the menu if it signals support for it -// or uses a standard menubar inside the window. void main_window_setup_menu(MainWindow *self, char *model_string) { - create_model_menu_items(self, model_string); - - gtk_box_pack_start(GTK_BOX(self->container), GTK_WIDGET(self->main_menu), false, false, 0); + main_menu_setup(self->main_menu, model_string); } // GbScreen wrappers diff --git a/gtk3/resources/ui/main_menu.ui b/gtk3/resources/ui/main_menu.ui new file mode 100644 index 0000000..c59b2b3 --- /dev/null +++ b/gtk3/resources/ui/main_menu.ui @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + application/x-gameboy-rom + application/x-gameboy-color-rom + + + *.gb + *.gbc + *.isx + + + sameboy + + + diff --git a/gtk3/resources/ui/main_window.ui b/gtk3/resources/ui/main_window.ui index 4070bfb..4e9ef49 100644 --- a/gtk3/resources/ui/main_window.ui +++ b/gtk3/resources/ui/main_window.ui @@ -34,327 +34,18 @@ Author: Maximilian Mader - - - - application/x-gameboy-rom - application/x-gameboy-color-rom - - - *.gb - *.gbc - *.isx - - - sameboy - - diff --git a/gtk3/sameboy.gresource.xml b/gtk3/sameboy.gresource.xml index 6d9aa14..f3b95b9 100644 --- a/gtk3/sameboy.gresource.xml +++ b/gtk3/sameboy.gresource.xml @@ -3,6 +3,7 @@ ui/window.ui ui/main_window.ui + ui/main_menu.ui ui/console_window.ui ui/preferences_window.ui ui/vram_viewer_window.ui