ui/gtk/container.c

changeset 112
c3f2f16fa4b8
parent 110
c00e968d018b
--- a/ui/gtk/container.c	Sat Oct 04 14:54:25 2025 +0200
+++ b/ui/gtk/container.c	Sun Oct 19 21:20:08 2025 +0200
@@ -37,19 +37,20 @@
 
 #include "../common/context.h"
 #include "../common/object.h"
+#include "../common/container.h"
 
 #include "../ui/properties.h"
 
 
 void ui_container_begin_close(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
+    UiContainerX *ct = obj->container_end;
     ct->close = 1;
 }
 
 int ui_container_finish(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
+    UiContainerX *ct = obj->container_end;
     if(ct->close) {
-        ui_end(obj);
+        ui_end_new(obj);
         return 0;
     }
     return 1;
@@ -73,7 +74,7 @@
 
 GtkWidget* ui_subcontainer_create(
         UiSubContainerType type,
-        UiObject *newobj,
+        UiObject *obj,
         int spacing,
         int columnspacing,
         int rowspacing,
@@ -81,38 +82,39 @@
 {
     GtkWidget *sub = NULL;
     GtkWidget *add = NULL;
+    UiContainerX *container = 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;
+            add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+            container = ui_box_container(obj, sub, type);
             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;
+            add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+            container = ui_box_container(obj, sub, type);
             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, FALSE, FALSE, FALSE, FALSE);
-            newobj->widget = sub;
+            add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+            container = ui_grid_container(obj, sub, FALSE, FALSE, FALSE, FALSE);
             break;
         }
         case UI_CONTAINER_NO_SUB: {
             break;
         }
     }
+    if(container) {
+        uic_object_push_container(obj, container);
+    }
     return add;
 }
 
 
 /* -------------------- Box Container -------------------- */
-UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) {
+UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) {
     UiBoxContainer *ct = cxCalloc(
             obj->ctx->allocator,
             1,
@@ -120,12 +122,14 @@
     ct->container.widget = box;
     ct->container.add = ui_box_container_add;
     ct->type = type;
-    return (UiContainer*)ct;
+    return (UiContainerX*)ct;
 }
 
-void ui_box_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_box_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiBoxContainer *bc = (UiBoxContainer*)ct;
-    UiBool fill = ct->layout.fill;
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+    
+    UiBool fill = layout->fill;
     if(bc->has_fill && fill) {
         fprintf(stderr, "UiError: container has 2 filled widgets");
         fill = FALSE;
@@ -152,11 +156,10 @@
     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(
+UiContainerX* ui_grid_container(
         UiObject *obj,
         GtkWidget *grid,
         UiBool def_hexpand,
@@ -176,142 +179,72 @@
     ct->container.add = ui_grid_container_add;
     UI_GTK_V2(ct->width = 0);
     UI_GTK_V2(ct->height = 1);
-    return (UiContainer*)ct;
+    return (UiContainerX*)ct;
 }
 
+
 #if GTK_MAJOR_VERSION >= 3
-void ui_grid_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiGridContainer *grid = (UiGridContainer*)ct;
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     
-    if(ct->layout.newline) {
+    if(ct->container.newline) {
         grid->x = 0;
         grid->y++;
-        ct->layout.newline = FALSE;
+        ct->container.newline = FALSE;
     }
     
-    int hexpand = FALSE;
-    int vexpand = FALSE;
-    int hfill = FALSE;
-    int vfill = FALSE;
-    if(!ct->layout.override_defaults) {
-        if(grid->def_hexpand) {
-            hexpand = TRUE;
-        }
-        if(grid->def_hfill) {
-            hfill = TRUE;
-        }
-        if(grid->def_vexpand) {
-            vexpand = TRUE;
-        }
-        if(grid->def_vfill) {
-            vfill = TRUE;
-        }
-    }
+    uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);
     
-    UiBool fill = ct->layout.fill;
-    if(ct->layout.hexpand) {
-        hexpand = TRUE;
-    }
-    if(ct->layout.hfill) {
-        hfill = TRUE;
-    }
-    if(ct->layout.vexpand) {
-        vexpand = TRUE;
-    }
-    if(ct->layout.vfill) {
-        vfill = TRUE;
-    }
-    if(fill) {
-        hfill = TRUE;
-        vfill = TRUE;
-        hexpand = TRUE;
-        vexpand = TRUE;
-    }
-    
-    if(!hfill) {
+    if(!layout->hfill) {
         gtk_widget_set_halign(widget, GTK_ALIGN_START);
     }
-    if(!vfill) {
+    if(!layout->vfill) {
         gtk_widget_set_valign(widget, GTK_ALIGN_START);
     }
     
-    gtk_widget_set_hexpand(widget, hexpand);
-    gtk_widget_set_vexpand(widget, vexpand);
+    gtk_widget_set_hexpand(widget, layout->hexpand);
+    gtk_widget_set_vexpand(widget, layout->vexpand);
     
-    int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1;
-    int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1;
+    int colspan = layout->colspan > 0 ? layout->colspan : 1;
+    int rowspan = layout->rowspan > 0 ? 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;
+    grid->container.current = widget;
 }
 #endif
 #ifdef UI_GTK2
-void ui_grid_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget) {
     UiGridContainer *grid = (UiGridContainer*)ct;
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     
-    if(ct->layout.newline) {
+    if(ct->container.newline) {
         grid->x = 0;
         grid->y++;
-        ct->layout.newline = FALSE;
+        ct->container.newline = FALSE;
     }
     
-    int hexpand = FALSE;
-    int vexpand = FALSE;
-    int hfill = FALSE;
-    int vfill = FALSE;
-    if(!ct->layout.override_defaults) {
-        if(grid->def_hexpand) {
-            hexpand = TRUE;
-            hfill = TRUE;
-        } else if(grid->def_hfill) {
-            hfill = TRUE;
-        }
-        if(grid->def_vexpand) {
-            vexpand = TRUE;
-            vfill = TRUE;
-        } else if(grid->def_vfill) {
-            vfill = TRUE;
-        }
-    }
-    
-    UiBool fill = ct->layout.fill;
-    if(ct->layout.hexpand) {
-        hexpand = TRUE;
-        hfill = TRUE;
-    } else if(ct->layout.hfill) {
-        hfill = TRUE;
-    }
-    if(ct->layout.vexpand) {
-        vexpand = TRUE;
-        vfill = TRUE;
-    } else if(ct->layout.vfill) {
-        vfill = TRUE;
-    }
-    if(fill) {
-        hfill = TRUE;
-        vfill = TRUE;
-    }
+    uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);
     
     GtkAttachOptions xoptions = 0;
     GtkAttachOptions yoptions = 0;
-    if(hexpand) {
+    if(layout->hexpand) {
         xoptions = GTK_EXPAND;
     }
-    if(hfill) {
+    if(layout->hfill) {
         xoptions |= GTK_FILL;
     }
-    if(vexpand) {
+    if(layout->vexpand) {
         yoptions = GTK_EXPAND;
     }
-    if(vfill) {
+    if(layout->vfill) {
         yoptions |= GTK_FILL;
     }
     
-    int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1;
-    int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1;
+    int colspan = layout->colspan > 0 ? layout->colspan : 1;
+    int rowspan = layout->rowspan > 0 ? 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);
@@ -323,116 +256,138 @@
         gtk_table_resize(GTK_TABLE(ct->widget), grid->width, grid->height);
     }
     
-    ui_reset_layout(ct->layout);
     ct->current = widget;
 }
 #endif
 
-UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) {
-    UiContainer *ct = cxCalloc(
+UiContainerX* ui_frame_container(UiObject *obj, GtkWidget *frame) {
+    UiContainerPrivate *ct = cxCalloc(
             obj->ctx->allocator,
             1,
-            sizeof(UiContainer));
+            sizeof(UiContainerPrivate));
     ct->widget = frame;
     ct->add = ui_frame_container_add;
-    return ct;
-}
-
-void ui_frame_container_add(UiContainer *ct, GtkWidget *widget) {
-    FRAME_SET_CHILD(ct->widget, widget);
+    return (UiContainerX*)ct;
 }
 
-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) {
-    EXPANDER_SET_CHILD(ct->widget, widget);
-}
-
-void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget) {
-    // TODO: check if the widget implements GtkScrollable
-    SCROLLEDWINDOW_SET_CHILD(ct->widget, widget);
-    ui_reset_layout(ct->layout);
+void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+    FRAME_SET_CHILD(ct->widget, widget);
     ct->current = widget;
 }
 
-UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) {
-    UiContainer *ct = cxCalloc(
+UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander) {
+    UiContainerPrivate *ct = cxCalloc(
             obj->ctx->allocator,
             1,
-            sizeof(UiContainer));
+            sizeof(UiContainerPrivate));
+    ct->widget = expander;
+    ct->add = ui_expander_container_add;
+    return (UiContainerX*)ct;
+}
+
+void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+    EXPANDER_SET_CHILD(ct->widget, widget);
+    ct->current = widget;
+}
+
+void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+    // TODO: check if the widget implements GtkScrollable
+    SCROLLEDWINDOW_SET_CHILD(ct->widget, widget);
+    ct->current = widget;
+}
+
+UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) {
+    UiContainerPrivate *ct = cxCalloc(
+            obj->ctx->allocator,
+            1,
+            sizeof(UiContainerPrivate));
     ct->widget = scrolledwindow;
     ct->add = ui_scrolledwindow_container_add;
-    return ct;
+    return (UiContainerX*)ct;
 }
 
-UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview) {
+UiContainerX* ui_tabview_container(UiObject *obj, GtkWidget *tabview) {
     UiTabViewContainer *ct = cxCalloc(
             obj->ctx->allocator,
             1,
             sizeof(UiTabViewContainer));
     ct->container.widget = tabview;
     ct->container.add = ui_tabview_container_add;
-    return (UiContainer*)ct;
+    return (UiContainerX*)ct;
 }
 
-void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_tabview_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     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);
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+    data->add_tab(ct->widget, -1, layout->label, widget);
     
-    ui_reset_layout(ct->layout);
     ct->current = widget;
 }
 
+#ifdef UI_GTK2
 
+static void alignment_child_visibility_changed(GtkWidget *widget, gpointer user_data) {
+    gtk_widget_set_visible(gtk_widget_get_parent(widget), gtk_widget_get_visible(widget));
+}
+
+#endif
 
-GtkWidget* ui_box_set_margin(GtkWidget *box, int margin) {
-    GtkWidget *ret = box;
+GtkWidget* ui_gtk_set_margin(GtkWidget *widget, int margin, int margin_left, int margin_right, int margin_top, int margin_bottom) {
+    if(margin > 0) {
+        margin_left = margin;
+        margin_right = margin;
+        margin_top = margin;
+        margin_bottom = margin;
+    }
+    GtkWidget *ret = widget;
 #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);
+#if GTK_CHECK_VERSION(3, 12, 0)
+    gtk_widget_set_margin_start(widget, margin_left);
+    gtk_widget_set_margin_end(widget, margin_right);
 #else
-    gtk_widget_set_margin_left(box, margin);
-    gtk_widget_set_margin_right(box, margin);
+    gtk_widget_set_margin_left(widget, margin_left);
+    gtk_widget_set_margin_right(widget, margin_right);
 #endif
-    gtk_widget_set_margin_top(box, margin);
-    gtk_widget_set_margin_bottom(box, margin);
+    gtk_widget_set_margin_top(widget, margin_top);
+    gtk_widget_set_margin_bottom(widget, margin_bottom);
 #elif defined(UI_GTK2)
     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), box);
+    gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin_top, margin_bottom, margin_left, margin_right);
+    gtk_container_add(GTK_CONTAINER(a), widget);
+    g_signal_connect(
+                widget,
+                "show",
+                G_CALLBACK(alignment_child_visibility_changed),
+                NULL);
+    g_signal_connect(
+                widget,
+                "hide",
+                G_CALLBACK(alignment_child_visibility_changed),
+                NULL);
     ret = a;
 #endif
     return ret;
 }
 
 UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, UiSubContainerType type) {
-    UiObject *current = uic_current_obj(obj);
-    UiContainer *ct = current->container;
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     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);
+    ct->add(ct, box, &layout);
     
-    UiObject *newobj = uic_object_new(obj, box);
-    newobj->container = ui_box_container(obj, box, type);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_box_container(obj, box, type);
+    uic_object_push_container(obj, container);
     
-    return widget;
+    return box;
 }
 
 UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) {
@@ -457,82 +412,113 @@
 }
 
 UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     GtkWidget *widget;
     
     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);
+    ct->add(ct, grid, &layout);
+    
+    UiContainerX *container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill);
+    uic_object_push_container(obj, container);
     
-    UiObject *newobj = uic_object_new(obj, grid);
-    newobj->container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill);
-    uic_obj_add(obj, newobj);
-    
-    return widget;
+    return grid;
+}
+
+static void frame_create_subcontainer(UiObject *obj, UiFrameArgs *args) {
+    switch(args->subcontainer) {
+        default:
+        case UI_CONTAINER_VBOX: {
+            UiContainerArgs sub_args = { .spacing = args->spacing, .margin = args->padding };
+            ui_vbox_create(obj, &sub_args);
+            break;
+        }
+        case UI_CONTAINER_HBOX: {
+            UiContainerArgs sub_args = { .spacing = args->spacing, .margin = args->padding };
+            ui_hbox_create(obj, &sub_args);
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            UiContainerArgs sub_args = { .columnspacing = args->columnspacing, .rowspacing = args->rowspacing, .margin = args->padding };
+            ui_grid_create(obj, &sub_args);
+            break;
+        }
+        case UI_CONTAINER_NO_SUB: {
+            break; // NOOP
+        }
+    }
 }
 
 UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(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);
+    ct->add(ct, frame, &layout);
+    
+    GtkWidget *sub = ui_subcontainer_create(
+            args->subcontainer,
+            obj, args->spacing,
+            args->columnspacing,
+            args->rowspacing,
+            args->padding);
     if(sub) {
         FRAME_SET_CHILD(frame, sub);
     } else {
-        newobj->widget = frame;
-        newobj->container = ui_frame_container(obj, frame);
+        UiContainerX *container = ui_frame_container(obj, frame);
+        uic_object_push_container(obj, container);
     }
-    current->container->add(current->container, frame);
-    uic_obj_add(obj, newobj);
     
     return frame;
 }
 
 UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(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);
+    ct->add(ct, expander, &layout);
+    
+    GtkWidget *sub = ui_subcontainer_create(
+            args->subcontainer,
+            obj, args->spacing,
+            args->columnspacing,
+            args->rowspacing,
+            args->padding);
     if(sub) {
         EXPANDER_SET_CHILD(expander, sub);
     } else {
-        newobj->widget = expander;
-        newobj->container = ui_expander_container(obj, expander);
+        UiContainerX *container = ui_expander_container(obj, expander);
+        uic_object_push_container(obj, container);
     }
-    current->container->add(current->container, expander);
-    uic_obj_add(obj, newobj);
     
     return expander;
 }
 
 
 UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(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);
+    ct->add(ct, sw, &layout);
     
-    UiObject *newobj = uic_object_new(obj, sw);
-    GtkWidget *sub = ui_subcontainer_create(args->subcontainer, newobj, args->spacing, args->columnspacing, args->rowspacing, args->margin);
+    GtkWidget *sub = ui_subcontainer_create(
+            args->subcontainer,
+            obj, args->spacing,
+            args->columnspacing,
+            args->rowspacing,
+            args->padding);
     if(sub) {
         SCROLLEDWINDOW_SET_CHILD(sw, sub);
     } else {
-        newobj->widget = sw;
-        newobj->container = ui_scrolledwindow_container(obj, sw);
+        UiContainerX *container = ui_scrolledwindow_container(obj, sw);
+        uic_object_push_container(obj, container);
     }
     
-    uic_obj_add(obj, newobj);
-    
     return sw;
 }
 
@@ -741,7 +727,7 @@
 UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) {
     UiGtkTabView *data = malloc(sizeof(UiGtkTabView));
     memset(data, 0, sizeof(UiGtkTabView));
-    data->margin = args->margin;
+    data->padding = args->padding;
     data->spacing = args->spacing;
     data->columnspacing = args->columnspacing;
     data->rowspacing = args->rowspacing;
@@ -801,9 +787,8 @@
         }
     }
     
-    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);
+        UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
         UiInteger *i = var->value;
         i->get = getfunc;
         i->set = setfunc;
@@ -812,29 +797,56 @@
     
     g_object_set_data(G_OBJECT(widget), "ui_tabview", data);
     data->widget = data_widget;
-    data->subcontainer = args->subcontainer;
-    
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, widget);
     
-    UiObject *newobj = uic_object_new(obj, widget);
-    newobj->container = ui_tabview_container(obj, widget);
-    uic_obj_add(obj, newobj);
-    data->obj = newobj;
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
+    
+    UiContainerX *container = ui_tabview_container(obj, widget);
+    uic_object_push_container(obj, container);
     
     return widget;
 }
 
+static GtkWidget* create_tab(UiObject *obj, UiGtkTabView *tabview, const char *title, int tab) {
+    UiContainerX *container;
+    GtkWidget *sub;
+    switch(tabview->subcontainer) {
+        default: {
+            sub = ui_gtk_vbox_new(tabview->spacing);
+            container = ui_box_container(obj, sub, tabview->subcontainer);
+            break;
+        }
+        case UI_CONTAINER_HBOX: {
+            sub = ui_gtk_hbox_new(tabview->spacing);
+            container = ui_box_container(obj, sub, tabview->subcontainer);
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            sub = ui_create_grid_widget(tabview->columnspacing, tabview->rowspacing);
+            container = ui_grid_container(obj, sub, FALSE, FALSE, FALSE, FALSE);
+            break;
+        }
+    }
+    
+    uic_object_push_container(obj, container);
+    
+    GtkWidget *widget = ui_gtk_set_margin(sub, tabview->padding, 0, 0, 0, 0);
+    tabview->add_tab(tabview->widget, tab, title, widget);
+    
+    return sub;
+}
+
 void ui_tab_create(UiObject* obj, const char* title) {
-    UiObject* current = uic_current_obj(obj);
-    UiGtkTabView *data = ui_widget_get_tabview_data(current->widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; 
+    GtkWidget *tabview = ct->widget;
+    UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
     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;
+    create_tab(obj, data, title, -1);
 }
 
 
@@ -864,31 +876,8 @@
         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, FALSE, FALSE, FALSE, FALSE);
-            break;
-        }
-    }
-    newobj->widget = sub;
-    GtkWidget *widget = ui_box_set_margin(sub, data->margin);
-    
-    data->add_tab(data->widget, tab_index, name, widget);
+    UiObject *newobj = uic_object_new_toplevel();
+    newobj->widget = create_tab(newobj, data, name, tab_index);
     
     return newobj;
 }
@@ -897,20 +886,16 @@
 /* -------------------- Headerbar -------------------- */
 
 static void hb_set_part(UiObject *obj, int part) {
-    UiObject* current = uic_current_obj(obj);
-    GtkWidget *headerbar = current->widget;
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    GtkWidget *headerbar = ct->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);
-    
+    memcpy(hb, ct, sizeof(UiHeaderbarContainer));
     hb->part = part;
+    uic_object_push_container(obj, (UiContainerX*)hb);
 }
 
 void ui_headerbar_start_create(UiObject *obj) {
@@ -926,74 +911,70 @@
 }
 
 UIWIDGET ui_headerbar_fallback_create(UiObject *obj, UiHeaderbarArgs *args) {
-    UiObject *current = uic_current_obj(obj);
-    UiContainer *ct = current->container;
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(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);
+    ct->add(ct, box, &layout);
     
-    UiObject *newobj = uic_object_new(obj, box);
-    newobj->container = ui_headerbar_fallback_container(obj, box);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_headerbar_fallback_container(obj, box);
+    uic_object_push_container(obj, container);
     
     return box;
 }
 
 static void hb_fallback_set_part(UiObject *obj, int part) {
-    UiObject* current = uic_current_obj(obj);
-    GtkWidget *headerbar = current->widget;
+     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    GtkWidget *headerbar = ct->widget;
     
-    UiObject *newobj = uic_object_new(obj, headerbar);
-    newobj->container = ui_headerbar_container(obj, headerbar);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_headerbar_container(obj, headerbar);
+    uic_object_push_container(obj, container);
     
-    UiHeaderbarContainer *hb = (UiHeaderbarContainer*)newobj->container;
+    UiHeaderbarContainer *hb = (UiHeaderbarContainer*)container;
     hb->part = part;
 }
 
-UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) {
+UiContainerX* 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;
+    return (UiContainerX*)ct;
 }
 
-void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_headerbar_fallback_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct;
     BOX_ADD(ct->widget, widget);
 }
 
 #if GTK_CHECK_VERSION(3, 10, 0)
 
-UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) {
+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);
+    UiContainerX *container = ui_headerbar_container(obj, headerbar);
+    uic_object_push_container(obj, container);
     
     return headerbar;    
 }
 
-UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) {
+UiContainerX* 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;
+    return (UiContainerX*)ct;
 }
 
-void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct;
     if(hb->part == 0) {
         UI_HEADERBAR_PACK_START(ct->widget, widget);
@@ -1028,12 +1009,11 @@
     }
     
     GtkWidget *box = ui_gtk_vbox_new(args->spacing);
-    ui_box_set_margin(box, args->margin);
+    ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
     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);
+    UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_object_push_container(obj, container);
     
     return box;
 }
@@ -1042,12 +1022,11 @@
     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);
+    ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
     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);
+    UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_object_push_container(obj, container);
     
     return box;
 }
@@ -1063,12 +1042,11 @@
     
     GtkWidget *box = ui_gtk_vbox_new(args->spacing);
     ui_set_name_and_style(box, args->name, args->style_class);
-    ui_box_set_margin(box, args->margin);
+    ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
     BOX_ADD_EXPAND(pbox, box);
     
-    UiObject *newobj = uic_object_new(obj, box);
-    newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_object_push_container(obj, container);
     
     return box;
 }
@@ -1108,12 +1086,11 @@
 }
 
 static UIWIDGET splitpane_create(UiObject *obj, UiOrientation orientation, UiSplitPaneArgs *args) {
-    UiObject* current = uic_current_obj(obj);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     GtkWidget *pane0 = create_paned(orientation);
-    
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, pane0);
+    ct->add(ct, pane0, &layout);
     
     int max = args->max_panes == 0 ? 2 : args->max_panes;
     
@@ -1134,19 +1111,18 @@
                 strdup(args->position_property));
     }
     
-    UiObject *newobj = uic_object_new(obj, pane0);
-    newobj->container = ui_splitpane_container(obj, pane0, orientation, max, args->initial_position);
-    uic_obj_add(obj, newobj);
+    UiSplitPane *splitpane = ui_create_splitpane_data(pane0, orientation, max, args->initial_position);
+    UiContainerX *container = ui_splitpane_container(obj, pane0, splitpane);
+    uic_object_push_container(obj, container);
     
-    g_object_set_data(G_OBJECT(pane0), "ui_splitpane", newobj->container);
+    g_object_set_data(G_OBJECT(pane0), "ui_splitpane", splitpane);
     
-    UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER);
+    UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
     if(var) {
-        UiSplitPaneContainer *s = (UiSplitPaneContainer*)newobj->container;
         UiInteger *i = var->value;
-        s->initial_position = i->value;
+        splitpane->initial_position = i->value;
         
-        i->obj = s;
+        i->obj = splitpane;
         i->get = ui_splitpane_get;
         i->set = ui_splitpane_set;
     }
@@ -1162,20 +1138,27 @@
     return splitpane_create(obj, UI_VERTICAL, args);
 }
 
-UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max, int init) {
-    UiSplitPaneContainer *ct = ui_calloc(obj->ctx, 1, sizeof(UiSplitPaneContainer));
-    ct->container.widget = pane;
-    ct->container.add = ui_splitpane_container_add;
+UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init) {
+    UiSplitPane *ct = malloc(sizeof(UiSplitPane));
     ct->current_pane = pane;
     ct->orientation = orientation;
     ct->max = max;
     ct->initial_position = init;
     ct->children = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
-    return (UiContainer*)ct;
+    return ct;
 }
 
-void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget) {
-    UiSplitPaneContainer *s = (UiSplitPaneContainer*)ct;
+UiContainerX* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiSplitPane *data) {
+    UiSplitPaneContainer *ct = ui_calloc(obj->ctx, 1, sizeof(UiSplitPaneContainer));
+    ct->container.widget = pane;
+    ct->container.add = ui_splitpane_container_add;
+    ct->splitpane = data;
+    return (UiContainerX*)ct;
+}
+
+void ui_splitpane_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    UiSplitPaneContainer *sct = (UiSplitPaneContainer*)ct;
+    UiSplitPane *s = sct->splitpane;
     
     if(s->nchildren >= s->max) {
         fprintf(stderr, "splitpane: maximum number of children reached\n");
@@ -1207,25 +1190,25 @@
 }
 
 int64_t ui_splitpane_get(UiInteger *i) {
-    UiSplitPaneContainer *s = i->obj;
-    i->value = gtk_paned_get_position(GTK_PANED(s->container.widget));
+    UiSplitPane *s = i->obj;
+    i->value = gtk_paned_get_position(GTK_PANED(s->current_pane));
     return i->value;
 }
 
 void ui_splitpane_set(UiInteger *i, int64_t value) {
-    UiSplitPaneContainer *s = i->obj;
+    UiSplitPane *s = i->obj;
     i->value = value;
-    gtk_paned_set_position(GTK_PANED(s->container.widget), (int)value);
+    gtk_paned_set_position(GTK_PANED(s->current_pane), (int)value);
 }
 
 UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible) {
-    UiSplitPaneContainer *ct = g_object_get_data(G_OBJECT(splitpane), "ui_splitpane");
-    if(!ct) {
+    UiSplitPane *s = g_object_get_data(G_OBJECT(splitpane), "ui_splitpane");
+    if(!s) {
         fprintf(stderr, "UI Error: not a splitpane\n");
         return;
     }
     
-    GtkWidget *w = cxListAt(ct->children, child_index);
+    GtkWidget *w = cxListAt(s->children, child_index);
     if(w) {
         gtk_widget_set_visible(w, visible);
     }
@@ -1279,13 +1262,12 @@
         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);
+            UiLayout layout = {0};
+            ui_box_container_add(ct->container, item_obj->widget, &layout);
         } else {
             // create new widget and object for this list element
-            CxMempool *mp = cxMempoolCreateSimple(256);
-            const CxAllocator *a = mp->allocator;
-            UiObject *obj = cxCalloc(a, 1, sizeof(UiObject));
-            obj->ctx = uic_context(obj, mp);
+            UiObject *obj = uic_object_new_toplevel();
+            obj->ctx->parent = ct->parent->ctx;
             obj->window = NULL;
             obj->widget = ui_subcontainer_create(
                     ct->subcontainer,
@@ -1294,7 +1276,8 @@
                     ct->columnspacing,
                     ct->rowspacing,
                     ct->margin);
-            ui_box_container_add(ct->container, obj->widget);
+            UiLayout layout = {0};
+            ui_box_container_add(ct->container, obj->widget, &layout);
             if(ct->create_ui) {
                 ct->create_ui(obj, index, elm, ct->userdata);
             }
@@ -1316,19 +1299,17 @@
 }
 
 UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) {
-    UiObject *current = uic_current_obj(obj);
-    UiContainer *ct = current->container;
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(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);
+    ct->add(ct, box, &layout);
     
     UiGtkItemListContainer *container = malloc(sizeof(UiGtkItemListContainer));
     container->parent = obj;
     container->widget = box;
-    container->container = ui_box_container(current, box, args->container);
+    container->container = (UiContainerPrivate*)ui_box_container(obj, box, args->container);
     container->create_ui = args->create_ui;
     container->userdata = args->userdata;
     container->subcontainer = args->subcontainer;
@@ -1341,7 +1322,7 @@
     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);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_LIST);
     if(var) {
         UiList *list = var->value;
         list->obj = container;
@@ -1358,56 +1339,3 @@
 }
 
 
-
-/*
- * -------------------- Layout Functions --------------------
- * 
- * functions for setting layout attributes for the current container
- *
- */
-
-void ui_layout_fill(UiObject *obj, UiBool fill) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.fill = fill;
-}
-
-void ui_layout_hexpand(UiObject *obj, UiBool expand) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.hexpand = expand;
-}
-
-void ui_layout_vexpand(UiObject *obj, UiBool expand) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.vexpand = expand;
-}
-
-void ui_layout_hfill(UiObject *obj, UiBool fill) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.hfill = fill;
-}
-
-void ui_layout_vfill(UiObject *obj, UiBool fill) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.vfill = fill;
-}
-
-UIEXPORT void ui_layout_override_defaults(UiObject *obj, UiBool d) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.override_defaults = d;
-}
-
-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) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.newline = TRUE;
-}
-

mercurial