# HG changeset patch # User Olaf Wintermann # Date 1607187022 -3600 # Node ID b70e2a77dea030726c97c83ed93900e1974ab66c # Parent 18892c0a9adc58ede295044dbd0300205f9f540e refactore document system to support document trees diff -r 18892c0a9adc -r b70e2a77dea0 application/main.c --- a/application/main.c Sat Dec 05 11:54:58 2020 +0100 +++ b/application/main.c Sat Dec 05 17:50:22 2020 +0100 @@ -33,14 +33,57 @@ #include #include +typedef struct { + UiText *text; +} MyDocument; + +MyDocument *doc1; +MyDocument *doc2; + + void action_menu(UiEvent *event, void *userdata) { } +void action_button(UiEvent *event, void *userdata) { + printf("button test\n"); + MyDocument *doc = event->document; + if(!doc) { + printf("no document\n"); + return; + } + + char *text = doc->text->get(doc->text); + printf("text: {\n%s\n}\n", text); +} + +void action_switch(UiEvent *event, void *userdata) { + if(event->document == doc1) { + ui_set_document(event->obj, doc2); + } else { + ui_set_document(event->obj, doc1); + } +} + + +MyDocument* create_doc(void) { + MyDocument *doc = ui_document_new(sizeof(MyDocument)); + UiContext *docctx = ui_document_context(doc); + doc->text = ui_text_new(docctx, "text"); + return doc; +} + void application_startup(UiEvent *event, void *data) { UiObject *obj = ui_window("Test", NULL); + ui_textarea_nv(obj, "text"); + ui_button(obj, "Test", action_button, NULL); + ui_button(obj, "Switch Document", action_switch, NULL); + doc1 = create_doc(); + doc2 = create_doc(); + + ui_attach_document(obj->ctx, doc1); ui_show(obj); } diff -r 18892c0a9adc -r b70e2a77dea0 ui/common/context.c --- a/ui/common/context.c Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/common/context.c Sat Dec 05 17:50:22 2020 +0100 @@ -44,8 +44,8 @@ ctx->obj = toplevel; ctx->vars = ucx_map_new_a(mp->allocator, 16); - ctx->set_document = uic_context_set_document; - ctx->detach_document = uic_context_detach_document; + ctx->attach_document = uic_context_attach_document; + ctx->detach_document2 = uic_context_detach_document2; #ifdef UI_GTK if(toplevel->widget) { @@ -61,139 +61,126 @@ return ctx->parent ? uic_root_context(ctx->parent) : ctx; } -void uic_context_set_document(UiContext *ctx, void *document) { - if(ctx->document == document) { - return; - } - if(ctx->document) { - uic_context_detach_document(ctx); - } - ctx->document = document; +void uic_context_attach_document(UiContext *ctx, void *document) { + ctx->documents = ucx_list_append_a(ctx->mempool->allocator, ctx->documents, document); + ctx->document = ctx->documents->data; + + UiContext *doc_ctx = ui_document_context(document); - UiContext *docctx = ui_document_context(document); - if(!docctx) { - fprintf(stderr, "UiError: uic_context_set_document: pointer is not a document\n"); - return; + // check if any parent context has an unbound variable with the same name + // as any document variable + UiContext *var_ctx = ctx; + while(var_ctx) { + if(var_ctx->vars_unbound && var_ctx->vars_unbound->count > 0) { + UcxMapIterator i = ucx_map_iterator(var_ctx->vars_unbound); + UiVar *var; + // rmkeys holds all keys, that shall be removed from vars_unbound + UcxKey *rmkeys = calloc(var_ctx->vars_unbound->count, sizeof(UcxKey)); + size_t numkeys = 0; + UCX_MAP_FOREACH(key, var, i) { + UiVar *docvar = ucx_map_get(doc_ctx->vars, key); + if(docvar) { + // bind var to document var + uic_copy_binding(var, docvar, TRUE); + rmkeys[numkeys++] = key; // save the key for removal + } + } + // now that we may have bound some vars to the document, + // we can remove them from the unbound map + for(size_t k=0;kvars_unbound, rmkeys[k]); + } + } + + var_ctx = ctx->parent; } - docctx->obj = ctx->obj; - docctx->parent = ctx; - - UiContext *root = uic_root_context(ctx); - if(!root->bound || root->bound->count == 0) { - return; +} + +static void uic_context_unbind_vars(UiContext *ctx) { + UcxMapIterator i = ucx_map_iterator(ctx->vars); + UiVar *var; + UCX_MAP_FOREACH(key, var, i) { + if(var->from && var->from_ctx) { + uic_save_var2(var); + uic_copy_binding(var, var->from, FALSE); + ucx_map_put(var->from_ctx->vars_unbound, key, var->from); + var->from_ctx = ctx; + } } - // there are bound variables in the root ctx - // copy bindings with correct name to doc vars - UcxMapIterator i = ucx_map_iterator(docctx->vars); - UiVar *var; - UCX_MAP_FOREACH(key, var, i) { - UiVar *v = ucx_map_get(root->bound, key); - if(v) { - // copy binding: after this, all doc vars with names of previously - // bound variables are bound to the widgets - // the widgets still hold a pointer to the root ctx vars, but this - // vars have a pointer to the document variable value - confusing - uic_copy_binding(v, var, TRUE); - } + UCX_FOREACH(elm, ctx->documents) { + UiContext *subctx = ui_document_context(elm->data); + uic_context_unbind_vars(subctx); } } -void uic_context_detach_document(UiContext *ctx) { - if(!ctx->document) { - return; +void uic_context_detach_document2(UiContext *ctx, void *document) { + // find the document in the documents list + UcxList *doc = NULL; + UCX_FOREACH(elm, ctx->documents) { + if(elm->data == document) { + doc = elm; + break; + } + } + if(!doc) { + return; // document is not a subdocument of this context } - UiContext *docctx = ui_document_context(ctx->document); - if(!docctx) { - fprintf(stderr, "UiError: Cannot detach document: no context\n"); - return; - } - ctx->document = NULL; - docctx->parent = NULL; - docctx->obj = NULL; + ctx->documents = ucx_list_remove_a(ctx->mempool->allocator, ctx->documents, doc); + ctx->document = ctx->documents ? ctx->documents->data : NULL; - // unbind all vars - UcxMapIterator i = ucx_map_iterator(docctx->vars); - UiVar *var; - UCX_MAP_FOREACH(key, var, i) { - uic_save_var(var); - if(var->from) { - // restore old root bound var val - var->from->value = var->from->orig_val; - var->from->orig_val = NULL; - // copy - uic_copy_binding(var, var->from, FALSE); + UiContext *docctx = ui_document_context(document); + uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent +} + +void uic_context_detach_all(UiContext *ctx) { + UcxList *ls = ucx_list_clone(ctx->documents, NULL, NULL); + UCX_FOREACH(elm, ls) { + ctx->detach_document2(ctx, elm->data); + } + ucx_list_free(ls); +} + +static UiVar* ctx_getvar(UiContext *ctx, UcxKey key) { + UiVar *var = ucx_map_get(ctx->vars, key); + if(!var) { + UCX_FOREACH(elm, ctx->documents) { + UiContext *subctx = ui_document_context(elm->data); + var = ctx_getvar(subctx, key); + if(var) { + break; + } } - uic_unbind_var(var); } + return var; } UiVar* uic_get_var(UiContext *ctx, char *name) { - // check document variables first - UiVar *var = NULL; - UiContext *doc_ctx = ui_document_context(ctx->document); - if(doc_ctx) { - var = uic_get_var(doc_ctx, name); - } - - // check variables of this context - if(!var) { - var = ucx_map_cstr_get(ctx->vars, name); - } - - return var; + UcxKey key = ucx_key(name, strlen(name)); + return ctx_getvar(ctx, key); } UiVar* uic_create_var(UiContext *ctx, char *name, UiVarType type) { - // check if this context has a var with this name - // otherweise add it to the bound map - UiVar *cv = ucx_map_cstr_get(ctx->vars, name); - if(cv) { - if(cv->type == type) { - return cv; + UiVar *var = uic_get_var(ctx, name); + if(var) { + if(var->type == type) { + return var; } else { - fprintf(stderr, "UiError: var '%s' already exists with different type\n", name); - } - } - - UiVar *var; - if(ctx->bound) { - var = ucx_map_cstr_get(ctx->bound, name); - if(var) { - if(var->type != type) { - fprintf(stderr, "UiError: var '%s' already bound with different type\n", name); - } else { - return var; - } + fprintf(stderr, "UiError: var '%s' already bound with different type\n", name); } } - // create var and add it to the bound map - // this map contains vars that are created by widgets - // the vars can later be moved to subdocuments var = ui_malloc(ctx, sizeof(UiVar)); var->type = type; var->value = uic_create_value(ctx, type); - var->orig_val = NULL; - var->from = NULL; // not connected to a doc var - - if(!ctx->bound) { - ctx->bound = ucx_map_new_a(ctx->mempool->allocator, 16); + var->from = NULL; + var->from_ctx = ctx; + + if(!ctx->vars_unbound) { + ctx->vars_unbound = ucx_map_new_a(ctx->mempool->allocator, 16); } - ucx_map_cstr_put(ctx->bound, name, var); - - // if a subdocument has a variable with this name, we must copy - // the binding to the doc var - UiContext *doc_ctx = ui_document_context(ctx->document); - if(doc_ctx) { - UiVar *docvar = uic_get_var(doc_ctx, name); - if(docvar) { - var->orig_val = var->value; - var->value = docvar->value; - docvar->from = var; - } - } + ucx_map_cstr_put(ctx->vars_unbound, name, var); return var; } @@ -230,11 +217,6 @@ } void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { - if(copytodoc && from->from) { - fprintf(stderr, "UiError: var already connected to a document\n"); - return; - } - // check type if(from->type != to->type) { fprintf(stderr, "UI Error: var has incompatible type.\n"); @@ -245,9 +227,7 @@ // update var if(copytodoc) { to->from = from; - - from->orig_val = from->value; - from->value = to->value; + to->from_ctx = from->from_ctx; } // copy binding @@ -311,8 +291,9 @@ } } -void uic_save_var(UiVar *var) { +void uic_save_var2(UiVar *var) { switch(var->type) { + case UI_VAR_SPECIAL: break; case UI_VAR_INTEGER: uic_int_save(var->value); break; case UI_VAR_DOUBLE: uic_double_save(var->value); break; case UI_VAR_STRING: uic_string_save(var->value); break; @@ -334,8 +315,14 @@ } void uic_reg_var(UiContext *ctx, char *name, UiVarType type, void *value) { - UiContext *rootctx = uic_root_context(ctx); - + // 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 @@ -350,33 +337,46 @@ } } } + */ // create new var and add it to doc's vars UiVar *var = ui_malloc(ctx, sizeof(UiVar)); - var->from = NULL; - var->orig_val = NULL; var->type = type; var->value = value; + var->from = NULL; + var->from_ctx = ctx; size_t oldcount = ctx->vars->count; ucx_map_cstr_put(ctx->vars, name, var); if(ctx->vars->count != 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); } + */ } void uic_remove_bound_var(UiContext *ctx, UiVar *var) { // TODO: implement + printf("TODO: implement uic_remove_bound_var\n"); } // public API +void ui_attach_document(UiContext *ctx, void *document) { + uic_context_attach_document(ctx, document); +} + +void ui_detach_document2(UiContext *ctx, void *document) { + uic_context_detach_document2(ctx, document); +} + void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) { ctx->close_callback = fnc; ctx->close_data = udata; diff -r 18892c0a9adc -r b70e2a77dea0 ui/common/context.h --- a/ui/common/context.h Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/common/context.h Sat Dec 05 17:50:22 2020 +0100 @@ -60,18 +60,17 @@ UiObject *obj; UcxMempool *mempool; - UcxMap *bound; // key: char* value: UiVar* deprecated - UcxMap *vars; // key: char* value: UiVar* deprecated - void *document; // deprecated + void *document; + UcxList *documents; - UcxMap *vars2; // manually created context vars + UcxMap *vars; // manually created context vars UcxMap *vars_unbound; // unbound vars created by widgets UcxList *groups; // int list UcxList *group_widgets; // UiGroupWidget* list - void (*set_document)(UiContext *ctx, void *document); // deprecated - void (*detach_document)(UiContext *ctx); // deprecated + void (*attach_document)(UiContext *ctx, void *document); + void (*detach_document2)(UiContext *ctx, void *document); char *title; @@ -83,19 +82,12 @@ void *close_data; }; -// deprecated +// UiVar replacement, rename it to UiVar when finished struct UiVar { void *value; - void *orig_val; UiVarType type; - UiVar *from; -}; - -// UiVar replacement, rename it to UiVar when finished -struct UiVar2 { - void *value; - UiVarType type; - UiContext *bound_from; /* bound by this context or NULL if unbound */ + UiVar *from; + UiContext *from_ctx; }; struct UiGroupWidget { @@ -110,12 +102,16 @@ void uic_context_set_document(UiContext *ctx, void *document); // deprecated void uic_context_detach_document(UiContext *ctx); // deprecated -//UiVar* uic_get_var(UiContext *ctx, char *name); +void uic_context_attach_document(UiContext *ctx, void *document); +void uic_context_detach_document2(UiContext *ctx, void *document); +void uic_context_detach_all(UiContext *ctx); + +UiVar* uic_get_var(UiContext *ctx, char *name); UiVar* uic_create_var(UiContext *ctx, char *name, UiVarType type); void* uic_create_value(UiContext *ctx, UiVarType type); void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc); -void uic_save_var(UiVar *var); +void uic_save_var2(UiVar *var); void uic_unbind_var(UiVar *var); void uic_reg_var(UiContext *ctx, char *name, UiVarType type, void *value); diff -r 18892c0a9adc -r b70e2a77dea0 ui/common/document.c --- a/ui/common/document.c Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/common/document.c Sat Dec 05 17:50:22 2020 +0100 @@ -39,11 +39,12 @@ } void ui_set_document(UiObject *obj, void *document) { - obj->ctx->set_document(obj->ctx, document); + uic_context_detach_all(obj->ctx); + obj->ctx->attach_document(obj->ctx, document); } void ui_detach_document(UiObject *obj) { - obj->ctx->detach_document(obj->ctx); + uic_context_detach_all(obj->ctx); } void* ui_get_document(UiObject *obj) { @@ -55,7 +56,7 @@ if(!ctx) { fprintf(stderr, "UI Error: pointer is not a document\n"); } - uic_context_set_document(ctx, sub); + // TODO } void ui_detach_subdocument(void *document, void *sub) { @@ -63,7 +64,7 @@ if(!ctx) { fprintf(stderr, "UI Error: pointer is not a document\n"); } - uic_context_detach_document(ctx); + // TODO } void* ui_get_subdocument(void *document) { @@ -71,14 +72,15 @@ if(!ctx) { fprintf(stderr, "UI Error: pointer is not a document\n"); } - return ctx->document; + // TODO + return NULL; } void* ui_document_new(size_t size) { UcxMempool *mp = ucx_mempool_new(256); UiContext *ctx = ucx_mempool_calloc(mp, 1, sizeof(UiContext)); - ctx->set_document = uic_context_set_document; - ctx->detach_document = uic_context_detach_document; + ctx->attach_document = uic_context_attach_document; + ctx->detach_document2 = uic_context_detach_document2; ctx->mempool = mp; ctx->vars = ucx_map_new_a(mp->allocator, 16); diff -r 18892c0a9adc -r b70e2a77dea0 ui/gtk/container.c --- a/ui/gtk/container.c Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/gtk/container.c Sat Dec 05 17:50:22 2020 +0100 @@ -426,7 +426,8 @@ //printf("page_change: %d\n", page_num); UiContext *ctx = tab->ctx; - ctx->parent->set_document(ctx->parent, ctx->document); + uic_context_detach_all(ctx->parent); // TODO: fix? + ctx->parent->attach_document(ctx->parent, ctx->document); } UiTabbedPane* ui_tabbed_document_view(UiObject *obj) { @@ -460,8 +461,8 @@ tab->widget = NULL; // initialization for uic_context() tab->ctx = uic_context(tab, view->ctx->mempool); tab->ctx->parent = view->ctx; - tab->ctx->set_document = ui_tab_set_document; - tab->ctx->detach_document = ui_tab_detach_document; + tab->ctx->attach_document = uic_context_attach_document; + tab->ctx->detach_document2 = uic_context_detach_document2; tab->widget = frame; tab->window = view->ctx->obj->window; tab->container = ui_frame_container(tab, frame); @@ -474,16 +475,18 @@ } void ui_tab_set_document(UiContext *ctx, void *document) { + // TODO: remove? if(ctx->parent->document) { //ctx->parent->detach_document(ctx->parent, ctx->parent->document); } - uic_context_set_document(ctx, document); + //uic_context_set_document(ctx, document); //uic_context_set_document(ctx->parent, document); //ctx->parent->document = document; } void ui_tab_detach_document(UiContext *ctx) { - uic_context_detach_document(ctx->parent); + // TODO: remove? + //uic_context_detach_document(ctx->parent); } diff -r 18892c0a9adc -r b70e2a77dea0 ui/gtk/model.h --- a/ui/gtk/model.h Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/gtk/model.h Sat Dec 05 17:50:22 2020 +0100 @@ -46,7 +46,7 @@ GObject object; UiObject *obj; UiModel *model; - UiVar *var; + UiVar *var; GType *columntypes; int numcolumns; int stamp; diff -r 18892c0a9adc -r b70e2a77dea0 ui/gtk/text.c --- a/ui/gtk/text.c Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/gtk/text.c Sat Dec 05 17:50:22 2020 +0100 @@ -155,12 +155,11 @@ } UIWIDGET ui_textarea(UiObject *obj, UiText *value) { - UiVar *var = NULL; - if(value) { - var = malloc(sizeof(UiVar)); - var->value = value; - var->type = UI_VAR_SPECIAL; - } + UiVar *var = malloc(sizeof(UiVar)); + var->value = value; + var->type = UI_VAR_SPECIAL; + var->from = NULL; + var->from_ctx = NULL; return ui_textarea_var(obj, var); } diff -r 18892c0a9adc -r b70e2a77dea0 ui/gtk/text.h --- a/ui/gtk/text.h Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/gtk/text.h Sat Dec 05 17:50:22 2020 +0100 @@ -57,13 +57,13 @@ typedef struct UiTextArea { UiContext *ctx; - UiVar *var; + UiVar *var; int last_selection_state; } UiTextArea; typedef struct UiTextField { UiContext *ctx; - UiVar *var; + UiVar *var; // TODO: validatefunc } UiTextField; diff -r 18892c0a9adc -r b70e2a77dea0 ui/gtk/toolbar.c --- a/ui/gtk/toolbar.c Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/gtk/toolbar.c Sat Dec 05 17:50:22 2020 +0100 @@ -168,6 +168,8 @@ UiVar *var = malloc(sizeof(UiVar)); var->value = list; var->type = UI_VAR_SPECIAL; + var->from = NULL; + var->from_ctx = NULL; cb->var = var; cb->getvalue = getvalue; cb->callback = f; diff -r 18892c0a9adc -r b70e2a77dea0 ui/gtk/toolkit.c --- a/ui/gtk/toolkit.c Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/gtk/toolkit.c Sat Dec 05 17:50:22 2020 +0100 @@ -182,6 +182,7 @@ if(!result) { g_idle_add(ui_job_finished, job); } + return NULL; } void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { diff -r 18892c0a9adc -r b70e2a77dea0 ui/gtk/toolkit.h --- a/ui/gtk/toolkit.h Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/gtk/toolkit.h Sat Dec 05 17:50:22 2020 +0100 @@ -46,7 +46,7 @@ typedef struct UiVarEventData { UiObject *obj; - UiVar *var; + UiVar *var; UiObserver **observers; } UiVarEventData; diff -r 18892c0a9adc -r b70e2a77dea0 ui/gtk/tree.c --- a/ui/gtk/tree.c Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/gtk/tree.c Sat Dec 05 17:50:22 2020 +0100 @@ -493,6 +493,7 @@ GtkWidget *combobox = ui_create_combobox(obj, listmodel, f, udata); UiContainer *ct = uic_get_current_container(obj); ct->add(ct, combobox, FALSE); + return combobox; } GtkWidget* ui_create_combobox(UiObject *obj, UiListModel *model, ui_callback f, void *udata) { diff -r 18892c0a9adc -r b70e2a77dea0 ui/ui/toolkit.h --- a/ui/ui/toolkit.h Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/ui/toolkit.h Sat Dec 05 17:50:22 2020 +0100 @@ -300,16 +300,17 @@ void* ui_document_new(size_t size); void ui_document_destroy(void *doc); -void ui_set_document(UiObject *obj, void *document); -void ui_detach_document(UiObject *obj); -void* ui_get_document(UiObject *obj); -void ui_set_subdocument(void *document, void *sub); -void ui_detach_subdocument(void *document, void *sub); -void* ui_get_subdocument(void *document); +void ui_set_document(UiObject *obj, void *document); // deprecated +void ui_detach_document(UiObject *obj); // deprecated +void* ui_get_document(UiObject *obj); // deprecated +void ui_set_subdocument(void *document, void *sub); // deprecated +void ui_detach_subdocument(void *document, void *sub); // deprecated +void* ui_get_subdocument(void *document); // deprecated UiContext* ui_document_context(void *doc); - +void ui_attach_document(UiContext *ctx, void *document); +void ui_detach_document2(UiContext *ctx, void *document); void ui_set_group(UiContext *ctx, int group); void ui_unset_group(UiContext *ctx, int group);