--- 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; }