ui/common/context.c

changeset 115
e57ca2747782
parent 113
dde28a806552
--- a/ui/common/context.c	Sun Dec 07 20:00:33 2025 +0100
+++ b/ui/common/context.c	Sat Dec 13 15:58:58 2025 +0100
@@ -64,8 +64,8 @@
     ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16);
     
     ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, CX_STORE_POINTERS);
-    ctx->group_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiGroupWidget));
-    ctx->groups = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32);
+    ctx->state_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiStateWidget));
+    ctx->states = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32);
     
     ctx->attach_document = uic_context_attach_document;
     ctx->detach_document2 = uic_context_detach_document;
@@ -92,11 +92,17 @@
 }
 
 void uic_context_prepare_close(UiContext *ctx) {
-    cxListClear(ctx->groups);
-    cxListClear(ctx->group_widgets);
+    cxListClear(ctx->states);
+    cxListClear(ctx->state_widgets);
 }
 
 void uic_context_attach_document(UiContext *ctx, void *document) {
+    if(ctx->single_document_mode) {
+        if(ctx->document) {
+            uic_context_detach_document(ctx, ctx->document);
+        }
+    }
+    
     cxListAdd(ctx->documents, document);
     ctx->document = document;
     
@@ -114,7 +120,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);
             }
         }
@@ -124,16 +130,19 @@
 }
 
 static void uic_context_unbind_vars(UiContext *ctx) {
+    ui_onchange_events_enable(FALSE);
     CxMapIterator mi = cxMapIterator(ctx->vars);
     cx_foreach(CxMapEntry*, entry, mi) {
+        printf("detach %.*s\n", (int)entry->key->len, (char*)entry->key->data);
         UiVar *var = entry->value;
         // 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;
         }
+        uic_unbind_var(var);
     }
     
     if(ctx->documents) {
@@ -143,6 +152,8 @@
             uic_context_unbind_vars(subctx);
         }
     }
+    
+    ui_onchange_events_enable(TRUE);
 }
 
 void uic_context_detach_document(UiContext *ctx, void *document) {
@@ -197,6 +208,14 @@
     return ctx_getvar(ctx, key);
 }
 
+UiVar* uic_get_var_t(UiContext *ctx,const char *name, UiVarType type) {
+    UiVar *var = uic_get_var(ctx, name);
+    if(var && var->type == type) {
+        return var;
+    }
+    return NULL;
+}
+
 UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
     UiVar *var = uic_get_var(ctx, name);
     if(var) {
@@ -213,8 +232,9 @@
     var->original_value = NULL;
     var->from = NULL;
     var->from_ctx = ctx;
-
-    cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var);
+    var->bound = FALSE;
+    
+    cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); // TODO: use another destructor that cleans the value (UiString free, UiText destroy, ...)
 
     cxMapPut(ctx->vars, name, var);
     
@@ -266,6 +286,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 +332,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 +355,36 @@
         }
     }
     
-    ui_setop_enable(TRUE);
+    uic_copy_value_binding(from->type, fromvalue, tovalue);
+}
+
+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 +393,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 +418,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,59 +456,56 @@
     }
 }
 
+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), uic_type2str(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_context_get_document(UiContext *ctx) {
+    return ctx->document;
 }
 
-
-// public API
+void ui_context_single_attachment_mode(UiContext *ctx, UiBool enable) {
+    ctx->single_document_mode = enable;
+}
 
 void ui_attach_document(UiContext *ctx, void *document) {
     uic_context_attach_document(ctx, document);
@@ -478,48 +533,48 @@
 }
 
 
-void ui_set_group(UiContext *ctx, int group) {
-    if(!cxListIndexValid(ctx->groups, cxListFind(ctx->groups, &group))) {
-        cxListAdd(ctx->groups, &group);
+void ui_set_state(UiContext *ctx, int state) {
+    if(!cxListIndexValid(ctx->states, cxListFind(ctx->states, &state))) {
+        cxListAdd(ctx->states, &state);
     }
     
     // enable/disable group widgets
-    uic_check_group_widgets(ctx);
+    uic_check_state_widgets(ctx);
 }
 
-void ui_unset_group(UiContext *ctx, int group) {
-    int i = cxListFind(ctx->groups, &group);
+void ui_unset_state(UiContext *ctx, int state) {
+    int i = cxListFind(ctx->states, &state);
     if(i != -1) {
-        cxListRemove(ctx->groups, i);
+        cxListRemove(ctx->states, i);
     }
     
     // enable/disable group widgets
-    uic_check_group_widgets(ctx);
+    uic_check_state_widgets(ctx);
 }
 
-int* ui_active_groups(UiContext *ctx, int *ngroups) {
-    *ngroups = cxListSize(ctx->groups);
-    return cxListAt(ctx->groups, 0);
+int* ui_active_states(UiContext *ctx, int *nstates) {
+    *nstates = cxListSize(ctx->states);
+    return cxListAt(ctx->states, 0);
 }
 
-void uic_check_group_widgets(UiContext *ctx) {
+void uic_check_state_widgets(UiContext *ctx) {
     int ngroups = 0;
-    int *groups = ui_active_groups(ctx, &ngroups);
+    int *groups = ui_active_states(ctx, &ngroups);
     
-    CxIterator i = cxListIterator(ctx->group_widgets);
-    cx_foreach(UiGroupWidget *, gw, i) {
-        char *check = calloc(1, gw->numgroups);
+    CxIterator i = cxListIterator(ctx->state_widgets);
+    cx_foreach(UiStateWidget *, gw, i) {
+        char *check = calloc(1, gw->numstates);
         
         for(int i=0;i<ngroups;i++) {
-            for(int k=0;k<gw->numgroups;k++) {
-                if(groups[i] == gw->groups[k]) {
+            for(int k=0;k<gw->numstates;k++) {
+                if(groups[i] == gw->states[k]) {
                     check[k] = 1;
                 }
             }
         }
         
         int enable = 1;
-        for(int i=0;i<gw->numgroups;i++) {
+        for(int i=0;i<gw->numstates;i++) {
             if(check[i] == 0) {
                 enable = 0;
                 break;
@@ -530,70 +585,70 @@
     }
 }
 
-void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
+void ui_widget_set_states(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
     if(enable == NULL) {
         enable = (ui_enablefunc)ui_set_enabled;
     }
-    // get groups
-    CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
+    // get states
+    CxList *states = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
     va_list ap;
     va_start(ap, enable);
-    int group;
-    while((group = va_arg(ap, int)) != -1) {
-        cxListAdd(groups, &group);
+    int state;
+    while((state = va_arg(ap, int)) != -1) {
+        cxListAdd(states, &state);
     }
     va_end(ap);
     
-    uic_add_group_widget(ctx, widget, enable, groups);
+    uic_add_state_widget(ctx, widget, enable, states);
     
-    cxListFree(groups);
+    cxListFree(states);
 }
 
-void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups) {
+void ui_widget_set_states2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *states, int nstates) {
     if(enable == NULL) {
         enable = (ui_enablefunc)ui_set_enabled;
     }
-    CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), ngroups);
-    for(int i=0;i<ngroups;i++) {
-        cxListAdd(ls, groups+i);
+    CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), nstates);
+    for(int i=0;i<nstates;i++) {
+        cxListAdd(ls, states+i);
     }
-    uic_add_group_widget(ctx, widget, enable, ls);
+    uic_add_state_widget(ctx, widget, enable, ls);
     cxListFree(ls);
 }
 
 void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates) {
-    ui_widget_set_groups2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
+    ui_widget_set_states2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
 }
 
-size_t uic_group_array_size(const int *groups) {
+size_t uic_state_array_size(const int *states) {
     int i;
-    for(i=0;groups[i] >= 0;i++) { }
+    for(i=0;states[i] >= 0;i++) { }
     return i;
 }
 
-void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups) {
-    uic_add_group_widget_i(ctx, widget, enable, cxListAt(groups, 0), cxListSize(groups));
+void uic_add_state_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *states) {
+    uic_add_state_widget_i(ctx, widget, enable, cxListAt(states, 0), cxListSize(states));
 }
 
-void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups) {
+void uic_add_state_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *states, size_t numstates) {
     const CxAllocator *a = ctx->allocator;
-    UiGroupWidget gw;
+    UiStateWidget gw;
     
     gw.widget = widget;
     gw.enable = enable;
-    gw.numgroups = numgroups;
-    gw.groups = cxCalloc(a, numgroups, sizeof(int));
+    gw.numstates = numstates;
+    gw.states = cxCalloc(a, numstates, sizeof(int));
     
-    // copy groups
-    if(groups) {
-        memcpy(gw.groups, groups, gw.numgroups * sizeof(int));
+    // copy states
+    if(states) {
+        memcpy(gw.states, states, gw.numstates * sizeof(int));
     }
     
-    cxListAdd(ctx->group_widgets, &gw);
+    cxListAdd(ctx->state_widgets, &gw);
 }
 
-void uic_remove_group_widget(UiContext *ctx, void *widget) {
-    (void)cxListFindRemove(ctx->group_widgets, widget);
+void uic_remove_state_widget(UiContext *ctx, void *widget) {
+    (void)cxListFindRemove(ctx->state_widgets, widget);
 }
 
 UIEXPORT void *ui_allocator(UiContext *ctx) {
@@ -638,3 +693,33 @@
 void  ui_set_destructor(void *mem, ui_destructor_func destr) {
     cxMempoolSetDestructor(mem, (cx_destructor_func)destr);
 }
+
+UiInteger* ui_get_int_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_INTEGER);
+    return var ? var->value : NULL;
+}
+
+UiDouble* ui_get_double_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_DOUBLE);
+    return var ? var->value : NULL;
+}
+
+UiString* ui_get_string_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_STRING);
+    return var ? var->value : NULL;
+}
+
+UiText* ui_get_text_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_TEXT);
+    return var ? var->value : NULL;
+}
+
+UiRange* ui_get_range_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_RANGE);
+    return var ? var->value : NULL;
+}
+
+UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_GENERIC);
+    return var ? var->value : NULL;
+}

mercurial