--- a/ui/gtk/button.c Sun Jul 20 22:04:39 2025 +0200 +++ b/ui/gtk/button.c Sun Aug 24 15:24:16 2025 +0200 @@ -32,6 +32,8 @@ #include "button.h" #include "container.h" #include <cx/allocator.h> +#include <cx/buffer.h> +#include <cx/json.h> #include "../common/context.h" #include "../common/object.h" @@ -120,6 +122,14 @@ event->callback(&e, event->userdata); } +void ui_button_set_label(UIWIDGET button, const char *label) { + gtk_button_set_label(GTK_BUTTON(button), label); +} + +void ui_button_set_icon(UIWIDGET button, const char *icon) { + ui_button_set_icon_name(button, icon); +} + int64_t ui_toggle_button_get(UiInteger *integer) { GtkToggleButton *button = integer->obj; integer->value = (int)gtk_toggle_button_get_active(button); @@ -594,3 +604,280 @@ value->value = i; } #endif + + +static void ui_destroy_linkbutton(GtkWidget *widget, UiLinkButton *data) { + free(data->link); + free(data); +} + +static const char* linkbutton_get_uri(UiLinkButton *link) { + if(link->type == UI_LINK_BUTTON) { + return link->link; + } else { + return gtk_link_button_get_uri(GTK_LINK_BUTTON(link->widget)); + } +} + +static void linkbutton_set_uri(UiLinkButton *link, const char *uri) { + if(link->type == UI_LINK_BUTTON) { + free(link->link); + link->link = uri ? strdup(uri) : NULL; + } else { + gtk_link_button_set_uri(GTK_LINK_BUTTON(link->widget), uri); + } +} + +static gboolean linkbutton_get_visited(UiLinkButton *link) { + if(link->type == UI_LINK_BUTTON) { + return FALSE; + } else { + gtk_link_button_get_visited(GTK_LINK_BUTTON(link->widget)); + } +} + +static void linkbutton_set_visited(UiLinkButton *link, gboolean visited) { + if(link->type != UI_LINK_BUTTON) { + gtk_link_button_set_visited(GTK_LINK_BUTTON(link->widget), visited); + } +} + +/* + * Apply linkbutton settings from json. Expects jsonvalue to be a valid + * json object. + * + * { + * "label": "label text", + * "uri": "http://example.com", + * "visited": true + * } + * + */ +static void linkbutton_apply_value(UiLinkButton *link, const char *jsonvalue) { + CxJson json; + cxJsonInit(&json, NULL); + cxJsonFill(&json, jsonvalue); + + CxJsonValue *value; + if(cxJsonNext(&json, &value) == CX_JSON_NO_ERROR) { + if(cxJsonIsObject(value)) { + CxJsonValue *label = cxJsonObjGet(value, "label"); + CxJsonValue *uri = cxJsonObjGet(value, "uri"); + CxJsonValue *visited = cxJsonObjGet(value, "visited"); + if(label) { + gtk_button_set_label(GTK_BUTTON(link->widget), cxJsonIsString(label) ? cxJsonAsString(label) : NULL); + } + if(uri) { + linkbutton_set_uri(link, cxJsonIsString(uri) ? cxJsonAsString(uri) : NULL); + + } + if(visited) { + linkbutton_set_visited(link, cxJsonIsBool(visited) ? cxJsonAsBool(visited) : FALSE); + } + } + cxJsonValueFree(value); + } + cxJsonDestroy(&json); +} + +static char* create_linkbutton_jsonvalue(const char *label, const char *uri, gboolean include_null, gboolean visited, gboolean set_visited) { + CxJsonValue *obj = cxJsonCreateObj(NULL); + if(label) { + cxJsonObjPutString(obj, CX_STR("label"), label); + } else if(include_null) { + cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL); + } + + if(uri) { + cxJsonObjPutString(obj, CX_STR("uri"), uri); + } else if(include_null) { + cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL); + } + + if(set_visited) { + cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE); + } + + CxJsonWriter writer = cxJsonWriterCompact(); + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND); + cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer); + cxJsonValueFree(obj); + cxBufferTerminate(&buf); + + return buf.space; +} + +static char* linkbutton_get_value(UiLinkButton *link) { + const char *label = gtk_button_get_label(GTK_BUTTON(link->widget)); + const char *uri = linkbutton_get_uri(link); + gboolean visited = linkbutton_get_visited(link); + return create_linkbutton_jsonvalue(label, uri, TRUE, visited, TRUE); +} + +static void linkbutton_clicked(GtkWidget *widget, UiLinkButton *data) { + if(data->onclick) { + UiEvent e; + e.obj = data->obj; + e.document = e.obj->ctx->document; + e.window = e.obj->window; + e.eventdata = (char*)linkbutton_get_uri(data); + e.eventdatatype = UI_EVENT_DATA_STRING; + e.intval = 0; + e.set = ui_get_setop(); + data->onclick(&e, data->onclickdata); + } +} + +static gboolean linkbutton_activate_link(GtkLinkButton *self, UiLinkButton *data) { + linkbutton_clicked(data->widget, data); + return data->nofollow; +} + +UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) { + UiLinkButton *data = malloc(sizeof(UiLinkButton)); + memset(data, 0, sizeof(UiLinkButton)); + data->obj = obj; + data->type = args->type; + data->nofollow = args->nofollow; + data->onclick = args->onclick; + data->onclickdata = args->onclickdata; + + GtkWidget *button; + if(args->type == UI_LINK_BUTTON) { + button = gtk_button_new(); + g_signal_connect( + button, + "clicked", + G_CALLBACK(linkbutton_clicked), + data); + } else { + button = gtk_link_button_new("file:///"); + g_signal_connect( + button, + "activate-link", + G_CALLBACK(linkbutton_activate_link), + data); + } + gtk_button_set_label(GTK_BUTTON(button), args->label); + g_object_set_data(G_OBJECT(button), "ui_linkbutton", data); + g_signal_connect( + button, + "destroy", + G_CALLBACK(ui_destroy_linkbutton), + data); + + data->widget = button; + + UiObject* current = uic_current_obj(obj); + UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING); + if(var) { + UiString *str = var->value; + char *current_value = ui_get(str); + if(current_value) { + linkbutton_apply_value(data, current_value); + } + + str->obj = data; + str->get = ui_linkbutton_get; + str->set = ui_linkbutton_set; + } + + ui_set_name_and_style(button, args->name, args->style_class); + ui_set_widget_groups(obj->ctx, button, args->groups); + UI_APPLY_LAYOUT2(current, args); + current->container->add(current->container, button); + + return button; +} + +char* ui_linkbutton_get(UiString *s) { + UiLinkButton *link = s->obj; + if(s->value.free) { + s->value.free(s->value.ptr); + } + s->value.ptr = linkbutton_get_value(link); + s->value.free = free; + return s->value.ptr; +} + +void ui_linkbutton_set(UiString *s, const char *str) { + linkbutton_apply_value(s->obj, str); + if(s->value.free) { + s->value.free(s->value.ptr); + } +} + + +void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri) { + char *value = create_linkbutton_jsonvalue(label, uri, TRUE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_label(UiString *str, const char *label) { + char *value = create_linkbutton_jsonvalue(label, NULL, FALSE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_uri(UiString *str, const char *uri) { + char *value = create_linkbutton_jsonvalue(NULL, uri, FALSE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_visited(UiString *str, UiBool visited) { + char *value = create_linkbutton_jsonvalue(NULL, NULL, FALSE, visited, TRUE); + ui_set(str, value); + free(value); +} + + +void ui_linkbutton_set_label(UIWIDGET button, const char *label) { + gtk_button_set_label(GTK_BUTTON(button), label); +} + +void ui_linkbutton_set_uri(UIWIDGET button, const char *label) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + linkbutton_set_uri(link, label); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } +} + +void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + linkbutton_set_visited(link, visited); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } +} + +char* ui_linkbutton_get_label(UIWIDGET button) { + const char *label = gtk_button_get_label(GTK_BUTTON(button)); + return label ? strdup(label) : NULL; +} + +char* ui_linkbutton_get_uri(UIWIDGET button) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + const char *uri = linkbutton_get_uri(link); + return uri ? strdup(uri) : NULL; + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } + return NULL; +} + +UiBool ui_linkbutton_get_visited(UIWIDGET button) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + return linkbutton_get_visited(link); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } + return FALSE; +}