Mon, 17 Nov 2025 14:27:48 +0100
fix var creation when a widget is already bound to this var name
| application/main.c | file | annotate | diff | comparison | revisions | |
| ui/common/context.c | file | annotate | diff | comparison | revisions | |
| ui/common/context.h | file | annotate | diff | comparison | revisions | |
| ui/common/types.c | file | annotate | diff | comparison | revisions | |
| ui/common/types.h | file | annotate | diff | comparison | revisions | |
| ui/ui/toolkit.h | file | annotate | diff | comparison | revisions |
--- a/application/main.c Mon Nov 17 11:28:47 2025 +0100 +++ b/application/main.c Mon Nov 17 14:27:48 2025 +0100 @@ -922,6 +922,8 @@ UiInteger *spinner; UiInteger *tab; UiDouble *num; + UiInteger *toggle1; + UiInteger *toggle2; } WData; @@ -961,6 +963,12 @@ ui_set(wdata->tab, index+1); } +static void action_menu_toggle(UiEvent *event, void *data) { + WData *wdata = event->window; + printf("toggle1: %d\n", (int)ui_get(wdata->toggle1)); + printf("toggle2: %d\n\n", (int)ui_get(wdata->toggle2)); +} + void application_startup(UiEvent *event, void *data) { menulist = ui_list_new(ui_global_context(), "menulist"); @@ -977,6 +985,8 @@ wdata->spinner = ui_int_new(obj->ctx, NULL); wdata->tab = ui_int_new(obj->ctx, NULL); wdata->num = ui_double_new(obj->ctx, "num"); + wdata->toggle1 = ui_int_new(obj->ctx, "toggle1"); + wdata->toggle2 = ui_int_new(obj->ctx, "toggle2"); obj->window = wdata; ui_list_append(wdata->list, "List Item 1"); @@ -1053,8 +1063,8 @@ ui_menuitem(.label = "Test 2", .onclick = action_test); ui_menuitem(.label = "Test 3", .onclick = action_test); ui_menuseparator(); - ui_menu_toggleitem(.label = "Toggle 1"); - ui_menu_toggleitem(.label = "Toggle 2"); + ui_menu_toggleitem(.label = "Toggle 1", .varname = "toggle1", .onchange = action_menu_toggle); + ui_menu_toggleitem(.label = "Toggle 2", .varname = "toggle2", .onchange = action_menu_toggle); ui_menuseparator(); ui_menu_radioitem(.label = "Radio 1", .varname = "menu_radio"); ui_menu_radioitem(.label = "Radio 2", .varname = "menu_radio");
--- a/ui/common/context.c Mon Nov 17 11:28:47 2025 +0100 +++ b/ui/common/context.c Mon Nov 17 14:27:48 2025 +0100 @@ -114,7 +114,7 @@ UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); if(docvar) { // bind var to document var - uic_copy_binding(var, docvar, TRUE); + uic_copy_var_binding(var, docvar, TRUE); cxIteratorFlagRemoval(i); } } @@ -130,7 +130,7 @@ // var->from && var->from_ctx && var->from_ctx != ctx uic_save_var(var); if(var->from) { - uic_copy_binding(var, var->from, FALSE); + uic_copy_var_binding(var, var->from, FALSE); cxMapPut(var->from->from_ctx->vars, *entry->key, var->from); var->from = NULL; } @@ -213,7 +213,8 @@ var->original_value = NULL; var->from = NULL; var->from_ctx = ctx; - + var->bound = FALSE; + cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); cxMapPut(ctx->vars, name, var); @@ -266,6 +267,40 @@ return val; } +// destroys a value, that was created by uic_create_value +void uic_destroy_value(UiContext *ctx, UiVarType type, void *value) { + switch(type) { + default: { + ui_free(ctx, value); + break; + } + case UI_VAR_SPECIAL: break; + case UI_VAR_STRING: { + UiString *s = value; + if(s->value.free) { + s->value.free(s->value.ptr); + } + ui_free(ctx, value); + } + case UI_VAR_TEXT: { + UiText *t = value; + if(t->value.free) { + t->value.free(t->value.ptr); + t->value.free = NULL; + t->value.ptr = NULL; + } + if(t->destroy) { + t->destroy(t); + } + ui_free(ctx, value); + } + case UI_VAR_LIST: { + ui_list_free(ctx, value); + break; + } + } +} + UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type) { if (value) { @@ -278,7 +313,7 @@ } -void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { +void uic_copy_var_binding(UiVar *from, UiVar *to, UiBool copytodoc) { // check type if(from->type != to->type) { fprintf(stderr, "UI Error: var has incompatible type.\n"); @@ -301,32 +336,36 @@ } } - ui_setop_enable(TRUE); + uic_copy_value_binding(from->type, from, to); +} + +void uic_copy_value_binding(UiVarType type, void *from, void *to) { + ui_setop_enable(TRUE); // copy binding - // we don't copy the observer, because the from var has never one - switch(from->type) { + // we don't copy the observer, because the from value never has oberservers + switch(type) { default: fprintf(stderr, "uic_copy_binding: wtf!\n"); break; case UI_VAR_SPECIAL: break; case UI_VAR_INTEGER: { - UiInteger *f = fromvalue; - UiInteger *t = tovalue; + UiInteger *f = from; + UiInteger *t = to; if(!f->obj) break; uic_int_copy(f, t); t->set(t, t->value); break; } case UI_VAR_DOUBLE: { - UiDouble *f = fromvalue; - UiDouble *t = tovalue; + UiDouble *f = from; + UiDouble *t = to; if(!f->obj) break; uic_double_copy(f, t); t->set(t, t->value); break; } case UI_VAR_STRING: { - UiString *f = fromvalue; - UiString *t = tovalue; + UiString *f = from; + UiString *t = to; if(!f->obj) break; uic_string_copy(f, t); char *tvalue = t->value.ptr ? t->value.ptr : ""; @@ -335,23 +374,23 @@ break; } case UI_VAR_TEXT: { - UiText *f = fromvalue; - UiText *t = tovalue; + UiText *f = from; + UiText *t = to; if(!f->obj) break; uic_text_copy(f, t); t->restore(t); break; } case UI_VAR_LIST: { - UiList *f = fromvalue; - UiList *t = tovalue; + UiList *f = from; + UiList *t = to; uic_list_copy(f, t); ui_list_update(t); break; } case UI_VAR_RANGE: { - UiRange *f = fromvalue; - UiRange *t = tovalue; + UiRange *f = from; + UiRange *t = to; if(!f->obj) break; uic_range_copy(f, t); t->setextent(t, t->extent); @@ -360,8 +399,8 @@ break; } case UI_VAR_GENERIC: { - UiGeneric *f = fromvalue; - UiGeneric *t = tovalue; + UiGeneric *f = from; + UiGeneric *t = to; if(!f->obj) break; uic_generic_copy(f, t); t->set(t, t->value, t->type); @@ -398,58 +437,47 @@ } } +const char *uic_type2str(UiVarType type) { + switch(type) { + default: return ""; + case UI_VAR_INTEGER: return "int"; + case UI_VAR_DOUBLE: return "double"; + case UI_VAR_STRING: return "string"; + case UI_VAR_TEXT: return "text"; + case UI_VAR_LIST: return "list"; + case UI_VAR_RANGE: return "range"; + case UI_VAR_GENERIC: return "generic"; + } +} + void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value) { - // TODO: do we need/want this? Why adding vars to a context after - // widgets reference these? Workarounds: - // 1. add vars to ctx before creating ui - // 2. create ui, create new document with vars, attach doc - // also it would be possible to create a function, that scans unbound vars - // and connects them to available vars - /* - UiContext *rootctx = uic_root_context(ctx); - UiVar *b = NULL; - if(rootctx->bound) { - // some widgets are already bound to some vars - b = ucx_map_cstr_get(rootctx->bound, name); - if(b) { - // a widget is bound to a var with this name - // if ctx is the root context we can remove the var from bound - // because set_doc or detach can't fuck things up - if(ctx == rootctx) { - ucx_map_cstr_remove(ctx->bound, name); - // TODO: free stuff - } + UiVar *var = cxMapGet(ctx->vars, name); + if(!var) { + // create new var and add it to the context var map + var = ui_malloc(ctx, sizeof(UiVar)); + cxMapPut(ctx->vars, name, var); + } else { + // override var with new value + if(var->type != type) { + fprintf(stderr, "Error: var %s type mismatch: %s - %s\n", name, uic_type2str(var->type), type); + return; } + if(var->bound) { + fprintf(stderr, "Error: var %s already bound\n", name); + return; + } + UiInteger *prev_value = var->value; + uic_copy_value_binding(type, prev_value, value); + uic_destroy_value(var->from_ctx, var->type, var->value); } - */ - // create new var and add it to doc's vars - UiVar *var = ui_malloc(ctx, sizeof(UiVar)); var->type = type; var->value = value; var->from = NULL; var->from_ctx = ctx; - size_t oldcount = cxMapSize(ctx->vars); - cxMapPut(ctx->vars, name, var); - if(cxMapSize(ctx->vars) != oldcount + 1) { - fprintf(stderr, "UiError: var '%s' already exists\n", name); - } - - // TODO: remove? - // a widget is already bound to a var with this name - // copy the binding (like uic_context_set_document) - /* - if(b) { - uic_copy_binding(b, var, TRUE); - } - */ + var->bound = TRUE; } -void uic_remove_bound_var(UiContext *ctx, UiVar *var) { - // TODO -} - - // public API void ui_attach_document(UiContext *ctx, void *document) {
--- a/ui/common/context.h Mon Nov 17 11:28:47 2025 +0100 +++ b/ui/common/context.h Mon Nov 17 14:27:48 2025 +0100 @@ -97,6 +97,7 @@ UiVarType type; UiVar *from; UiContext *from_ctx; + UiBool bound; }; struct UiGroupWidget { @@ -130,17 +131,18 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type); UiVar* uic_create_value_var(UiContext *ctx, void *value); void* uic_create_value(UiContext *ctx, UiVarType type); +void uic_destroy_value(UiContext *ctx, UiVarType type, void *value); UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type); -void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc); +void uic_copy_var_binding(UiVar *from, UiVar *to, UiBool copytodoc); +void uic_copy_value_binding(UiVarType type, void *from, void *to); void uic_save_var(UiVar *var); void uic_unbind_var(UiVar *var); +const char *uic_type2str(UiVarType type); void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value); -void uic_remove_bound_var(UiContext *ctx, UiVar *var); - size_t uic_group_array_size(const int *groups); void uic_check_group_widgets(UiContext *ctx); void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups);
--- a/ui/common/types.c Mon Nov 17 11:28:47 2025 +0100 +++ b/ui/common/types.c Mon Nov 17 14:27:48 2025 +0100 @@ -40,6 +40,8 @@ static ui_list_init_func default_list_init; static void *default_list_init_userdata; +static ui_list_destroy_func default_list_destroy; +static void *default_list_destroy_userdata; UiObserver* ui_observer_new(ui_callback f, void *data) { UiObserver *observer = malloc(sizeof(UiObserver)); @@ -105,6 +107,11 @@ list->count = ui_list_count; } +void uic_ucx_list_destroy(UiContext *ctx, UiList *list, void *unused) { + cxListFree(list->data); + ui_free(ctx, list); +} + UiList* ui_list_new(UiContext *ctx, const char *name) { return ui_list_new2(ctx, name, default_list_init ? default_list_init : uic_ucx_list_init, default_list_init_userdata); } @@ -121,9 +128,13 @@ return list; } -void ui_list_free(UiList *list) { - cxListFree(list->data); - free(list); +void ui_list_free(UiContext *ctx, UiList *list) { + if(!default_list_destroy) { + uic_ucx_list_destroy(ctx, list, NULL); + } else { + default_list_destroy(ctx, list, default_list_destroy_userdata); + } + } void* ui_list_first(UiList *list) { @@ -803,6 +814,11 @@ default_list_init_userdata = userdata; } +void ui_global_list_destructor(ui_list_destroy_func func, void *userdata) { + default_list_destroy = func; + default_list_destroy_userdata = userdata; +} + void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list)) { list->first = first; }
--- a/ui/common/types.h Mon Nov 17 11:28:47 2025 +0100 +++ b/ui/common/types.h Mon Nov 17 14:27:48 2025 +0100 @@ -36,6 +36,7 @@ #endif void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused); +void uic_ucx_list_destroy(UiContext *ctx, UiList *list, void *unused); void uic_int_copy(UiInteger *from, UiInteger *to); void uic_double_copy(UiDouble *from, UiDouble *to);
--- a/ui/ui/toolkit.h Mon Nov 17 11:28:47 2025 +0100 +++ b/ui/ui/toolkit.h Mon Nov 17 14:27:48 2025 +0100 @@ -417,6 +417,7 @@ }; typedef void (*ui_list_init_func)(UiContext *ctx, UiList *list, void *userdata); +typedef void (*ui_list_destroy_func)(UiContext *ctx, UiList *list, void *userdata); /* * abstract list @@ -635,7 +636,7 @@ UIEXPORT UiList* ui_list_new(UiContext *ctx, const char *name); UIEXPORT UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func init, void *userdata); -UIEXPORT void ui_list_free(UiList *list); +UIEXPORT void ui_list_free(UiContext *ctx, UiList *list); UIEXPORT void* ui_list_first(UiList *list); UIEXPORT void* ui_list_next(UiList *list); UIEXPORT void* ui_list_get(UiList *list, int i); @@ -683,6 +684,7 @@ UIEXPORT void ui_global_list_initializer(ui_list_init_func func, void *userdata); +UIEXPORT void ui_global_list_destructor(ui_list_destroy_func func, void *userdata); UIEXPORT void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list)); UIEXPORT void ui_list_class_set_next(UiList *list, void*(*next)(UiList *list)); UIEXPORT void ui_list_class_set_get(UiList *list, void*(*get)(UiList *list, int i));