[GTK3] Start menu migration from GtkUiManager to GtkBuilder

This commit is contained in:
Maximilian Mader 2020-05-14 04:22:58 +02:00
parent 6ea36bbc39
commit add54953c6
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
3 changed files with 529 additions and 125 deletions

View File

@ -220,7 +220,6 @@ static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer
static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer app);
static void on_pause_changed(GSimpleAction *action, GVariant *value, gpointer user_data_ptr);
static void on_mute_changed(GSimpleAction *action, GVariant *value, gpointer user_data_ptr);
static void on_model_changed(GSimpleAction *action, GVariant *value, gpointer user_data_ptr);
static const GActionEntry file_entries[] = {
{ "open", activate_open, NULL, NULL, NULL },
@ -241,7 +240,7 @@ static const GActionEntry emulation_entries[] = {
static const GActionEntry developer_entries[] = {
{ "show_console", activate_show_console, NULL, NULL, NULL },
{ "open_gtk_debugger", activate_open_gtk_debugger, NULL, NULL, NULL },
{ "open_memory_viewer", activate_open_memory_viewer, NULL, NULL, NULL },
// { "open_memory_viewer", activate_open_memory_viewer, NULL, NULL, NULL },
{ "open_vram_viewer", activate_open_vram_viewer, NULL, NULL, NULL },
{ "toggle_developer_mode", NULL, NULL, "false", NULL },
{ "clear_console", activate_clear_console, NULL, NULL, NULL },
@ -252,9 +251,9 @@ static const GActionEntry app_entries[] = {
{ "about", activate_about, NULL, NULL, NULL },
{ "preferences", activate_preferences, NULL, NULL, NULL },
{ "toggle_mute", NULL, NULL, "false", on_mute_changed },
{ "change_model", NULL, "s", "@s 'CGB'", on_model_changed },
};
static void replace_extension(const char *src, size_t length, char *dest, const char *ext) {
memcpy(dest, src, length);
dest[length] = 0;
@ -817,14 +816,17 @@ static char *async_console_input(GB_gameboy_t *gb) {
return input;
}
// Returns a `GApplication`s `GMenuModel` by ID
// GApplication menus are loaded from `gtk/menus.ui`, `gtk/menus-traditional.ui` and `gtk/menus-common.ui`.
static GMenuModel *get_menu_model(GApplication *app, const char *id) {
GMenu *menu;
GtkWidget *menubar_to_menu(GtkMenuBar *menubar) {
GtkWidget *menu = gtk_menu_new();
g_autoptr(GList) iter = gtk_container_get_children(GTK_CONTAINER (menubar));
menu = gtk_application_get_menu_by_id(GTK_APPLICATION(app), id);
while (iter) {
GtkWidget *item = GTK_WIDGET(iter->data);
gtk_widget_reparent(item, menu);
iter = g_list_next(iter);
}
return menu ? G_MENU_MODEL(g_object_ref_sink(menu)) : NULL;
return menu;
}
// Create our applications menu.
@ -834,7 +836,7 @@ static GMenuModel *get_menu_model(GApplication *app, const char *id) {
// the desktop environment shell handle the menu if it signals support for it
// or uses a standard menubar inside the window.
static void setup_menu(GApplication *app) {
GMenuModel *menubar_model = get_menu_model(app, "menubar");
GtkMenuBar *menubar = builder_get(GTK_MENU_BAR, "main_menu");
enum menubar_type_t menubar_type = get_show_menubar();
// Try to use a sane default
@ -878,13 +880,8 @@ static void setup_menu(GApplication *app) {
break;
case MENUBAR_SHOW_IN_SHELL:
g_debug("Showing menu in the shell");
gtk_application_set_menubar(GTK_APPLICATION(app), menubar_model);
break;
case MENUBAR_SHOW_IN_WINDOW: {
g_debug("Showing menu in the window");
GtkMenuBar *menubar = GTK_MENU_BAR(gtk_menu_bar_new_from_model(menubar_model));
gtk_box_pack_start(GTK_BOX(gui_data.main_window_container), GTK_WIDGET(menubar), false, false, 0);
break;
}
@ -901,7 +898,7 @@ static void setup_menu(GApplication *app) {
// Hook menubar up to the hamburger button
GtkMenuButton *hamburger_button = GTK_MENU_BUTTON(get_object("hamburger_button"));
gtk_menu_button_set_menu_model(hamburger_button, menubar_model);
gtk_menu_button_set_popup(hamburger_button, GTK_WIDGET(menubar_to_menu(menubar)));
break;
}
}
@ -1490,8 +1487,19 @@ static void reset(void) {
bool success = false;
if (gui_data.file) {
char *path = g_file_get_path(gui_data.file);
char *ext = strrchr(path, '.');
int result;
if (GB_load_rom(&gb, path) == 0) {
GB_debugger_clear_symbols(&gb);
if (g_strcmp0(ext + 1, "isx") == 0) {
result = GB_load_isx(&gb, path);
}
else {
result = GB_load_rom(&gb, path);
}
if (result == 0) {
success = true;
}
else {
@ -1643,14 +1651,6 @@ static void quit_interrupt(int ignored) {
quit();
}
// `destroy` signal GCallback
// Exits the application
static void on_quit(GtkWidget *w, gpointer app) {
g_debug("on_quit(%p, %p);", w, &app);
quit();
}
static void create_action_groups(GApplication *app) {
g_action_map_add_action_entries(G_ACTION_MAP(app), emulation_entries, G_N_ELEMENTS(emulation_entries), NULL);
g_action_map_add_action_entries(G_ACTION_MAP(app), developer_entries, G_N_ELEMENTS(developer_entries), NULL);
@ -1990,8 +1990,10 @@ static void startup(GApplication *app, gpointer null_ptr) {
create_action_groups(app);
if (gui_data.cli_options.prefix != NULL) {
GAction *action = g_action_map_lookup_action(G_ACTION_MAP(gui_data.main_application), "change_model");
g_action_change_state(action, g_variant_new_string(gui_data.cli_options.prefix));
// TODO
//GAction *action = g_action_map_lookup_action(G_ACTION_MAP(gui_data.main_application), "change_model");
//g_action_change_state(action, g_variant_new_string(gui_data.cli_options.prefix));
}
#if NDEBUG
@ -2056,7 +2058,7 @@ static void startup(GApplication *app, gpointer null_ptr) {
}
// Let GTK choose the proper icon
gtk_window_set_icon_list(GTK_WINDOW(gui_data.main_window), icon_list);
gtk_window_set_default_icon_list(icon_list);
// Add missing information to the about dialog
GtkAboutDialog *about_dialog = GTK_ABOUT_DIALOG(get_object("about_dialog"));
@ -2065,12 +2067,53 @@ static void startup(GApplication *app, gpointer null_ptr) {
g_list_free_full(icon_list, g_object_unref);
}
G_MODULE_EXPORT void on_quit_activate(GtkWidget *w, gpointer user_data_ptr) {
quit();
}
G_MODULE_EXPORT void on_change_model(GtkCheckMenuItem *check_menu_item, const gchar *model_str) {
if (!GB_is_inited(&gb) || !gtk_check_menu_item_get_active(check_menu_item)) {
return;
}
GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new(
GTK_WINDOW(gui_data.main_window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Changing the emulated model requires a reset.\nChange model and reset game?"
));
stop();
gint result = gtk_dialog_run(GTK_DIALOG(dialog));
switch (result) {
case GTK_RESPONSE_YES:
// Reset the CLI model override
gui_data.cli_options.model = -1;
// g_simple_action_set_state(action, value);
g_free(config.emulation.model);
config.emulation.model = g_strdup(model_str);
reset();
break;
default:
// Action has been canceled
break;
}
run();
gtk_widget_destroy(GTK_WIDGET(dialog));
}
static void connect_signal_handlers(GApplication *app) {
// Connect signal handlers
gtk_widget_add_events(GTK_WIDGET(gui_data.main_window), GDK_KEY_PRESS_MASK);
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), app);
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, "window-state-event", G_CALLBACK(on_window_state_change), NULL);
@ -2095,6 +2138,12 @@ static void connect_signal_handlers(GApplication *app) {
g_signal_connect(get_object("vram_viewer_tilemap_canvas"), "motion_notify_event", G_CALLBACK(on_motion_vram_viewer_tilemap), NULL);
g_signal_connect(get_object("vram_viewer_stack"), "notify::visible-child", G_CALLBACK(on_vram_tab_change), NULL);
// We cant set these values in the UI definition file
g_signal_connect(get_object("change_model_dmg"), "toggled", G_CALLBACK(on_change_model), (gpointer) "DMG");
g_signal_connect(get_object("change_model_sgb"), "toggled", G_CALLBACK(on_change_model), (gpointer) "SGB");
g_signal_connect(get_object("change_model_cgb"), "toggled", G_CALLBACK(on_change_model), (gpointer) "CGB");
g_signal_connect(get_object("change_model_agb"), "toggled", G_CALLBACK(on_change_model), (gpointer) "AGB");
}
static void update_viewport(void) {
@ -2388,6 +2437,47 @@ static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter
gtk_widget_show_all(GTK_WIDGET(gui_data.vram_viewer));
}
// Closes a ROM
static void close_rom(void) {
stop();
GB_free(&gb);
// Clear the screen as side effect
update_window_geometry();
if (gui_data.fallback_canvas) {
gtk_widget_queue_draw(GTK_WIDGET(gui_data.main_window));
}
else if (gui_data.gl_area) {
gtk_gl_area_queue_render(gui_data.gl_area);
}
// Clear the VRAM viewer
g_mutex_lock(&gui_data.tileset_buffer_mutex);
memset(gui_data.tileset_buffer, 0, sizeof gui_data.tileset_buffer);
g_mutex_unlock(&gui_data.tileset_buffer_mutex);
g_mutex_lock(&gui_data.tilemap_buffer_mutex);
memset(gui_data.tilemap_buffer, 0, sizeof gui_data.tilemap_buffer);
g_mutex_unlock(&gui_data.tilemap_buffer_mutex);
gtk_stack_set_visible_child_name(builder_get(GTK_STACK, "vram_viewer_stack"), "vram_viewer_tileset");
gtk_tree_view_set_model(builder_get(GTK_TREE_VIEW, "vram_viewer_sprites"), NULL);
gtk_tree_view_set_model(builder_get(GTK_TREE_VIEW, "vram_viewer_palettes"), NULL);
// Redraw the VRAM viewer
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
// Update menu action states
action_set_enabled(gui_data.main_application, "close", false);
action_entries_set_enabled(emulation_entries, G_N_ELEMENTS(emulation_entries), false);
// Try force the queued redraws
while (g_main_context_pending(NULL)) {
g_main_context_iteration(NULL, FALSE);
}
}
// app.open GAction
// Opens a ROM file
static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer app) {
@ -2410,34 +2500,8 @@ static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer a
}
// app.close GAction
// Closes a ROM
static void activate_close(GSimpleAction *action, GVariant *parameter, gpointer app) {
stop();
GB_free(&gb);
// Clear the screen as side effect
update_window_geometry();
gtk_widget_queue_draw(gui_data.fallback_canvas ? GTK_WIDGET(gui_data.fallback_canvas) : GTK_WIDGET(gui_data.gl_area));
// Clear the VRAM viewer
g_mutex_lock(&gui_data.tileset_buffer_mutex);
memset(gui_data.tileset_buffer, 0, sizeof gui_data.tileset_buffer);
g_mutex_unlock(&gui_data.tileset_buffer_mutex);
g_mutex_lock(&gui_data.tilemap_buffer_mutex);
memset(gui_data.tilemap_buffer, 0, sizeof gui_data.tilemap_buffer);
g_mutex_unlock(&gui_data.tilemap_buffer_mutex);
gtk_stack_set_visible_child_name(builder_get(GTK_STACK, "vram_viewer_stack"), "vram_viewer_tileset");
gtk_tree_view_set_model(builder_get(GTK_TREE_VIEW, "vram_viewer_sprites"), NULL);
gtk_tree_view_set_model(builder_get(GTK_TREE_VIEW, "vram_viewer_palettes"), NULL);
// Redraw the VRAM viewer
gtk_widget_queue_draw(GTK_WIDGET(gui_data.vram_viewer));
// Update menu action states
action_set_enabled(gui_data.main_application, "close", false);
action_entries_set_enabled(emulation_entries, G_N_ELEMENTS(emulation_entries), false);
close_rom();
}
// app.preferences GAction
@ -2464,46 +2528,6 @@ static void activate_clear_console(GSimpleAction *action, GVariant *parameter, g
g_rec_mutex_unlock(&gui_data.console_output_lock);
}
static void on_model_changed(GSimpleAction *action, GVariant *value, gpointer user_data_ptr) {
if (!GB_is_inited(&gb)) {
g_simple_action_set_state(action, value);
return;
}
const gchar *model_str = g_variant_get_string(value, NULL);
GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new(
GTK_WINDOW(gui_data.main_window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Changing the emulated model requires a reset.\nChange model and reset game?"
));
stop();
gint result = gtk_dialog_run(GTK_DIALOG(dialog));
switch (result) {
case GTK_RESPONSE_YES:
// Reset the CLI model override
gui_data.cli_options.model = -1;
g_simple_action_set_state(action, value);
g_free(config.emulation.model);
config.emulation.model = g_strdup(g_variant_get_string(value, NULL));
reset();
break;
default:
// Action has been canceled
break;
}
run();
gtk_widget_destroy(GTK_WIDGET(dialog));
}
static void on_mute_changed(GSimpleAction *action, GVariant *value, gpointer user_data_ptr) {
config.audio.muted = g_variant_get_boolean(value);
@ -2522,6 +2546,29 @@ static void on_pause_changed(GSimpleAction *action, GVariant *value, gpointer us
g_simple_action_set_state(action, value);
}
G_MODULE_EXPORT void on_open_recent_activate(GtkRecentChooser *chooser, gpointer user_data_ptr) {
stop();
gchar *uri = gtk_recent_chooser_get_current_uri(chooser);
GFile *file = g_file_new_for_uri(uri);
if (g_file_query_exists(file, NULL)) {
gui_data.file = file;
// Add the file back to the top of the list
GtkRecentManager *manager = gtk_recent_manager_get_default();
gtk_recent_manager_add_item(manager, uri);
// TODO: Not nice
activate_reset(NULL, NULL, NULL);
}
else {
// TODO
g_warning("File not found: %s", uri);
close_rom();
}
}
G_MODULE_EXPORT void on_boot_rom_location_changed(GtkWidget *w, gpointer user_data_ptr) {
GtkComboBox *box = GTK_COMBO_BOX(w);
const gchar *id = gtk_combo_box_get_active_id(box);

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2
<!-- Generated with glade 3.36.0
The MIT License (MIT)
@ -27,7 +27,7 @@ Author: Maximilian Mader
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type mit -->
<!-- interface-name SameBoy -->
<!-- interface-description SameBoy is an open source Game Boy (DMG) and Game Boy Color (CGB) emulator, written in portable C. -->
@ -46,9 +46,6 @@ GTK3 frontend by
Maximilian Mader https://github.com/max-m</property>
<property name="logo_icon_name"/>
<property name="license_type">mit-x11</property>
<child type="titlebar">
<placeholder/>
</child>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
@ -70,6 +67,9 @@ Maximilian Mader https://github.com/max-m</property>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
<object class="GtkListStore" id="cgb_models">
<columns>
@ -118,9 +118,6 @@ Maximilian Mader https://github.com/max-m</property>
<property name="title" translatable="yes">Debug Console</property>
<property name="default_width">920</property>
<property name="default_height">400</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
@ -262,8 +259,6 @@ Maximilian Mader https://github.com/max-m</property>
<object class="GtkEntry" id="console_input">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_left">3</property>
<property name="margin_right">3</property>
<property name="margin_top">3</property>
<property name="margin_bottom">3</property>
<property name="placeholder_text" translatable="yes">Console input</property>
@ -277,6 +272,9 @@ Maximilian Mader https://github.com/max-m</property>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
<style>
<class name="debug-console"/>
</style>
@ -317,9 +315,6 @@ Maximilian Mader https://github.com/max-m</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">Preferences</property>
<property name="resizable">False</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkNotebook">
<property name="visible">True</property>
@ -1207,6 +1202,9 @@ Maximilian Mader https://github.com/max-m</property>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
<object class="GtkHeaderBar" id="main_header_bar">
<property name="visible">True</property>
@ -1231,9 +1229,6 @@ Maximilian Mader https://github.com/max-m</property>
<object class="GtkWindow" id="memory_viewer">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Memory Viewer</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
@ -1278,12 +1273,35 @@ Maximilian Mader https://github.com/max-m</property>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
<object class="GtkWindow" id="printer">
<property name="can_focus">False</property>
<property name="resizable">False</property>
<property name="default_width">320</property>
<property name="default_height">432</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkDrawingArea" id="printer_canvas">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar">
<property name="visible">True</property>
@ -1318,20 +1336,359 @@ Maximilian Mader https://github.com/max-m</property>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
</object>
<object class="GtkRecentFilter" id="recent_files_filter">
<mime-types>
<mime-type>application/x-gameboy-rom</mime-type>
<mime-type>application/x-gameboy-color-rom</mime-type>
<mime-type>application/octet-stream</mime-type>
</mime-types>
<patterns>
<pattern>*.gb</pattern>
<pattern>*.gbc</pattern>
<pattern>*.isx</pattern>
</patterns>
<applications>
<application>sameboy</application>
</applications>
</object>
<object class="GtkMenuBar" id="main_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkDrawingArea" id="printer_canvas">
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open</property>
<property name="label" translatable="yes">_Open</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Recent files</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkRecentChooserMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="filter">recent_files_filter</property>
<property name="limit">10</property>
<property name="sort_type">mru</property>
<signal name="item-activated" handler="on_open_recent_activate" swapped="no"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.close</property>
<property name="label" translatable="yes">_Close</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.quit</property>
<property name="label" translatable="yes">_Quit</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.preferences</property>
<property name="label" translatable="yes">_Preferences</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">E_mulation</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.reset</property>
<property name="label" translatable="yes">_Reset</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.pause</property>
<property name="label" translatable="yes">Pause</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.save_state</property>
<property name="label" translatable="yes">Save State</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.load_state</property>
<property name="label" translatable="yes">Load State</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkRadioMenuItem" id="change_model_dmg">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Game Boy</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkRadioMenuItem" id="change_model_sgb">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Super Game Boy</property>
<property name="use_underline">True</property>
<property name="group">change_model_dmg</property>
</object>
</child>
<child>
<object class="GtkRadioMenuItem" id="change_model_cgb">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Game Boy Color</property>
<property name="use_underline">True</property>
<property name="group">change_model_dmg</property>
</object>
</child>
<child>
<object class="GtkRadioMenuItem" id="change_model_agb">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Game Boy Advance</property>
<property name="use_underline">True</property>
<property name="group">change_model_dmg</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.toggle_mute</property>
<property name="label" translatable="yes">Mute Sound</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Link</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkRadioMenuItem" id="change_serial_device_none">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">None</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkRadioMenuItem" id="change_serial_device_printer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Game Boy Printer</property>
<property name="use_underline">True</property>
<property name="group">change_serial_device_none</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Develop</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.show_console</property>
<property name="label" translatable="yes">Show Console</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.clear_console</property>
<property name="label" translatable="yes">Clear Console</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.break_debugger</property>
<property name="label" translatable="yes">Break Debugger</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_memory_viewer</property>
<property name="label" translatable="yes">Show Memory Viewer</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_vram_viewer</property>
<property name="label" translatable="yes">Show VRAM Viewer</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.open_gtk_debugger</property>
<property name="label" translatable="yes">Show GTK Debugger</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">app.about</property>
<property name="label">_About</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
@ -1343,15 +1700,10 @@ Maximilian Mader https://github.com/max-m</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">VRAM Viewer</property>
<property name="resizable">False</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">3</property>
<property name="margin_right">3</property>
<property name="margin_top">3</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
@ -1815,5 +2167,8 @@ Maximilian Mader https://github.com/max-m</property>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
</interface>

View File

@ -2,9 +2,11 @@
<gresources>
<gresource prefix="/io/github/sameboy">
<file preprocess="xml-stripblanks" compressed="true">ui/window.ui</file>
<!--
<file preprocess="xml-stripblanks">gtk/menus.ui</file>
<file preprocess="xml-stripblanks">gtk/menus-common.ui</file>
<file preprocess="xml-stripblanks">gtk/menus-traditional.ui</file>
-->
<file compressed="true">css/main.css</file>
<file compressed="true" alias="gamecontrollerdb.txt">gamecontrollerdb_456ad4d.txt</file>