ui/gtk/button.c

changeset 431
bb7da585debc
parent 403
b59935b2de79
--- a/ui/gtk/button.c	Sun May 23 09:44:43 2021 +0200
+++ b/ui/gtk/button.c	Sat Jan 04 16:38:48 2025 +0100
@@ -31,19 +31,48 @@
 
 #include "button.h"
 #include "container.h"
-#include <ucx/mempool.h>
+#include <cx/allocator.h>
 #include "../common/context.h"
 #include "../common/object.h"
 
-UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data) {
+void ui_button_set_icon_name(GtkWidget *button, const char *icon) {
+    if(!icon) {
+        return;
+    }
+    
+#ifdef UI_GTK4
+    gtk_button_set_icon_name(GTK_BUTTON(button), icon);
+#else
+#if GTK_CHECK_VERSION(2, 6, 0)
+    GtkWidget *image = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON);
+    if(image) {
+        gtk_button_set_image(GTK_BUTTON(button), image);
+    }
+#else
+    // TODO
+#endif
+#endif
+}
+
+GtkWidget* ui_create_button(
+        UiObject *obj,
+        const char *label,
+        const char *icon,
+        ui_callback onclick,
+        void *userdata,
+        int event_value,
+        bool activate_event)
+{
     GtkWidget *button = gtk_button_new_with_label(label);
+    ui_button_set_icon_name(button, icon);
     
-    if(f) {
+    if(onclick) {
         UiEventData *event = malloc(sizeof(UiEventData));
         event->obj = obj;
-        event->userdata = data;
-        event->callback = f;
-        event->value = 0;
+        event->userdata = userdata;
+        event->callback = onclick;
+        event->value = event_value;
+        event->customdata = NULL;
 
         g_signal_connect(
                 button,
@@ -55,11 +84,25 @@
                 "destroy",
                 G_CALLBACK(ui_destroy_userdata),
                 event);
+        if(activate_event) {
+            g_signal_connect(
+                button,
+                "activate",
+                G_CALLBACK(ui_button_clicked),
+                event);
+        }
     }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, button, FALSE);
-    
+    return button;
+}
+
+UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    GtkWidget *button = ui_create_button(obj, args.label, args.icon, args.onclick, args.onclickdata, 0, FALSE);
+    ui_set_name_and_style(button, args.name, args.style_class);
+    ui_set_widget_groups(obj->ctx, button, args.groups);
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, button, FALSE);
     return button;
 }
 
@@ -86,81 +129,318 @@
     gtk_toggle_button_set_active(button, value != 0 ? TRUE : FALSE);
 }
 
-void ui_toggled_obs(GtkToggleToolButton *widget, UiVarEventData *event) {
+void ui_toggled_obs(void *widget, UiVarEventData *event) {
+    UiInteger *i = event->var->value;
     UiEvent e;
     e.obj = event->obj;
     e.window = event->obj->window;
     e.document = event->obj->ctx->document;
     e.eventdata = event->var->value;
-    e.intval = gtk_toggle_tool_button_get_active(widget);
+    e.intval = i->get(i);  
     
-    UiInteger *i = event->var->value;
     ui_notify_evt(i->observers, &e);
 }
 
-UIWIDGET ui_checkbox_var(UiObject *obj, char *label, UiVar *var) {
-    GtkWidget *button = gtk_check_button_new_with_label(label);
+static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) {
+    UiEvent e;
+    e.obj = event->obj;
+    e.window = event->obj->window;
+    e.document = event->obj->ctx->document;
+    e.eventdata = NULL;
+    e.intval = gtk_toggle_button_get_active(widget);
+    event->callback(&e, event->userdata);    
+}
+
+static void ui_togglebutton_enable_state_callback(GtkToggleButton *widget, UiEventData *event) {
+    if(gtk_toggle_button_get_active(widget)) {
+        ui_set_group(event->obj->ctx, event->value);
+    } else {
+        ui_unset_group(event->obj->ctx, event->value);
+    }
+}
+
+void ui_setup_togglebutton(
+        UiObject *obj,
+        GtkWidget *togglebutton,
+        const char *label,
+        const char *icon,
+        const char *varname,
+        UiInteger *value,
+        ui_callback onchange,
+        void *onchangedata,
+        int enable_state)
+{
+    if(label) {
+        gtk_button_set_label(GTK_BUTTON(togglebutton), label);
+    }
+    ui_button_set_icon_name(togglebutton, icon);
     
-    // bind value
-    if(var) {
-        UiInteger *value = var->value;
-        value->obj = GTK_TOGGLE_BUTTON(button);
-        value->get = ui_toggle_button_get;
-        value->set = ui_toggle_button_set;
-        gtk_toggle_button_set_active(value->obj, value->value);
+    ui_bind_togglebutton(
+            obj,
+            togglebutton,
+            ui_toggle_button_get,
+            ui_toggle_button_set,
+            varname,
+            value,
+            (ui_toggled_func)ui_toggled_callback,
+            onchange,
+            onchangedata,
+            (ui_toggled_func)ui_togglebutton_enable_state_callback,
+            enable_state
+            );
+}
+
+void ui_bind_togglebutton(
+        UiObject *obj,
+        GtkWidget *widget,
+        int64_t (*getfunc)(UiInteger*),
+        void (*setfunc)(UiInteger*, int64_t),
+        const char *varname,
+        UiInteger *value,
+        void (*toggled_callback)(void*, void*),
+        ui_callback onchange,
+        void *onchangedata,
+        void (*enable_state_func)(void*, void*),
+        int enable_state)
+{
+    UiObject* current = uic_current_obj(obj);
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, value, varname, UI_VAR_INTEGER);
+    if (var) {
+        UiInteger* value = (UiInteger*)var->value;
+        value->obj = widget;
+        value->get = getfunc;
+        value->set = setfunc;
         
         UiVarEventData *event = malloc(sizeof(UiVarEventData));
         event->obj = obj;
         event->var = var;
         event->observers = NULL;
+        event->callback = NULL;
+        event->userdata = NULL;
 
         g_signal_connect(
-                button,
-                "clicked",
+                widget,
+                "toggled",
                 G_CALLBACK(ui_toggled_obs),
                 event);
         g_signal_connect(
-                button,
+                widget,
                 "destroy",
                 G_CALLBACK(ui_destroy_vardata),
                 event);
     }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, button, FALSE);
+    if(onchange) {
+        UiEventData *event = malloc(sizeof(UiEventData));
+        event->obj = obj;
+        event->userdata = onchangedata;
+        event->callback = onchange;
+        event->value = 0;
+        event->customdata = NULL;
+        
+        g_signal_connect(
+                widget,
+                "toggled",
+                G_CALLBACK(toggled_callback),
+                event);
+        g_signal_connect(
+                widget,
+                "destroy",
+                G_CALLBACK(ui_destroy_userdata),
+                event);
+    }
+    
+    if(enable_state > 0) {
+        UiEventData *event = malloc(sizeof(UiEventData));
+        event->obj = obj;
+        event->userdata = NULL;
+        event->callback = NULL;
+        event->value = enable_state;
+        event->customdata = NULL;
+        
+        g_signal_connect(
+                widget,
+                "toggled",
+                G_CALLBACK(enable_state_func),
+                event);
+        g_signal_connect(
+                widget,
+                "destroy",
+                G_CALLBACK(ui_destroy_userdata),
+                event);
+    }
+}
+
+static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs args) {
+    UiObject* current = uic_current_obj(obj);
     
-    return button;
+    ui_setup_togglebutton(
+            current,
+            widget,
+            args.label,
+            args.icon,
+            args.varname,
+            args.value,
+            args.onchange,
+            args.onchangedata,
+            args.enable_group);
+    ui_set_name_and_style(widget, args.name, args.style_class);
+    ui_set_widget_groups(obj->ctx, widget, args.groups);
+    
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, widget, FALSE);
+    
+    return widget;
+}
+
+UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) {
+    return togglebutton_create(obj, gtk_toggle_button_new(), args);
+}
+
+#if GTK_MAJOR_VERSION >= 4
+
+int64_t ui_check_button_get(UiInteger *integer) {
+    GtkCheckButton *button = integer->obj;
+    integer->value = (int)gtk_check_button_get_active(button);
+    return integer->value;
+}
+
+void ui_check_button_set(UiInteger *integer, int64_t value) {
+    GtkCheckButton *button = integer->obj;
+    integer->value = value;
+    gtk_check_button_set_active(button, value != 0 ? TRUE : FALSE);
+}
+
+static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) {
+    UiEvent e;
+    e.obj = event->obj;
+    e.window = event->obj->window;
+    e.document = event->obj->ctx->document;
+    e.eventdata = NULL;
+    e.intval = gtk_check_button_get_active(widget);
+    event->callback(&e, event->userdata);    
 }
 
-UIWIDGET ui_checkbox(UiObject *obj, char *label, UiInteger *value) {
-    UiVar *var = NULL;
-    if(value) {
-        var = malloc(sizeof(UiVar));
-        var->value = value;
-        var->type = UI_VAR_SPECIAL;
+static void ui_checkbox_enable_state(GtkCheckButton *widget, UiEventData *event) {
+    if(gtk_check_button_get_active(widget)) {
+        ui_set_group(event->obj->ctx, event->value);
+    } else {
+        ui_unset_group(event->obj->ctx, event->value);
     }
-    return ui_checkbox_var(obj, label, var);
+}
+
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
+    GtkWidget *widget = gtk_check_button_new_with_label(args.label); 
+    ui_bind_togglebutton(
+            obj,
+            widget,
+            ui_check_button_get,
+            ui_check_button_set,
+            args.varname,
+            args.value,
+            (ui_toggled_func)ui_checkbox_callback,
+            args.onchange,
+            args.onchangedata,
+            (ui_toggled_func)ui_checkbox_enable_state,
+            args.enable_group);
+    
+    ui_set_name_and_style(widget, args.name, args.style_class);
+    ui_set_widget_groups(obj->ctx, widget, args.groups);
+    
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, widget, FALSE);
+    
+    return widget;
 }
 
-UIWIDGET ui_checkbox_nv(UiObject *obj, char *label, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_INTEGER);
-    return ui_checkbox_var(obj, label, var);
+#else
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) {
+    return togglebutton_create(obj, gtk_check_button_new(), args);
+}
+#endif
+
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) {
+#ifdef UI_GTK3
+    return NULL; // TODO
+#else
+    return ui_checkbox_create(obj, args);
+#endif
 }
 
+#if GTK_MAJOR_VERSION >= 4
+#define RADIOBUTTON_NEW(group, label) gtk_check_button_new_with_label(label)
+#define RADIOBUTTON_SET_GROUP(button, group) 
+#define RADIOBUTTON_GET_GROUP(button) GTK_CHECK_BUTTON(button)
+#define RADIOBUTTON_GET_ACTIVE(button) gtk_check_button_get_active(GTK_CHECK_BUTTON(button))
+#else
+#define RADIOBUTTON_NEW(group, label) gtk_radio_button_new_with_label(group, label)
+#define RADIOBUTTON_SET_GROUP(button, group) /* noop */
+#define RADIOBUTTON_GET_GROUP(button) gtk_radio_button_get_group(GTK_RADIO_BUTTON(button))
+#define RADIOBUTTON_GET_ACTIVE(button) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))
+#endif
 
-UIWIDGET ui_radiobutton_var(UiObject *obj, char *label, UiVar *var) {
+static void radiobutton_toggled(void *widget, UiEventData *event) {
+    UiEvent e;
+    e.obj = event->obj;
+    e.window = event->obj->window;
+    e.document = event->obj->ctx->document;
+    e.eventdata = NULL;
+    e.intval = RADIOBUTTON_GET_ACTIVE(widget);
+    event->callback(&e, event->userdata);    
+}
+
+typedef struct UiRadioButtonData {
+    UiInteger *value;
+    UiVarEventData *eventdata;
+    UiBool first;
+} UiRadioButtonData;
+
+static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) {
+    if(data->first) {
+        ui_destroy_vardata(w, data->eventdata);
+        g_slist_free(data->value->obj);
+        data->value->obj = NULL;
+        data->value->get = NULL;
+        data->value->set = NULL;
+    } else {
+        free(data->eventdata);
+    }
+    free(data);
+}
+
+UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
     GSList *rg = NULL;
     UiInteger *rgroup;
     
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER);
+    
+    UiBool first = FALSE;
     if(var) {
         rgroup = var->value;
         rg = rgroup->obj;
+        if(!rg) {
+            first = TRUE;
+        }
     }
     
-    GtkWidget *rbutton = gtk_radio_button_new_with_label(rg, label);
-    rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton));
-    
+    GtkWidget *rbutton = RADIOBUTTON_NEW(rg, args.label); 
+    ui_set_name_and_style(rbutton, args.name, args.style_class);
+    ui_set_widget_groups(obj->ctx, rbutton, args.groups);
     if(rgroup) {
+#if GTK_MAJOR_VERSION >= 4
+        if(rg) {
+            gtk_check_button_set_group(GTK_CHECK_BUTTON(rbutton), rg->data);
+        }
+        rg = g_slist_prepend(rg, rbutton);
+#else
+        gtk_radio_button_set_group(GTK_RADIO_BUTTON(rbutton), rg);
+        rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton));
+#endif
+        
         rgroup->obj = rg;
         rgroup->get = ui_radiobutton_get;
         rgroup->set = ui_radiobutton_set;
@@ -171,41 +451,53 @@
         event->obj = obj;
         event->var = var;
         event->observers = NULL;
+        event->callback = NULL;
+        event->userdata = NULL;
+        
+        UiRadioButtonData *rbdata = malloc(sizeof(UiRadioButtonData));
+        rbdata->value = rgroup;
+        rbdata->eventdata = event;
+        rbdata->first = first;
         
         g_signal_connect(
                 rbutton,
-                "clicked",
+                "toggled",
                 G_CALLBACK(ui_radio_obs),
                 event);
         g_signal_connect(
                 rbutton,
                 "destroy",
-                G_CALLBACK(ui_destroy_vardata),
+                G_CALLBACK(destroy_radiobutton),
+                rbdata);
+    }
+    
+    if(args.onchange) {
+        UiEventData *event = malloc(sizeof(UiEventData));
+        event->obj = obj;
+        event->userdata = args.onchangedata;
+        event->callback = args.onchange;
+        event->value = 0;
+        event->customdata = NULL;
+        
+        g_signal_connect(
+                rbutton,
+                "toggled",
+                G_CALLBACK(radiobutton_toggled),
+                event);
+        g_signal_connect(
+                rbutton,
+                "destroy",
+                G_CALLBACK(ui_destroy_userdata),
                 event);
     }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, rbutton, FALSE);
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, rbutton, FALSE);
     
     return rbutton;
 }
 
-UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup) {
-    UiVar *var = NULL;
-    if(rgroup) {
-        var = malloc(sizeof(UiVar));
-        var->value = rgroup;
-        var->type = UI_VAR_SPECIAL;
-    }
-    return ui_radiobutton_var(obj, label, var);
-}
-
-UIWIDGET ui_radiobutton_nv(UiObject *obj, char *label, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_INTEGER);
-    return ui_radiobutton_var(obj, label, var);
-}
-
-void ui_radio_obs(GtkToggleToolButton *widget, UiVarEventData *event) {
+void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event) {
     UiInteger *i = event->var->value;
     
     UiEvent e;
@@ -218,6 +510,41 @@
     ui_notify_evt(i->observers, &e);
 }
 
+#if GTK_MAJOR_VERSION >= 4
+int64_t ui_radiobutton_get(UiInteger *value) {
+    int selection = 0;
+    GSList *ls = value->obj;
+    int i = 0;
+    guint len = g_slist_length(ls);
+    while(ls) {
+        if(gtk_check_button_get_active(GTK_CHECK_BUTTON(ls->data))) {
+            selection = len - i - 1;
+            break;
+        }
+        ls = ls->next;
+        i++;
+    }
+    
+    value->value = selection;
+    return selection;
+}
+
+void ui_radiobutton_set(UiInteger *value, int64_t i) {
+    GSList *ls = value->obj;
+    int s = g_slist_length(ls) - 1 - i;
+    int j = 0;
+    while(ls) {
+        if(j == s) {
+            gtk_check_button_set_active(GTK_CHECK_BUTTON(ls->data), TRUE);
+            break;
+        }
+        ls = ls->next;
+        j++;
+    }
+    
+    value->value = i;
+}
+#else
 int64_t ui_radiobutton_get(UiInteger *value) {
     int selection = 0;
     GSList *ls = value->obj;
@@ -251,4 +578,4 @@
     
     value->value = i;
 }
-
+#endif

mercurial