[GTK3] Use an abstraction over GtkCheckMenuItem instead of GtkRadioMenuItem

This commit is contained in:
Maximilian Mader 2020-05-16 00:23:03 +02:00
parent 1d7034fb88
commit 45e62a2f26
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
3 changed files with 133 additions and 39 deletions

View File

@ -0,0 +1,79 @@
#include "check_menu_radio_group.h"
void check_menu_item_group_handler(GtkCheckMenuItem *item, CheckMenuItemGroupHandlerData *data) {
bool cancel = false;
if (data->handler) {
cancel = data->handler(GTK_WIDGET(item), (gpointer) data->arg);
}
GValue value = G_VALUE_INIT;
g_value_init(&value, G_TYPE_BOOLEAN);
if (cancel) {
g_value_set_boolean(&value, false);
g_object_set_property(G_OBJECT(item), "active", &value);
}
else {
for (unsigned i = 0; i < data->group->count; i++) {
GtkCheckMenuItem *cur = GTK_CHECK_MENU_ITEM(data->group->items[i]);
g_value_set_boolean(&value, cur == item);
g_object_set_property(G_OBJECT(cur), "active", &value);
}
}
}
CheckMenuItemGroup *check_menu_item_group_new(char **names, char **args) {
unsigned name_count = 0;
if (names != NULL) {
for (char **ptr = names; *ptr != NULL; ptr++, name_count++);
}
CheckMenuItemGroup *group = g_malloc0(sizeof(CheckMenuItemGroup));
group->count = name_count;
group->items = g_malloc0(sizeof(GtkWidget*) * name_count);
group->handlers = g_malloc0(sizeof(CheckMenuItemGroupHandlerData*) * name_count);
for (unsigned i = 0; i < name_count; i++) {
group->items[i] = gtk_check_menu_item_new_with_label(names[i]);
group->handlers[i] = g_malloc0(sizeof(CheckMenuItemGroupHandlerData));
group->handlers[i]->group = group;
group->handlers[i]->arg = args[i];
g_signal_connect(group->items[i], "toggled", G_CALLBACK(check_menu_item_group_handler), group->handlers[i]);
gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(group->items[i]), true);
}
return group;
}
void check_menu_item_group_activate(CheckMenuItemGroup *group, char *arg) {
GValue value = G_VALUE_INIT;
g_value_init(&value, G_TYPE_BOOLEAN);
g_value_set_boolean(&value, false);
for (unsigned i = 0; i < group->count; i++) {
GtkCheckMenuItem *cur = GTK_CHECK_MENU_ITEM(group->items[i]);
if (g_strcmp0(arg, group->handlers[i]->arg) == 0) {
gtk_check_menu_item_set_active(cur, true);
}
else {
g_object_set_property(G_OBJECT(cur), "active", &value);
}
}
}
void check_menu_item_group_connect_toggle_signal(CheckMenuItemGroup *group, bool (*handler)(GtkWidget *, gpointer)) {
for (unsigned i = 0; i < group->count; i++) {
group->handlers[i]->handler = handler;
}
}
void check_menu_item_group_insert_into_menu_shell(CheckMenuItemGroup *group, GtkMenuShell *menu_shell, gint position) {
for (unsigned i = 0; i < group->count; i++) {
gtk_menu_shell_insert(menu_shell, group->items[i], position + i);
}
}

View File

@ -0,0 +1,24 @@
#ifndef check_menu_radio_group_h
#define check_menu_radio_group_h
#include <stdbool.h>
#include <gtk/gtk.h>
typedef struct CheckMenuItemGroupHandlerData {
struct CheckMenuItemGroup *group;
char *arg;
bool (*handler)(GtkWidget *, void *);
} CheckMenuItemGroupHandlerData;
typedef struct CheckMenuItemGroup {
unsigned count;
GtkWidget **items;
CheckMenuItemGroupHandlerData **handlers;
} CheckMenuItemGroup;
CheckMenuItemGroup *check_menu_item_group_new(char **names, char **args);
void check_menu_item_group_activate(CheckMenuItemGroup *group, char *arg);
void check_menu_item_group_connect_toggle_signal(CheckMenuItemGroup *group, bool (*handler)(GtkWidget *, gpointer));
void check_menu_item_group_insert_into_menu_shell(CheckMenuItemGroup *group, GtkMenuShell *menu_shell, gint position);
#endif

View File

@ -11,6 +11,7 @@
#include "settings.h" #include "settings.h"
#include "shader.h" #include "shader.h"
#include "check_menu_radio_group.h"
// used for audio and game controllers // used for audio and game controllers
#include "SDL.h" #include "SDL.h"
@ -822,20 +823,22 @@ GtkWidget *menubar_to_menu(GtkMenuBar *menubar) {
// environments and the manual call of `g_signal_connect` was needed anyway // environments and the manual call of `g_signal_connect` was needed anyway
// because the UI definition cant define string arguments for signal handlers. // because the UI definition cant define string arguments for signal handlers.
static void create_model_menu_items() { static void create_model_menu_items() {
void on_change_model(GtkCheckMenuItem *check_menu_item, const gchar *model_str); bool on_change_model(GtkWidget *, gpointer);
static const char *const model_names[] = { static const char *const model_names[] = {
"Game Boy", "Game Boy",
"Super Game Boy", "Super Game Boy",
"Game Boy Color", "Game Boy Color",
"Game Boy Advance" "Game Boy Advance",
NULL
}; };
static const char *const model_codes[] = { static const char *const model_codes[] = {
"DMG", "DMG",
"SGB", "SGB",
"CGB", "CGB",
"GBA" "GBA",
NULL
}; };
// Find the menu item index of the previous sibling of the new menu items // Find the menu item index of the previous sibling of the new menu items
@ -844,48 +847,27 @@ static void create_model_menu_items() {
g_autoptr(GList) list = gtk_container_get_children(parent); g_autoptr(GList) list = gtk_container_get_children(parent);
gint position = g_list_index(list, before); gint position = g_list_index(list, before);
GSList *group = NULL; CheckMenuItemGroup *model_group = check_menu_item_group_new((char **) model_names, (char **) model_codes);
for (int i = 0; i < sizeof(model_names) / sizeof(const char*); i++) { check_menu_item_group_insert_into_menu_shell(model_group, GTK_MENU_SHELL(parent), position + 1);
// Create a new menu item check_menu_item_group_connect_toggle_signal(model_group, on_change_model);
GtkWidget *item = gtk_radio_menu_item_new_with_label(group, model_names[i]); check_menu_item_group_activate(model_group, config.emulation.model);
// Add it to the existing menu
gtk_menu_shell_insert(GTK_MENU_SHELL(parent), item, ++position);
g_signal_connect(item, "toggled", G_CALLBACK(on_change_model), (gpointer) model_codes[i]);
if (g_strcmp0(config.emulation.model, model_codes[i]) == 0) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), true);
}
if (i == 0) group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
}
static const char *const peripheral_names[] = { static const char *const peripheral_names[] = {
"None", "None",
"Game Boy Printer", "Game Boy Printer",
NULL
}; };
static const char *const peripheral_codes[] = { static const char *const peripheral_codes[] = {
"NONE", "NONE",
"PRINTER", "PRINTER",
NULL,
}; };
GtkMenuShell *link_menu = builder_get(GTK_MENU_SHELL, "link_menu"); CheckMenuItemGroup *link_group = check_menu_item_group_new((char **) peripheral_names, (char **) peripheral_codes);
group = NULL; check_menu_item_group_insert_into_menu_shell(link_group, GTK_MENU_SHELL(builder_get(GTK_MENU_SHELL, "link_menu")), 0);
position = 0; // check_menu_item_group_connect_toggle_signal(link_group, on_change_linked_device);
for (int i = 0; i < sizeof(peripheral_names) / sizeof(const char*); i++) { check_menu_item_group_activate(link_group, "NONE");
// Create a new menu item
GtkWidget *item = gtk_radio_menu_item_new_with_label(group, peripheral_names[i]);
// Add it to the existing menu
gtk_menu_shell_insert(link_menu, item, position++);
// g_signal_connect(item, "toggled", G_CALLBACK(on_change_linked_device, (gpointer) peripheral_codes[i]);
if (i == 0) {
group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), true);
}
}
} }
// Create our applications menu. // Create our applications menu.
@ -2100,9 +2082,18 @@ G_MODULE_EXPORT void on_quit_activate(GtkWidget *w, gpointer user_data_ptr) {
quit(); quit();
} }
G_MODULE_EXPORT void on_change_model(GtkCheckMenuItem *check_menu_item, const gchar *model_str) { bool on_change_model(GtkWidget *widget, gpointer user_data) {
if (!GB_is_inited(&gb) || !gtk_check_menu_item_get_active(check_menu_item)) { GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM(widget);
return; gchar *model_str = (gchar *) user_data;
if (!gtk_check_menu_item_get_active(check_menu_item)) {
return true;
}
else if (!GB_is_inited(&gb)) {
gui_data.cli_options.model = -1;
config.emulation.model = g_strdup(model_str);
return false;
} }
GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new( GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new(
@ -2121,8 +2112,6 @@ G_MODULE_EXPORT void on_change_model(GtkCheckMenuItem *check_menu_item, const gc
// Reset the CLI model override // Reset the CLI model override
gui_data.cli_options.model = -1; gui_data.cli_options.model = -1;
// g_simple_action_set_state(action, value);
g_free(config.emulation.model); g_free(config.emulation.model);
config.emulation.model = g_strdup(model_str); config.emulation.model = g_strdup(model_str);
@ -2135,6 +2124,8 @@ G_MODULE_EXPORT void on_change_model(GtkCheckMenuItem *check_menu_item, const gc
run(); run();
gtk_widget_destroy(GTK_WIDGET(dialog)); gtk_widget_destroy(GTK_WIDGET(dialog));
return result != GTK_RESPONSE_YES;
} }
static void connect_signal_handlers(GApplication *app) { static void connect_signal_handlers(GApplication *app) {