ui/common/types.c

changeset 115
e57ca2747782
parent 113
dde28a806552
--- a/ui/common/types.c	Sun Dec 07 20:00:33 2025 +0100
+++ b/ui/common/types.c	Sat Dec 13 15:58:58 2025 +0100
@@ -33,13 +33,15 @@
 
 #include <cx/list.h>
 #include <cx/array_list.h>
-#include "../ui/tree.h"
+#include "../ui/list.h"
 #include "types.h"
 #include "context.h"
 #include "../ui/image.h"
 
 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) {
@@ -201,6 +212,7 @@
 
 UiModel* ui_model(UiContext *ctx, ...) {
     UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+    info->ctx = ctx;
     
     va_list ap;
     va_start(ap, ctx);
@@ -240,8 +252,18 @@
 
 #define UI_MODEL_DEFAULT_ALLOC_SIZE 16
 
+
+static void model_notify_observer(UiModel *model, int insert, int delete) {
+    UiModelChangeObserver *obs = model->observer;
+    while(obs) {
+        obs->update(model, obs->userdata, insert, delete);
+        obs = obs->next;
+    }
+}
+
 UiModel* ui_model_new(UiContext *ctx) {
     UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+    info->ctx = ctx;
     info->alloc = UI_MODEL_DEFAULT_ALLOC_SIZE;
     info->types = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(UiModelType));
     info->titles = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(char*));
@@ -249,16 +271,21 @@
     return info;
 }
 
-void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width) {
+void ui_model_add_column(UiModel *model, UiModelType type, const char *title, int width) {
+    UiContext *ctx = model->ctx;
     if(model->columns >= model->alloc) {
         model->alloc += UI_MODEL_DEFAULT_ALLOC_SIZE;
         model->types = ui_realloc(ctx, model->types, model->alloc * sizeof(UiModelType));
         model->titles = ui_realloc(ctx, model->titles, model->alloc * sizeof(char*));
         model->columnsize = ui_realloc(ctx, model->columnsize, model->alloc * sizeof(int));
     }
-    model->types[model->columns] = type;
-    model->titles[model->columns] = ui_strdup(ctx, title);
-    model->columnsize[model->columns] = width;
+    int index = model->columns;
+    model->types[index] = type;
+    model->titles[index] = ui_strdup(ctx, title);
+    model->columnsize[index] = width;
+    
+    model_notify_observer(model, index, -1);
+    
     model->columns++;
 }
 
@@ -266,6 +293,7 @@
     const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
 
     UiModel* newmodel = cxMalloc(a, sizeof(UiModel));
+    newmodel->ctx = ctx;
     *newmodel = *model;
 
     newmodel->types = cxCalloc(a, model->columns, sizeof(UiModelType));
@@ -281,11 +309,67 @@
     return newmodel;
 }
 
-void ui_model_free(UiContext *ctx, UiModel *mi) {
-    const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
+void ui_model_ref(UiModel *model) {
+    model->ref++;
+}
+
+void ui_model_unref(UiModel *model) { 
+    if(--model->ref == 0) {
+        ui_model_free(model);
+    }
+}
+
+void ui_model_add_observer(UiModel *model, ui_model_update_func update, void *data) {
+    UiModelChangeObserver *observer = ui_malloc(model->ctx, sizeof(UiModelChangeObserver));
+    observer->update = update;
+    observer->userdata = data;
+    observer->next = NULL;
+    
+    if(model->observer) {
+        UiModelChangeObserver *last = model->observer;
+        while(last->next) {
+            last = last->next;
+        }
+        last->next = observer;
+    } else {
+        model->observer = observer;
+    }
+}
+
+void ui_model_remove_observer(UiModel *model, void *data) {
+    if(model->observer) {
+        UiModelChangeObserver *obs = model->observer;
+        UiModelChangeObserver *prev = NULL;
+        while(obs) {
+            if(obs->userdata == data) {
+                // remove
+                if(prev) {
+                    prev->next = obs->next;
+                } else {
+                    model->observer = obs->next;
+                }
+                // free
+                ui_free(model->ctx, obs);
+                break;
+            }
+            prev = obs;
+            obs = obs->next;
+        }
+    }
+}
+
+void ui_model_free(UiModel *mi) {
+    UiContext *ctx = mi->ctx;
+    const CxAllocator* a = ctx->allocator;
     for(int i=0;i<mi->columns;i++) {
         ui_free(ctx, mi->titles[i]);
     }
+    UiModelChangeObserver *obs = mi->observer;
+    while(obs) {
+        UiModelChangeObserver *n = obs->next;
+        cxFree(a, obs);
+        obs = n;
+    }
     cxFree(a, mi->types);
     cxFree(a, mi->titles);
     cxFree(a, mi->columnsize);
@@ -724,9 +808,7 @@
 }
 
 UIEXPORT void ui_listselection_free(UiListSelection selection) {
-    if (selection.rows) {
-        free(selection.rows);
-    }
+    free(selection.rows);
 }
 
 UIEXPORT UiStr ui_str(char *cstr) {
@@ -787,6 +869,7 @@
 }
 
 static int ui_set_op = 0;
+static int ui_onchange_events_enabled = TRUE;
 
 void ui_setop_enable(int set) {
     ui_set_op = set;
@@ -796,6 +879,14 @@
     return ui_set_op;
 }
 
+void ui_onchange_events_enable(UiBool enable) {
+    ui_onchange_events_enabled = enable;
+}
+
+UiBool ui_onchange_events_is_enabled(void) {
+    return ui_onchange_events_enabled;
+}
+
 /* ---------------- List initializers and wrapper functions ---------------- */
 
 void ui_global_list_initializer(ui_list_init_func func, void *userdata) {
@@ -803,6 +894,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;
 }

mercurial