add item list container (GTK) newapi

Mon, 30 Dec 2024 19:48:05 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 30 Dec 2024 19:48:05 +0100
branch
newapi
changeset 422
c1354a29a7e9
parent 421
3b969399f962
child 423
02bafb660eb2

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

mercurial