ui/win32/grid.c

changeset 112
c3f2f16fa4b8
parent 108
77254bd6dccb
--- a/ui/win32/grid.c	Sat Oct 04 14:54:25 2025 +0200
+++ b/ui/win32/grid.c	Sun Oct 19 21:20:08 2025 +0200
@@ -31,11 +31,12 @@
 #include "../../ucx/cx/array_list.h"
 #include "../common/context.h"
 
-UiGridLayout* ui_grid_container(UiObject *obj, HWND control, short padding, short columnspacing, short rowspacing) {
-    UiGridLayout *grid = cxZalloc(obj->ctx->allocator, sizeof(UiGridLayout));
-    grid->hwnd = control;
-    grid->widgets = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(GridElm), 32);
-    grid->padding = padding;
+#include <stdio.h>
+#include <stdlib.h>
+
+UiGridLayout* ui_grid_layout_create(const CxAllocator *a, short columnspacing, short rowspacing) {
+    UiGridLayout *grid = cxZalloc(a, sizeof(UiGridLayout));
+    grid->widgets = cxArrayListCreate(a, NULL, sizeof(GridElm), 32);
     grid->columnspacing = columnspacing;
     grid->rowspacing = rowspacing;
     return grid;
@@ -48,14 +49,256 @@
     W32Widget *widget,
     GridLayoutInfo *layout)
 {
+    // add the widget
     GridElm elm;
     elm.widget = widget;
-    elm.x = x;
-    elm.y = y;
+    elm.gridx = x;
+    elm.gridy = y;
     elm.layout = *layout;
-    cxListAdd(grid->widgets, elm);
+    cxListAdd(grid->widgets, &elm);
+
+    // adjust max col/row count
+    if (x > grid->max_column) {
+        grid->max_column = x;
+    }
+    if (y > grid->max_row) {
+        grid->max_row = y;
+    }
 }
 
-void ui_grid_layout(UiGridLayout *grid) {
-    // TODO
+void ui_grid_layout(UiGridLayout *grid, int width, int height) {
+    if (width == 0 || height == 0) {
+        return;
+    }
+
+    int ncols = grid->max_column+1;
+    int nrows = grid->max_row+1;
+
+    GridDef *cols = calloc(ncols, sizeof(GridDef));
+    GridDef *rows = calloc(nrows, sizeof(GridDef));
+
+    int colspacing = grid->columnspacing;
+    int rowspacing = grid->rowspacing;
+
+    int span_max = 1;
+    for(int r=0;r<2;r++) {
+        CxIterator i = cxListIterator(grid->widgets);
+        cx_foreach(GridElm *, elm, i) {
+            int x = elm->gridx;
+            int y = elm->gridy;
+            GridDef *col = &cols[x];
+            GridDef *row = &rows[y];
+
+            W32Size size = w32_widget_get_preferred_size(elm->widget);
+            elm->layout.preferred_width = size.width;
+            elm->layout.preferred_height = size.height;
+
+            int elm_width = size.width + elm->layout.margin.left + elm->layout.margin.right;
+            if(elm_width > cols[elm->gridx].preferred_size && elm->layout.colspan <= 1 && span_max == 1) {
+                cols[elm->gridx].preferred_size = elm_width;
+            }
+            int elm_height = size.height + elm->layout.margin.top + elm->layout.margin.bottom;
+            if(elm_height > rows[elm->gridy].preferred_size && elm->layout.rowspan <= 1 && span_max == 1) {
+                rows[elm->gridy].preferred_size = elm_height;
+            }
+
+            if(elm->layout.rowspan > span_max || elm->layout.colspan > span_max) {
+                continue;
+            }
+
+            int end_col = x+elm->layout.colspan;
+            if(end_col > ncols) {
+                end_col = ncols;
+            }
+            int end_row = y+elm->layout.rowspan;
+            if(end_row > nrows) {
+                end_row = nrows;
+            }
+
+            // are all columns in the span > preferred_width?
+            if(elm->layout.colspan > 1) {
+                int span_width = 0;
+                GridDef *last_col = col;
+                for(int c=x;c<end_col;c++) {
+                    span_width += cols[c].size;
+                    last_col = &cols[c];
+                }
+                if(span_width < elm->layout.preferred_width) {
+                    last_col->size += elm->layout.preferred_width - span_width;
+                }
+            }
+
+            // are all rows in the span > preferred_height?
+            if(elm->layout.rowspan > 1) {
+                int span_height = 0;
+                GridDef *last_row = row;
+                for(int c=x;c<end_row;c++) {
+                    span_height += rows[c].size;
+                    last_row = &rows[c];
+                }
+                if(span_height < elm->layout.preferred_height) {
+                    last_row->size += elm->layout.preferred_height - span_height;
+                }
+            }
+
+            if(elm->layout.hexpand) {
+                if(elm->layout.colspan > 1) {
+                    // check if any column in the span is expanding
+                    // if not, make the last column expanding
+                    GridDef *last_col = col;
+                    for(int c=x;c<end_col;c++) {
+                        last_col = &cols[c];
+                        if(last_col->expand) {
+                            break;
+                        }
+                    }
+                    last_col->expand = TRUE;
+                } else {
+                    col->expand = TRUE;
+                }
+            }
+            if(elm->layout.vexpand) {
+                if(elm->layout.rowspan > 1) {
+                    // same as colspan
+                    GridDef *last_row = row;
+                    for(int c=x;c<nrows;c++) {
+                        last_row = &rows[c];
+                        if(last_row->expand) {
+                            break;
+                        }
+                    }
+                    last_row->expand = TRUE;
+                } else {
+                    row->expand = TRUE;
+                }
+            }
+        }
+        span_max = 50000; // not sure if this is unreasonable low or high
+    }
+
+    int col_ext = 0;
+    int row_ext = 0;
+
+    int preferred_width = 0;
+    int preferred_height = 0;
+    for(int j=0;j<ncols;j++) {
+        preferred_width += cols[j].preferred_size;
+        if(cols[j].expand) {
+            col_ext++;
+        }
+    }
+    for(int j=0;j<nrows;j++) {
+        preferred_height += rows[j].preferred_size;
+        if(rows[j].expand) {
+            row_ext++;
+        }
+    }
+    if(ncols > 0) {
+        preferred_width += (ncols-1) * colspacing;
+    }
+    if(nrows > 0) {
+        preferred_height += (nrows-1) * rowspacing;
+    }
+
+    grid->preferred_width = preferred_width;
+    grid->preferred_height = preferred_height;
+
+    int hremaining = width - preferred_width;
+    int vremaining = height - preferred_height;
+    int hext = col_ext > 0 ? hremaining/col_ext : 0;
+    int vext = row_ext > 0 ? vremaining/row_ext : 0;
+
+    for(int j=0;j<ncols;j++) {
+        GridDef *col = &cols[j];
+        if(col->expand) {
+            col->size = col->preferred_size + hext;
+        } else {
+            col->size = col->preferred_size;
+        }
+    }
+    for(int j=0;j<nrows;j++) {
+        GridDef *row = &rows[j];
+        if(row->expand) {
+            row->size = row->preferred_size + vext;
+        } else {
+            row->size = row->preferred_size;
+        }
+    }
+
+    int pos = 0;
+    for(int j=0;j<ncols;j++) {
+        cols[j].pos = pos;
+        pos += cols[j].size + colspacing;
+    }
+    pos = 0;
+    for(int j=0;j<nrows;j++) {
+        rows[j].pos = pos;
+        pos += rows[j].size + rowspacing;
+    }
+
+    CxIterator i = cxListIterator(grid->widgets);
+    cx_foreach(GridElm *, elm, i) {
+        GridDef *col = &cols[elm->gridx];
+        GridDef *row = &rows[elm->gridy];
+
+        int child_width = 0;
+        int child_height = 0;
+        int child_x = 0;
+        int child_y = 0;
+        if(elm->layout.hfill) {
+            if(elm->layout.colspan > 1) {
+                int cwidth = 0;
+                int end_col = elm->gridx + elm->layout.colspan;
+                if(end_col > ncols) {
+                    end_col = ncols;
+                }
+                int real_span = 0;
+                for(int c=elm->gridx;c<end_col;c++) {
+                    cwidth += cols[c].size;
+                    real_span++;
+                }
+                if(real_span > 0) {
+                    cwidth += (real_span-1) * colspacing;
+                }
+                child_width = cwidth;
+            } else {
+                child_width = col->size;
+            }
+            child_width -= elm->layout.margin.left + elm->layout.margin.right;
+        } else {
+            child_width = elm->layout.preferred_width;
+        }
+
+        if(elm->layout.vfill) {
+            if(elm->layout.rowspan > 1) {
+                int rheight = 0;
+                int end_row = elm->gridy + elm->layout.rowspan;
+                if(end_row > nrows) {
+                    end_row = nrows;
+                }
+                int real_span = 0;
+                for(int r=elm->gridy;r<end_row;r++) {
+                    rheight += rows[r].size;
+                    real_span++;
+                }
+                if(real_span > 0) {
+                    rheight += (real_span-1) * rowspacing;
+                }
+                child_height = rheight;
+            }
+            child_height = row->size - elm->layout.margin.top - elm->layout.margin.bottom;
+        } else {
+            child_height = elm->layout.preferred_height;
+        }
+
+        child_x = col->pos + elm->layout.margin.left;
+        child_y = row->pos + elm->layout.margin.top;
+        SetWindowPos(elm->widget->hwnd, NULL, child_x, child_y, child_width, child_height, SWP_NOZORDER);
+        if (elm->widget->layout) {
+            elm->widget->layout(elm->widget->layoutmanager, child_width, child_height);
+        }
+    }
+
+    free(cols);
+    free(rows);
 }

mercurial