ui/gtk/container.c

changeset 431
bb7da585debc
parent 422
c1354a29a7e9
child 440
7c4b9cba09ca
--- a/ui/gtk/container.c	Sun May 23 09:44:43 2021 +0200
+++ b/ui/gtk/container.c	Sat Jan 04 16:38:48 2025 +0100
@@ -32,6 +32,7 @@
 
 #include "container.h"
 #include "toolkit.h"
+#include "headerbar.h"
 
 #include "../common/context.h"
 #include "../common/object.h"
@@ -52,7 +53,7 @@
 }
 
 GtkWidget* ui_gtk_vbox_new(int spacing) {
-#ifdef UI_GTK3
+#if GTK_MAJOR_VERSION >= 3
     return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing);
 #else
     return gtk_vbox_new(FALSE, spacing);
@@ -60,39 +61,62 @@
 }
 
 GtkWidget* ui_gtk_hbox_new(int spacing) {
-#ifdef UI_GTK3
+#if GTK_MAJOR_VERSION >= 3
     return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing);
 #else
     return gtk_hbox_new(FALSE, spacing);
 #endif
 }
 
-/* -------------------- Frame Container (deprecated) -------------------- */
-UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) {
-    UiContainer *ct = ucx_mempool_calloc(
-            obj->ctx->mempool,
-            1,
-            sizeof(UiContainer));
-    ct->widget = frame;
-    ct->add = ui_frame_container_add;
-    return ct;
-}
-
-void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    gtk_container_add(GTK_CONTAINER(ct->widget), widget);
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
+GtkWidget* ui_subcontainer_create(
+        UiSubContainerType type,
+        UiObject *newobj,
+        int spacing,
+        int columnspacing,
+        int rowspacing,
+        int margin)
+{
+    GtkWidget *sub = NULL;
+    GtkWidget *add = NULL;
+    switch(type) {
+        default: {
+            sub = ui_gtk_vbox_new(spacing);
+            add = ui_box_set_margin(sub, margin);
+            newobj->container = ui_box_container(newobj, sub, type);
+            newobj->widget = sub;
+            break;
+        }
+        case UI_CONTAINER_HBOX: {
+            sub = ui_gtk_hbox_new(spacing);
+            add = ui_box_set_margin(sub, margin);
+            newobj->container = ui_box_container(newobj, sub, type);
+            newobj->widget = sub;
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            sub = ui_create_grid_widget(columnspacing, rowspacing);
+            add = ui_box_set_margin(sub, margin);
+            newobj->container = ui_grid_container(newobj, sub);
+            newobj->widget = sub;
+            break;
+        }
+        case UI_CONTAINER_NO_SUB: {
+            break;
+        }
+    }
+    return add;
 }
 
 
 /* -------------------- Box Container -------------------- */
-UiContainer* ui_box_container(UiObject *obj, GtkWidget *box) {
-    UiBoxContainer *ct = ucx_mempool_calloc(
-            obj->ctx->mempool,
+UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) {
+    UiBoxContainer *ct = cxCalloc(
+            obj->ctx->allocator,
             1,
             sizeof(UiBoxContainer));
     ct->container.widget = box;
     ct->container.add = ui_box_container_add;
+    ct->type = type;
     return (UiContainer*)ct;
 }
 
@@ -111,27 +135,40 @@
     }
     
     UiBool expand = fill;
+#if GTK_MAJOR_VERSION >= 4
+    gtk_box_append(GTK_BOX(ct->widget), widget);
+    GtkAlign align = expand ? GTK_ALIGN_FILL : GTK_ALIGN_START; 
+    if(bc->type == UI_CONTAINER_VBOX) {
+        gtk_widget_set_valign(widget, align);
+        gtk_widget_set_vexpand(widget, expand);
+        gtk_widget_set_hexpand(widget, TRUE);
+    } else if(bc->type == UI_CONTAINER_HBOX) {
+        gtk_widget_set_halign(widget, align);
+        gtk_widget_set_hexpand(widget, expand);
+        gtk_widget_set_vexpand(widget, TRUE);
+    }
+    
+#else
     gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0);
+#endif
     
     ui_reset_layout(ct->layout);
     ct->current = widget;
 }
 
 UiContainer* ui_grid_container(UiObject *obj, GtkWidget *grid) {
-    UiGridContainer *ct = ucx_mempool_calloc(
-            obj->ctx->mempool,
+    UiGridContainer *ct = cxCalloc(
+            obj->ctx->allocator,
             1,
             sizeof(UiGridContainer));
     ct->container.widget = grid;
     ct->container.add = ui_grid_container_add;
-#ifdef UI_GTK2
-    ct->width = 0;
-    ct->height = 1;
-#endif
+    UI_GTK_V2(ct->width = 0);
+    UI_GTK_V2(ct->height = 1);
     return (UiContainer*)ct;
 }
 
-#ifdef UI_GTK3
+#if GTK_MAJOR_VERSION >= 3
 void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
     UiGridContainer *grid = (UiGridContainer*)ct;
     
@@ -143,24 +180,39 @@
     
     int hexpand = FALSE;
     int vexpand = FALSE;
+    int hfill = FALSE;
+    int vfill = FALSE;
+    if(ct->layout.fill != UI_LAYOUT_UNDEFINED) {
+        fill = ui_lb2bool(ct->layout.fill);
+    }
     if(ct->layout.hexpand != UI_LAYOUT_UNDEFINED) {
         hexpand = ct->layout.hexpand;
+        hfill = TRUE;
     }
     if(ct->layout.vexpand != UI_LAYOUT_UNDEFINED) {
         vexpand = ct->layout.vexpand;
+        vfill = TRUE;
+    }
+    if(fill) {
+        hfill = TRUE;
+        vfill = TRUE;
     }
     
-    if(hexpand) {
-        gtk_widget_set_hexpand(widget, TRUE);
+    if(!hfill) {
+        gtk_widget_set_halign(widget, GTK_ALIGN_START);
     }
-    if(vexpand) {
-        gtk_widget_set_vexpand(widget, TRUE);
+    if(!vfill) {
+        gtk_widget_set_valign(widget, GTK_ALIGN_START);
     }
     
-    int gwidth = ct->layout.gridwidth > 0 ? ct->layout.gridwidth : 1;
+    gtk_widget_set_hexpand(widget, hexpand);
+    gtk_widget_set_vexpand(widget, vexpand);
     
-    gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, gwidth, 1);
-    grid->x += gwidth;
+    int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1;
+    int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1;
+    
+    gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan);
+    grid->x += colspan;
     
     ui_reset_layout(ct->layout);
     ct->current = widget;
@@ -187,6 +239,10 @@
     GtkAttachOptions xoptions = hexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL;
     GtkAttachOptions yoptions = vexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL;
     
+    int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1;
+    int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1;
+    // TODO: use colspan/rowspan
+    
     gtk_table_attach(GTK_TABLE(ct->widget), widget, grid->x, grid->x+1, grid->y, grid->y+1, xoptions, yoptions, 0, 0);
     grid->x++;
     int nw = grid->x > grid->width ? grid->x : grid->width;
@@ -201,9 +257,44 @@
 }
 #endif
 
+UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) {
+    UiContainer *ct = cxCalloc(
+            obj->ctx->allocator,
+            1,
+            sizeof(UiContainer));
+    ct->widget = frame;
+    ct->add = ui_frame_container_add;
+    return ct;
+}
+
+void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
+    FRAME_SET_CHILD(ct->widget, widget);
+}
+
+UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander) {
+    UiContainer *ct = cxCalloc(
+            obj->ctx->allocator,
+            1,
+            sizeof(UiContainer));
+    ct->widget = expander;
+    ct->add = ui_expander_container_add;
+    return ct;
+}
+
+void ui_expander_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
+    EXPANDER_SET_CHILD(ct->widget, widget);
+}
+
+void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
+    // TODO: check if the widget implements GtkScrollable
+    SCROLLEDWINDOW_SET_CHILD(ct->widget, widget);
+    ui_reset_layout(ct->layout);
+    ct->current = widget;
+}
+
 UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) {
-    UiContainer *ct = ucx_mempool_calloc(
-            obj->ctx->mempool,
+    UiContainer *ct = cxCalloc(
+            obj->ctx->allocator,
             1,
             sizeof(UiContainer));
     ct->widget = scrolledwindow;
@@ -211,20 +302,9 @@
     return ct;
 }
 
-void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    // TODO: check if the widget implements GtkScrollable
-#ifdef UI_GTK3
-    gtk_container_add(GTK_CONTAINER(ct->widget), widget);
-#else
-    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ct->widget), widget);
-#endif
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
 UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview) {
-    UiTabViewContainer *ct = ucx_mempool_calloc(
-            obj->ctx->mempool,
+    UiTabViewContainer *ct = cxCalloc(
+            obj->ctx->allocator,
             1,
             sizeof(UiTabViewContainer));
     ct->container.widget = tabview;
@@ -233,28 +313,23 @@
 }
 
 void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    gtk_notebook_append_page(
-            GTK_NOTEBOOK(ct->widget),
-            widget,
-            gtk_label_new(ct->layout.label));
+    UiGtkTabView *data = ui_widget_get_tabview_data(ct->widget);
+    if(!data) {
+        fprintf(stderr, "UI Error: widget is not a tabview");
+        return;
+    }
+    data->add_tab(ct->widget, -1, ct->layout.label, widget);
     
     ui_reset_layout(ct->layout);
     ct->current = widget;
 }
 
 
-UIWIDGET ui_vbox(UiObject *obj) {
-    return ui_vbox_sp(obj, 0, 0);
-}
 
-UIWIDGET ui_hbox(UiObject *obj) {
-    return ui_hbox_sp(obj, 0, 0);
-}
-
-static GtkWidget* box_set_margin(GtkWidget *box, int margin) {
+GtkWidget* ui_box_set_margin(GtkWidget *box, int margin) {
     GtkWidget *ret = box;
-#ifdef UI_GTK3
-#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 12
+#if GTK_MAJOR_VERSION >= 3
+#if GTK_MAJOR_VERSION * 1000 + GTK_MINOR_VERSION >= 3012
     gtk_widget_set_margin_start(box, margin);
     gtk_widget_set_margin_end(box, margin);
 #else
@@ -272,73 +347,53 @@
     return ret;
 }
 
-UIWIDGET ui_vbox_sp(UiObject *obj, int margin, int spacing) {
-    UiContainer *ct = uic_get_current_container(obj);
+UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type) {
+    UiObject *current = uic_current_obj(obj);
+    UiContainer *ct = current->container;
+    UI_APPLY_LAYOUT1(current, args);
     
-    GtkWidget *vbox = ui_gtk_vbox_new(spacing);   
-    GtkWidget *widget = margin > 0 ? box_set_margin(vbox, margin) : vbox;
+    GtkWidget *box = type == 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);
     
-    UiObject *newobj = uic_object_new(obj, vbox);
-    newobj->container = ui_box_container(obj, vbox);
-    uic_obj_add(obj, newobj);
-    
-    return widget;
-}
-
-UIWIDGET ui_hbox_sp(UiObject *obj, int margin, int spacing) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    GtkWidget *hbox = ui_gtk_hbox_new(spacing);
-    GtkWidget *widget = margin > 0 ? box_set_margin(hbox, margin) : hbox;
-    ct->add(ct, widget, TRUE);
-    
-    UiObject *newobj = uic_object_new(obj, hbox);
-    newobj->container = ui_box_container(obj, hbox);
+    UiObject *newobj = uic_object_new(obj, box);
+    newobj->container = ui_box_container(obj, box, type);
     uic_obj_add(obj, newobj);
     
     return widget;
 }
 
-UIWIDGET ui_grid(UiObject *obj) {
-    return ui_grid_sp(obj, 0, 0, 0);
+UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) {
+    return ui_box_create(obj, args, UI_CONTAINER_VBOX);
+}
+
+UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) {
+    return ui_box_create(obj, args, UI_CONTAINER_HBOX);
 }
 
-UIWIDGET ui_grid_sp(UiObject *obj, int margin, int columnspacing, int rowspacing) {
-    UiContainer *ct = uic_get_current_container(obj);
+GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing) {
+#if GTK_MAJOR_VERSION >= 3
+    GtkWidget *grid = gtk_grid_new();
+    gtk_grid_set_column_spacing(GTK_GRID(grid), colspacing);
+    gtk_grid_set_row_spacing(GTK_GRID(grid), rowspacing);
+#else
+    GtkWidget *grid = gtk_table_new(1, 1, FALSE);
+    gtk_table_set_col_spacings(GTK_TABLE(grid), colspacing);
+    gtk_table_set_row_spacings(GTK_TABLE(grid), rowspacing);
+#endif
+    return grid;
+}
+
+UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    UI_APPLY_LAYOUT1(current, args);
     GtkWidget *widget;
     
-#ifdef UI_GTK3
-    GtkWidget *grid = gtk_grid_new();
-    gtk_grid_set_column_spacing(GTK_GRID(grid), columnspacing);
-    gtk_grid_set_row_spacing(GTK_GRID(grid), rowspacing);
-#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 12
-    gtk_widget_set_margin_start(grid, margin);
-    gtk_widget_set_margin_end(grid, margin);
-#else
-    gtk_widget_set_margin_left(grid, margin);
-    gtk_widget_set_margin_right(grid, margin);
-#endif
-    gtk_widget_set_margin_top(grid, margin);
-    gtk_widget_set_margin_bottom(grid, margin);
-    
-    widget = grid;
-#elif defined(UI_GTK2)
-    GtkWidget *grid = gtk_table_new(1, 1, FALSE);
-    
-    gtk_table_set_col_spacings(GTK_TABLE(grid), columnspacing);
-    gtk_table_set_row_spacings(GTK_TABLE(grid), rowspacing);
-    
-    if(margin > 0) {
-        GtkWidget *a = gtk_alignment_new(0.5, 0.5, 1, 1);
-        gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin, margin, margin, margin);
-        gtk_container_add(GTK_CONTAINER(a), grid);
-        widget = a;
-    } else {
-        widget = grid;
-    }
-#endif
-    ct->add(ct, widget, TRUE);
+    GtkWidget *grid = ui_create_grid_widget(args.columnspacing, args.rowspacing);
+    ui_set_name_and_style(grid, args.name, args.style_class);
+    widget = ui_box_set_margin(grid, args.margin);
+    current->container->add(current->container, widget, TRUE);
     
     UiObject *newobj = uic_object_new(obj, grid);
     newobj->container = ui_grid_container(obj, grid);
@@ -347,47 +402,535 @@
     return widget;
 }
 
-UIWIDGET ui_scrolledwindow(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
-    ct->add(ct, sw, TRUE);
+UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    UI_APPLY_LAYOUT1(current, args);
+    
+    GtkWidget *frame = gtk_frame_new(args.label);
+    UiObject *newobj = uic_object_new(obj, frame);
+    GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin);
+    if(sub) {
+        FRAME_SET_CHILD(frame, sub);
+    } else {
+        newobj->widget = frame;
+        newobj->container = ui_frame_container(obj, frame);
+    }
+    current->container->add(current->container, frame, FALSE);
+    uic_obj_add(obj, newobj);
+    
+    return frame;
+}
+
+UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    UI_APPLY_LAYOUT1(current, args);
+    
+    GtkWidget *expander = gtk_expander_new(args.label);
+    gtk_expander_set_expanded(GTK_EXPANDER(expander), args.isexpanded);
+    UiObject *newobj = uic_object_new(obj, expander);
+    GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin);
+    if(sub) {
+        EXPANDER_SET_CHILD(expander, sub);
+    } else {
+        newobj->widget = expander;
+        newobj->container = ui_expander_container(obj, expander);
+    }
+    current->container->add(current->container, expander, FALSE);
+    uic_obj_add(obj, newobj);
+    
+    return expander;
+}
+
+
+UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    UI_APPLY_LAYOUT1(current, args);
+    
+    GtkWidget *sw = SCROLLEDWINDOW_NEW();
+    ui_set_name_and_style(sw, args.name, args.style_class);
+    GtkWidget *widget = ui_box_set_margin(sw, args.margin);
+    current->container->add(current->container, widget, TRUE);
     
     UiObject *newobj = uic_object_new(obj, sw);
-    newobj->container = ui_scrolledwindow_container(obj, sw);
+    GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin);
+    if(sub) {
+        SCROLLEDWINDOW_SET_CHILD(sw, sub);
+    } else {
+        newobj->widget = sw;
+        newobj->container = ui_scrolledwindow_container(obj, sw);
+    }
+    
     uic_obj_add(obj, newobj);
     
     return sw;
 }
 
-UIWIDGET ui_tabview(UiObject *obj) {
-    GtkWidget *tabview = gtk_notebook_new();
-    gtk_notebook_set_show_border(GTK_NOTEBOOK(tabview), FALSE);
-    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(tabview), FALSE);
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, tabview, TRUE);
-    
-    UiObject *tabviewobj = uic_object_new(obj, tabview);
-    tabviewobj->container = ui_tabview_container(obj, tabview);
-    uic_obj_add(obj, tabviewobj);
-    
-    return tabview;
+
+void ui_notebook_tab_select(UIWIDGET tabview, int tab) {
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab);
+}
+
+void ui_notebook_tab_remove(UIWIDGET tabview, int tab) {
+    gtk_notebook_remove_page(GTK_NOTEBOOK(tabview), tab);
+}
+
+void ui_notebook_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) {
+    gtk_notebook_insert_page(
+            GTK_NOTEBOOK(widget),
+            child,
+            gtk_label_new(name),
+            index);
+}
+
+int64_t ui_notebook_get(UiInteger *i) {
+    GtkNotebook *nb = i->obj;
+    i->value = gtk_notebook_get_current_page(nb);
+    return i->value;
+}
+
+void ui_notebook_set(UiInteger *i, int64_t value) {
+    GtkNotebook *nb = i->obj;
+    gtk_notebook_set_current_page(nb, value);
+    i->value = gtk_notebook_get_current_page(nb);
+}
+
+
+#if GTK_MAJOR_VERSION >= 4
+static int stack_set_page(GtkWidget *stack, int index) {
+    GtkSelectionModel *pages = gtk_stack_get_pages(GTK_STACK(stack));
+    GListModel *list = G_LIST_MODEL(pages);
+    GtkStackPage *page = g_list_model_get_item(list, index);
+    if(page) {
+        gtk_stack_set_visible_child(GTK_STACK(stack), gtk_stack_page_get_child(page));
+    } else {
+        fprintf(stderr, "UI Error: ui_stack_set value out of bounds\n");
+        return -1;
+    }
+    return index;
+}
+
+void ui_stack_tab_select(UIWIDGET tabview, int tab) {
+    stack_set_page(tabview, tab);
+}
+
+void ui_stack_tab_remove(UIWIDGET tabview, int tab) {
+    GtkStack *stack = GTK_STACK(tabview);
+    GtkWidget *current = gtk_stack_get_visible_child(stack);
+    GtkSelectionModel *pages = gtk_stack_get_pages(stack);
+    GListModel *list = G_LIST_MODEL(pages);
+    GtkStackPage *page = g_list_model_get_item(list, tab);
+    if(page) {
+        gtk_stack_remove(stack, gtk_stack_page_get_child(page));
+    }
+}
+
+void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) {
+    (void)gtk_stack_add_titled(GTK_STACK(widget), child, name, name);
+}
+
+int64_t ui_stack_get(UiInteger *i) {
+    GtkStack *stack = GTK_STACK(i->obj);
+    GtkWidget *current = gtk_stack_get_visible_child(stack);
+    GtkSelectionModel *pages = gtk_stack_get_pages(stack);
+    GListModel *list = G_LIST_MODEL(pages);
+    int nitems = g_list_model_get_n_items(list);
+    for(int p=0;p<nitems;p++) {
+        GtkStackPage *page = g_list_model_get_item(list, p);
+        GtkWidget *child = gtk_stack_page_get_child(page);
+        if(child == current) {
+            i->value = p;
+            break;
+        }
+    }
+    return i->value;
+}
+
+void ui_stack_set(UiInteger *i, int64_t value) {
+    GtkWidget *widget = i->obj;
+    if(stack_set_page(widget, value) >= 0) {
+        i->value = value;
+    }
+}
+#elif GTK_MAJOR_VERSION >= 3
+static GtkWidget* stack_get_child(GtkWidget *stack, int index) {
+    GList *children = gtk_container_get_children(GTK_CONTAINER(stack));
+    if(children) {
+        return g_list_nth_data(children, index);
+    }
+    return NULL;
+}
+
+void ui_stack_tab_select(UIWIDGET tabview, int tab) {
+    GtkWidget *child = stack_get_child(tabview, tab);
+    if(child) {
+        gtk_stack_set_visible_child(GTK_STACK(tabview), child);
+    }
+}
+
+void ui_stack_tab_remove(UIWIDGET tabview, int tab) {
+    GtkWidget *child = stack_get_child(tabview, tab);
+    if(child) {
+        gtk_container_remove(GTK_CONTAINER(tabview), child);
+    }
+}
+
+void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) {
+    gtk_stack_add_titled(GTK_STACK(widget), child, name, name);
+}
+
+int64_t ui_stack_get(UiInteger *i) {
+    GtkWidget *visible = gtk_stack_get_visible_child(GTK_STACK(i->obj));
+    GList *children = gtk_container_get_children(GTK_CONTAINER(i->obj));
+    GList *elm = children;
+    int n = 0;
+    int64_t v = -1;
+    while(elm) {
+        GtkWidget *child = elm->data;
+        if(child == visible) {
+            v = n;
+            break;
+        }
+        
+        elm = elm->next;
+        n++;
+    }
+    g_list_free(children);
+    i->value = v;
+    return v;
+}
+
+void ui_stack_set(UiInteger *i, int64_t value) {
+    GtkWidget *child = stack_get_child(i->obj, value);
+    if(child) {
+        gtk_stack_set_visible_child(GTK_STACK(i->obj), child);
+        i->value = value;
+    }
+}
+
+#endif
+
+
+
+
+UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview) {
+    return g_object_get_data(G_OBJECT(tabview), "ui_tabview");
 }
 
-void ui_tab(UiObject *obj, char *title) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.label = title;
-    ui_vbox(obj);
+typedef int64_t(*ui_tabview_get_func)(UiInteger*);
+typedef void (*ui_tabview_set_func)(UiInteger*, int64_t);
+
+UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs args) {
+    UiGtkTabView *data = malloc(sizeof(UiGtkTabView));
+    data->margin = args.margin;
+    data->spacing = args.spacing;
+    data->columnspacing = args.columnspacing;
+    data->rowspacing = args.rowspacing;
+    
+    ui_tabview_get_func getfunc = NULL;
+    ui_tabview_set_func setfunc = NULL;
+    
+    GtkWidget *widget = NULL;
+    GtkWidget *data_widget = NULL;
+    switch(args.tabview) {
+        case UI_TABVIEW_DOC: {
+            // TODO
+            break;
+        }
+        case UI_TABVIEW_NAVIGATION_SIDE: {
+#if GTK_CHECK_VERSION(3, 10, 0)
+            widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+            GtkWidget *sidebar = gtk_stack_sidebar_new();
+            BOX_ADD(widget, sidebar);
+            GtkWidget *stack = gtk_stack_new();
+            gtk_stack_set_transition_type (GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+            gtk_stack_sidebar_set_stack(GTK_STACK_SIDEBAR(sidebar), GTK_STACK(stack));
+            BOX_ADD_EXPAND(widget, stack);
+            data->select_tab = ui_stack_tab_select;
+            data->remove_tab = ui_stack_tab_remove;
+            data->add_tab = ui_stack_tab_add;
+            getfunc = ui_stack_get;
+            setfunc = ui_stack_set;
+            data_widget = stack;
+#else
+            // TODO
+#endif
+            break;
+        }
+        case UI_TABVIEW_DEFAULT: /* fall through */
+        case UI_TABVIEW_NAVIGATION_TOP: /* fall through */
+        case UI_TABVIEW_INVISIBLE: /* fall through */
+        case UI_TABVIEW_NAVIGATION_TOP2: {
+            widget = gtk_notebook_new();
+            data_widget = widget;
+            data->select_tab = ui_notebook_tab_select;
+            data->remove_tab = ui_notebook_tab_remove;
+            data->add_tab = ui_notebook_tab_add;
+            getfunc = ui_notebook_get;
+            setfunc = ui_notebook_set;
+            if(args.tabview == UI_TABVIEW_INVISIBLE) {
+                gtk_notebook_set_show_tabs(GTK_NOTEBOOK(widget), FALSE);
+                gtk_notebook_set_show_border(GTK_NOTEBOOK(widget), FALSE);
+            }
+            break;
+        }
+    }
+    
+    UiObject* current = uic_current_obj(obj);
+    if(args.value || args.varname) {
+        UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER);
+        UiInteger *i = var->value;
+        i->get = getfunc;
+        i->set = setfunc;
+        i->obj = data_widget;
+    }
+    
+    g_object_set_data(G_OBJECT(widget), "ui_tabview", data);
+    data->widget = data_widget;
+    data->subcontainer = args.subcontainer;
+    
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, widget, TRUE);
+    
+    UiObject *newobj = uic_object_new(obj, widget);
+    newobj->container = ui_tabview_container(obj, widget);
+    uic_obj_add(obj, newobj);
+    data->obj = newobj;
+    
+    return widget;
+}
+
+void ui_tab_create(UiObject* obj, const char* title) {
+    UiObject* current = uic_current_obj(obj);
+    UiGtkTabView *data = ui_widget_get_tabview_data(current->widget);
+    if(!data) {
+        fprintf(stderr, "UI Error: widget is not a tabview\n");
+        return;
+    }
+    
+    UiObject *newobj = ui_tabview_add(current->widget, title, -1);
+    current->next = newobj;
+}
+
+
+
+void ui_tabview_select(UIWIDGET tabview, int tab) {
+    UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
+    if(!data) {
+        fprintf(stderr, "UI Error: widget is not a tabview\n");
+        return;
+    }
+    data->select_tab(tabview, tab);
+}
+
+void ui_tabview_remove(UIWIDGET tabview, int tab) {
+    UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
+    if(!data) {
+        fprintf(stderr, "UI Error: widget is not a tabview\n");
+        return;
+    }
+    data->remove_tab(tabview, tab);
+}
+
+UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) {
+    UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
+    if(!data) {
+        fprintf(stderr, "UI Error: widget is not a tabview\n");
+        return NULL;
+    }
+    
+    UiObject *newobj = cxCalloc(data->obj->ctx->allocator, 1, sizeof(UiObject));
+    newobj->ctx = data->obj->ctx;
+    
+    GtkWidget *sub;
+    switch(data->subcontainer) {
+        default: {
+            sub = ui_gtk_vbox_new(data->spacing);
+            newobj->container = ui_box_container(newobj, sub, data->subcontainer);
+            break;
+        }
+        case UI_CONTAINER_HBOX: {
+            sub = ui_gtk_hbox_new(data->spacing);
+            newobj->container = ui_box_container(newobj, sub, data->subcontainer);
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            sub = ui_create_grid_widget(data->columnspacing, data->rowspacing);
+            newobj->container = ui_grid_container(newobj, sub);
+            break;
+        }
+    }
+    newobj->widget = sub;
+    GtkWidget *widget = ui_box_set_margin(sub, data->margin);
+    
+    data->add_tab(data->widget, tab_index, name, widget);
+    
+    return newobj;
 }
 
-void ui_select_tab(UIWIDGET tabview, int tab) {
-    gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab);
+
+/* -------------------- Headerbar -------------------- */
+
+static void hb_set_part(UiObject *obj, int part) {
+    UiObject* current = uic_current_obj(obj);
+    GtkWidget *headerbar = current->widget;
+    
+    UiHeaderbarContainer *hb = cxCalloc(
+            obj->ctx->allocator,
+            1,
+            sizeof(UiHeaderbarContainer));
+    memcpy(hb, current->container, sizeof(UiHeaderbarContainer));
+    
+    UiObject *newobj = uic_object_new(obj, headerbar);
+    newobj->container = (UiContainer*)hb;
+    uic_obj_add(obj, newobj);
+    
+    hb->part = part;
+}
+
+void ui_headerbar_start_create(UiObject *obj) {
+    hb_set_part(obj, 0);
+}
+
+void ui_headerbar_center_create(UiObject *obj) {
+    hb_set_part(obj, 2);
+}
+
+void ui_headerbar_end_create(UiObject *obj) {
+    hb_set_part(obj, 1);
+}
+
+UIWIDGET ui_headerbar_fallback_create(UiObject *obj, UiHeaderbarArgs args) {
+    UiObject *current = uic_current_obj(obj);
+    UiContainer *ct = current->container;
+    UI_APPLY_LAYOUT1(current, args);
+    
+    GtkWidget *box = ui_gtk_hbox_new(args.alt_spacing);
+    ui_set_name_and_style(box, args.name, args.style_class);
+    ct->add(ct, box, FALSE);
+    
+    UiObject *newobj = uic_object_new(obj, box);
+    newobj->container = ui_headerbar_fallback_container(obj, box);
+    uic_obj_add(obj, newobj);
+    
+    return box;
+}
+
+static void hb_fallback_set_part(UiObject *obj, int part) {
+    UiObject* current = uic_current_obj(obj);
+    GtkWidget *headerbar = current->widget;
+    
+    UiObject *newobj = uic_object_new(obj, headerbar);
+    newobj->container = ui_headerbar_container(obj, headerbar);
+    uic_obj_add(obj, newobj);
+    
+    UiHeaderbarContainer *hb = (UiHeaderbarContainer*)newobj->container;
+    hb->part = part;
+}
+
+UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) {
+    UiHeaderbarContainer *ct = cxCalloc(
+            obj->ctx->allocator,
+            1,
+            sizeof(UiHeaderbarContainer));
+    ct->container.widget = headerbar;
+    ct->container.add = ui_headerbar_fallback_container_add;
+    return (UiContainer*)ct;
+}
+
+void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
+    UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct;
+    BOX_ADD(ct->widget, widget);
 }
 
+#if GTK_CHECK_VERSION(3, 10, 0)
+
+UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) {
+    GtkWidget *headerbar = g_object_get_data(G_OBJECT(obj->widget), "ui_headerbar");
+    if(!headerbar) {
+        return ui_headerbar_fallback_create(obj, args);
+    }
+    
+    UiObject *newobj = uic_object_new(obj, headerbar);
+    newobj->container = ui_headerbar_container(obj, headerbar);
+    uic_obj_add(obj, newobj);
+    
+    return headerbar;    
+}
+
+UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) {
+    UiHeaderbarContainer *ct = cxCalloc(
+            obj->ctx->allocator,
+            1,
+            sizeof(UiHeaderbarContainer));
+    ct->container.widget = headerbar;
+    ct->container.add = ui_headerbar_container_add;
+    return (UiContainer*)ct;
+}
+
+void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
+    UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct;
+    if(hb->part == 0) {
+        UI_HEADERBAR_PACK_START(ct->widget, widget);
+    } else if(hb->part == 1) {
+        UI_HEADERBAR_PACK_END(ct->widget, widget);
+    } else if(hb->part == 2) {
+        if(!hb->centerbox) {
+            GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+            hb->centerbox = box;
+            UI_HEADERBAR_SET_TITLE_WIDGET(ct->widget, box);
+        }
+        BOX_ADD(hb->centerbox, widget);
+    }
+}
+
+#else
+
+UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) {
+    return ui_headerbar_fallback_create(obj, args);  
+}
+
+#endif
+
+/* -------------------- Sidebar -------------------- */
+
+#ifdef UI_LIBADWAITA
+UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args) {
+    GtkWidget *sidebar_toolbar_view = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar");
+    if(!sidebar_toolbar_view) {
+        fprintf(stderr, "Error: window is not configured for sidebar\n");
+        return NULL;
+    }
+    
+    GtkWidget *box = ui_gtk_vbox_new(args.spacing);
+    ui_box_set_margin(box, args.margin);
+    adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), box);
+    
+    UiObject *newobj = uic_object_new(obj, box);
+    newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_obj_add(obj, newobj);
+    
+    return box;
+}
+#else
+UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args) {
+    GtkWidget *sidebar_vbox = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar");
+    
+    GtkWidget *box = ui_gtk_vbox_new(args.spacing);
+    ui_box_set_margin(box, args.margin);
+    BOX_ADD_EXPAND(sidebar_vbox, box);
+    
+    UiObject *newobj = uic_object_new(obj, box);
+    newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_obj_add(obj, newobj);
+    
+    return box;
+}
+#endif
+
 /* -------------------- Splitpane -------------------- */
 
 static GtkWidget* create_paned(UiOrientation orientation) {
-#ifdef UI_GTK3
+#if GTK_MAJOR_VERSION >= 3
     switch(orientation) {
         case UI_HORIZONTAL: return gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
         case UI_VERTICAL: return gtk_paned_new(GTK_ORIENTATION_VERTICAL);
@@ -401,191 +944,129 @@
     return NULL;
 }
 
-UIWIDGET ui_splitpane(UiObject *obj, int max, UiOrientation orientation) {
-    GtkWidget *paned = create_paned(orientation);
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, paned, TRUE);
-    
-    if(max <= 0) max = INT_MAX;
-    
-    UiPanedContainer *pctn = ucx_mempool_calloc(
-            obj->ctx->mempool,
-            1,
-            sizeof(UiPanedContainer));
-    pctn->container.widget = paned;
-    pctn->container.add = ui_paned_container_add;
-    pctn->current_pane = paned;
-    pctn->orientation = orientation;
-    pctn->max = max;
-    pctn->cur = 0;
-    
-    UiObject *pobj = uic_object_new(obj, paned);
-    pobj->container = (UiContainer*)pctn;
-    
-    uic_obj_add(obj, pobj);
-    
-    return paned;
-}
-
-UIWIDGET ui_hsplitpane(UiObject *obj, int max) {
-    return ui_splitpane(obj, max, UI_HORIZONTAL);
-}
-
-UIWIDGET ui_vsplitpane(UiObject *obj, int max) {
-    return ui_splitpane(obj, max, UI_VERTICAL);
-}
-
-void ui_paned_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    UiPanedContainer *pctn = (UiPanedContainer*)ct;
-    
-    gboolean resize = (ct->layout.hexpand || ct->layout.vexpand) ? TRUE : FALSE;
-    int width = ct->layout.width;
-    ui_reset_layout(ct->layout);
-    
-    if(pctn->cur == 0) {
-        gtk_paned_pack1(GTK_PANED(pctn->current_pane), widget, resize, resize);
-    } else if(pctn->cur < pctn->max-1) {
-        GtkWidget *nextPane = create_paned(pctn->orientation);
-        gtk_paned_pack2(GTK_PANED(pctn->current_pane), nextPane, TRUE, TRUE);
-        gtk_paned_pack1(GTK_PANED(nextPane), widget, resize, resize);
-        pctn->current_pane = nextPane;
-    } else if(pctn->cur == pctn->max-1) {
-        gtk_paned_pack2(GTK_PANED(pctn->current_pane), widget, resize, resize);
-        width = 0; // disable potential call of gtk_paned_set_position
-    } else {
-        fprintf(stderr, "Splitpane max reached: %d\n", pctn->max);
-        return;
-    }
-    
-    if(width > 0) {
-        gtk_paned_set_position(GTK_PANED(pctn->current_pane), width);
-    }
-    
-    pctn->cur++;
-}
 
 
-/* -------------------- Sidebar (deprecated) -------------------- */
-UIWIDGET ui_sidebar(UiObject *obj) {
-#ifdef UI_GTK3
-    GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
-#else
-    GtkWidget *paned = gtk_hpaned_new();
-#endif
-    gtk_paned_set_position(GTK_PANED(paned), 200);
-    
-    GtkWidget *sidebar = ui_gtk_vbox_new(0);
-    gtk_paned_pack1(GTK_PANED(paned), sidebar, TRUE, FALSE);
-    
-    UiObject *left = uic_object_new(obj, sidebar);
-    UiContainer *ct1 = ui_box_container(obj, sidebar);
-    left->container = ct1;
-    
-    UiObject *right = uic_object_new(obj, sidebar);
-    UiContainer *ct2 = ucx_mempool_malloc(
-            obj->ctx->mempool,
-            sizeof(UiContainer));
-    ct2->widget = paned;
-    ct2->add = ui_split_container_add2;
-    right->container = ct2;
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, paned, TRUE);
-    
-    uic_obj_add(obj, right);
-    uic_obj_add(obj, left);
-    
-    return sidebar;
+/* -------------------- 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);
 }
 
-void ui_split_container_add1(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    // TODO: remove
-    gtk_paned_pack1(GTK_PANED(ct->widget), widget, TRUE, FALSE);
+static void update_itemlist(UiList *list, int c) {
+    UiGtkItemListContainer *ct = list->obj;
     
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-void ui_split_container_add2(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    gtk_paned_pack2(GTK_PANED(ct->widget), widget, TRUE, FALSE);
+    CxMap *new_items = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    new_items->collection.advanced_destructor = remove_item;
+    new_items->collection.destructor_data = ct;
     
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-
-/* -------------------- Document Tabview -------------------- */
-static void page_change(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer data) {
-    GQuark q = g_quark_from_static_string("ui.tab.object");
-    UiObject *tab = g_object_get_qdata(G_OBJECT(page), q);
-    if(!tab) {
-        return;
+    // 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++;
     }
     
-    //printf("page_change: %d\n", page_num);
-    UiContext *ctx = tab->ctx;
-    uic_context_detach_all(ctx->parent); // TODO: fix?
-    ctx->parent->attach_document(ctx->parent, ctx->document);
-}
-
-UiTabbedPane* ui_tabbed_document_view(UiObject *obj) {
-    GtkWidget *tabview = gtk_notebook_new();
-    gtk_notebook_set_show_border(GTK_NOTEBOOK(tabview), FALSE);
+    // 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;
     
-    g_signal_connect(
-                tabview,
-                "switch-page",
-                G_CALLBACK(page_change),
-                NULL);
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, tabview, TRUE);
-    
-    UiTabbedPane *tabbedpane = ui_malloc(obj->ctx, sizeof(UiTabbedPane));
-    tabbedpane->ctx = uic_current_obj(obj)->ctx;
-    tabbedpane->widget = tabview;
-    tabbedpane->document = NULL;
-    
-    return tabbedpane;
+    // 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++;
+    }
 }
 
-UiObject* ui_document_tab(UiTabbedPane *view) {
-    GtkWidget *frame = gtk_frame_new(NULL);
-    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
-    // TODO: label
-    gtk_notebook_append_page(GTK_NOTEBOOK(view->widget), frame, NULL);
-    
-    UiObject *tab = ui_malloc(view->ctx, sizeof(UiObject));
-    tab->widget = NULL; // initialization for uic_context()
-    tab->ctx = uic_context(tab, view->ctx->mempool);
-    tab->ctx->parent = view->ctx;
-    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);
-    tab->next = NULL;
-    
-    GQuark q = g_quark_from_static_string("ui.tab.object");
-    g_object_set_qdata(G_OBJECT(frame), q, tab);
-    
-    return tab;
+static void destroy_itemlist_container(GtkWidget *w, UiGtkItemListContainer *container) {
+    container->remove_items = FALSE;
+    cxMapDestroy(container->current_items);
+    free(container);
 }
 
-void ui_tab_set_document(UiContext *ctx, void *document) {
-    // TODO: remove?
-    if(ctx->parent->document) {
-        //ctx->parent->detach_document(ctx->parent, ctx->parent->document);
+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);
     }
-    //uic_context_set_document(ctx, document);
-    //uic_context_set_document(ctx->parent, document);
-    //ctx->parent->document = document;
+    g_signal_connect(
+                box,
+                "destroy",
+                G_CALLBACK(destroy_itemlist_container),
+                container);
+    
+    return box;
 }
 
-void ui_tab_detach_document(UiContext *ctx) {
-    // TODO: remove?
-    //uic_context_detach_document(ctx->parent);
-}
 
 
 /*
@@ -610,14 +1091,24 @@
     ct->layout.vexpand = expand;
 }
 
-void ui_layout_width(UiObject *obj, int width) {
+void ui_layout_hfill(UiObject *obj, UiBool fill) {
     UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.width = width;
+    ct->layout.hfill = fill;
 }
 
-void ui_layout_gridwidth(UiObject *obj, int width) {
+void ui_layout_vfill(UiObject *obj, UiBool fill) {
     UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.gridwidth = width;
+    ct->layout.vfill = fill;
+}
+
+void ui_layout_colspan(UiObject* obj, int cols) {
+    UiContainer* ct = uic_get_current_container(obj);
+    ct->layout.colspan = cols;
+}
+
+void ui_layout_rowspan(UiObject* obj, int rows) {
+    UiContainer* ct = uic_get_current_container(obj);
+    ct->layout.rowspan = rows;
 }
 
 void ui_newline(UiObject *obj) {

mercurial