Mon, 30 Dec 2024 19:48:05 +0100
add item list container (GTK)
application/main.c | file | annotate | diff | comparison | revisions | |
ui/gtk/container.c | file | annotate | diff | comparison | revisions | |
ui/gtk/container.h | file | annotate | diff | comparison | revisions | |
ui/gtk/toolkit.h | file | annotate | diff | comparison | revisions | |
ui/ui/container.h | file | annotate | diff | comparison | revisions |
--- a/application/main.c Mon Dec 30 12:50:52 2024 +0100 +++ b/application/main.c Mon Dec 30 19:48:05 2024 +0100 @@ -50,6 +50,7 @@ UiGeneric *image; UiList *srclist1; UiList *srclist2; + UiList *items; } MyDocument; MyDocument *doc1; @@ -179,7 +180,10 @@ ui_list_append(doc->srclist2, "x1"); ui_list_append(doc->srclist2, "x2"); - + doc->items = ui_list_new(docctx, "items"); + ui_list_append(doc->items, "Item 1"); + ui_list_append(doc->items, "Item 2"); + ui_list_append(doc->items, "Item 3"); //doc->text = ui_text_new(docctx, "text"); return doc; @@ -280,6 +284,44 @@ item->eventdata = sublistdata; } +typedef struct Item { + UiObject *obj; + MyDocument *doc; + void *elm; +} Item; + +void item_remove(UiEvent *event, void *userdata) { + Item *item = userdata; + int index = 0; + void *elm = ui_list_first(item->doc->items); + while(elm) { + if(elm == item->elm) { + break; + } + elm = ui_list_next(item->doc->items); + index++; + } + + ui_list_remove(item->doc->items, index); + ui_list_update(item->doc->items); +} + +void create_item(UiObject *obj, int index, void *elm, void *userdata) { + Item *i = ui_malloc(obj->ctx, sizeof(Item)); + i->obj = obj; + i->elm = elm; + i->doc = userdata; + + char *item = elm; + ui_button(obj, .label = item); + ui_checkbox(obj, .label = "Check"); + ui_label(obj, .fill = UI_ON); + ui_button(obj, .label = "X", .onclick = item_remove, .onclickdata = i); + + + +} + void application_startup(UiEvent *event, void *data) { // global list UiContext *global = ui_global_context(); @@ -404,6 +446,10 @@ } } } + + ui_tab(obj, "Tab 7") { + ui_itemlist(obj, .create_ui = create_item, .varname = "items", .subcontainer = UI_CONTAINER_HBOX, .sub_spacing = 10, .margin = 10, .spacing = 4, .userdata = doc); + } } /*
--- a/ui/gtk/container.c Mon Dec 30 12:50:52 2024 +0100 +++ b/ui/gtk/container.c Mon Dec 30 19:48:05 2024 +0100 @@ -946,6 +946,126 @@ +/* -------------------- ItemList Container -------------------- */ + +static void remove_item(void *data, void *item) { + UiGtkItemListContainer *ct = data; + UiObject *obj = item; + if(ct->remove_items) { + BOX_REMOVE(ct->widget, obj->widget); + } + uic_object_destroy(obj); +} + +static void update_itemlist(UiList *list, int c) { + UiGtkItemListContainer *ct = list->obj; + + CxMap *new_items = cxHashMapCreateSimple(CX_STORE_POINTERS); + new_items->collection.advanced_destructor = remove_item; + new_items->collection.destructor_data = ct; + + // only create new widgets for new elements, so at first we have + // to find which elements are new + // check which elements in the list are already in the container + void *elm = list->first(list); + int j = 0; + while(elm) { + CxHashKey key = cx_hash_key(&elm, sizeof(void*)); + UiObject *item_obj = cxMapRemoveAndGet(ct->current_items, key); + if(item_obj) { + g_object_ref(G_OBJECT(item_obj->widget)); + BOX_REMOVE(ct->widget, item_obj->widget); + cxMapPut(new_items, key, item_obj); + } + elm = list->next(list); + j++; + } + + // ct->current_items only contains elements, that are not in the list + cxMapDestroy(ct->current_items); // calls destructor remove_item + ct->current_items = new_items; + + // add all items + int index = 0; + elm = list->first(list); + while(elm) { + CxHashKey key = cx_hash_key(&elm, sizeof(void*)); + UiObject *item_obj = cxMapGet(ct->current_items, key); + if(item_obj) { + // re-add previously created widget + ui_box_container_add(ct->container, item_obj->widget, FALSE); + } else { + // create new widget and object for this list element + CxMempool *mp = cxBasicMempoolCreate(256); + const CxAllocator *a = mp->allocator; + UiObject *obj = cxCalloc(a, 1, sizeof(UiObject)); + obj->ctx = uic_context(obj, mp); + obj->window = NULL; + obj->widget = ui_subcontainer_create( + ct->subcontainer, + obj, + ct->spacing, + ct->columnspacing, + ct->rowspacing, + ct->margin); + ui_box_container_add(ct->container, obj->widget, FALSE); + if(ct->create_ui) { + ct->create_ui(obj, index, elm, ct->userdata); + } + cxMapPut(new_items, key, obj); + } + elm = list->next(list); + index++; + } +} + +static void destroy_itemlist_container(GtkWidget *w, UiGtkItemListContainer *container) { + container->remove_items = FALSE; + cxMapDestroy(container->current_items); + free(container); +} + +UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs args) { + UiObject *current = uic_current_obj(obj); + UiContainer *ct = current->container; + UI_APPLY_LAYOUT1(current, args); + + GtkWidget *box = args.container == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args.spacing) : ui_gtk_hbox_new(args.spacing); + ui_set_name_and_style(box, args.name, args.style_class); + GtkWidget *widget = args.margin > 0 ? ui_box_set_margin(box, args.margin) : box; + ct->add(ct, widget, TRUE); + + UiGtkItemListContainer *container = malloc(sizeof(UiGtkItemListContainer)); + container->parent = obj; + container->widget = box; + container->container = ui_box_container(current, box, args.container); + container->create_ui = args.create_ui; + container->userdata = args.userdata; + container->subcontainer = args.subcontainer; + container->current_items = cxHashMapCreateSimple(CX_STORE_POINTERS); + container->current_items->collection.advanced_destructor = remove_item; + container->current_items->collection.destructor_data = container; + container->margin = args.sub_margin; + container->spacing = args.sub_spacing; + container->columnspacing = args.sub_columnspacing; + container->rowspacing = args.sub_rowspacing; + container->remove_items = TRUE; + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_LIST); + if(var) { + UiList *list = var->value; + list->obj = container; + list->update = update_itemlist; + update_itemlist(list, 0); + } + g_signal_connect( + box, + "destroy", + G_CALLBACK(destroy_itemlist_container), + container); + + return box; +}
--- a/ui/gtk/container.h Mon Dec 30 12:50:52 2024 +0100 +++ b/ui/gtk/container.h Mon Dec 30 19:48:05 2024 +0100 @@ -34,6 +34,7 @@ #include <string.h> #include <cx/allocator.h> +#include <cx/hash_map.h> #ifdef __cplusplus extern "C" { @@ -133,6 +134,21 @@ UiHeaderbarAlternative alternative; /* only used by fallback headerbar */ } UiHeaderbarContainer; +typedef struct UiGtkItemListContainer { + UiObject *parent; + GtkWidget *widget; + UiContainer *container; + void (*create_ui)(UiObject *, int, void *, void *); + void *userdata; + UiSubContainerType subcontainer; + CxMap *current_items; + int margin; + int spacing; + int columnspacing; + int rowspacing; + bool remove_items; +} UiGtkItemListContainer; + GtkWidget* ui_gtk_vbox_new(int spacing); GtkWidget* ui_gtk_hbox_new(int spacing);
--- a/ui/gtk/toolkit.h Mon Dec 30 12:50:52 2024 +0100 +++ b/ui/gtk/toolkit.h Mon Dec 30 19:48:05 2024 +0100 @@ -61,6 +61,7 @@ #define BOX_ADD(box, child) gtk_box_append(GTK_BOX(box), child) #define BOX_ADD_EXPAND(box, child) gtk_widget_set_hexpand(child, TRUE); gtk_widget_set_vexpand(child, TRUE); gtk_box_append(GTK_BOX(box), child) #define BOX_ADD_NO_EXPAND(box, child) gtk_box_append(GTK_BOX(box), child) +#define BOX_REMOVE(box, child) gtk_box_remove(GTK_BOX(box), child) #define ENTRY_SET_TEXT(entry, text) gtk_editable_set_text(GTK_EDITABLE(entry), text) #define ENTRY_GET_TEXT(entry) gtk_editable_get_text(GTK_EDITABLE(entry)) #define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new() @@ -80,6 +81,7 @@ #define BOX_ADD(box, child) gtk_container_add(GTK_CONTAINER(box), child) #define BOX_ADD_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, TRUE, 0) #define BOX_ADD_NO_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, FALSE, 0) +#define BOX_REMOVE(box, child) gtk_container_remove(GTK_CONTAINER(box), child) #define ENTRY_SET_TEXT(entry, text) gtk_entry_set_text(GTK_ENTRY(entry), text) #define ENTRY_GET_TEXT(entry) gtk_entry_get_text(GTK_ENTRY(entry)) #define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new(NULL, NULL) @@ -158,9 +160,6 @@ }; #endif -typedef enum UiOrientation UiOrientation; -enum UiOrientation { UI_HORIZONTAL = 0, UI_VERTICAL }; - #ifdef UI_APPLICATION void ui_app_quit(); GtkApplication* ui_get_application();
--- a/ui/ui/container.h Mon Dec 30 12:50:52 2024 +0100 +++ b/ui/ui/container.h Mon Dec 30 19:48:05 2024 +0100 @@ -42,6 +42,11 @@ UI_CONTAINER_NO_SUB } UiSubContainerType; +typedef enum UiOrientation { + UI_VERTICAL = 0, + UI_HORIZONTAL +} UiOrientation; + typedef enum UiTabViewType { UI_TABVIEW_DEFAULT = 0, UI_TABVIEW_DOC, @@ -148,6 +153,49 @@ int spacing; } UiSidebarArgs; +typedef struct UiItemListContainerArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + int margin; + int spacing; + + int sub_margin; + int sub_spacing; + int sub_columnspacing; + int sub_rowspacing; + + UiList *value; + const char *varname; + /* + * void create_ui(UiObject *obj, int index, void *elm, void *userdata) + * + * UI constructor for each list element + * + * This callback is executed for each list element. A new UiObject is + * created for every item. + */ + void (*create_ui)(UiObject *, int, void *, void *); + void *userdata; + + /* + * ItemList container type + * Only UI_CONTAINER_VBOX or UI_CONTAINER_HBOX are supported + */ + UiSubContainerType container; + + /* + * item root container + */ + UiSubContainerType subcontainer; +} UiItemListContainerArgs; struct UiContainerX { void *container; @@ -185,6 +233,8 @@ #define ui_headerbar_center(obj) for(ui_headerbar_center_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_headerbar_end(obj) for(ui_headerbar_end_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_itemlist(obj, ...) ui_itemlist_create(obj, (UiItemListContainerArgs) { __VA_ARGS__} ) + UIEXPORT void ui_end(UiObject *obj); // deprecated UIEXPORT void ui_end_new(UiObject *obj); // TODO: rename to ui_end @@ -208,6 +258,7 @@ UIEXPORT UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args); +UIEXPORT UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs args); UIEXPORT UIWIDGET ui_hsplitpane(UiObject *obj, int max); // TODO UIEXPORT UIWIDGET ui_vsplitpane(UiObject *obj, int max); // TODO