diff -r 7b3a3130be44 -r 64ded9f6a6c6 ui/cocoa/GridLayout.m
--- a/ui/cocoa/GridLayout.m	Mon Jan 06 22:22:55 2025 +0100
+++ b/ui/cocoa/GridLayout.m	Tue Feb 25 21:11:00 2025 +0100
@@ -62,54 +62,129 @@
     int ncols = _cols+1;
     int nrows = _rows+1;
     
-    GridDef *coldef = calloc(ncols, sizeof(GridDef));
-    GridDef *rowdef = calloc(nrows, sizeof(GridDef));
+    GridDef *cols = calloc(ncols, sizeof(GridDef));
+    GridDef *rows = calloc(nrows, sizeof(GridDef));
     
     NSRect viewFrame = self.frame;
     
     int colspacing = _columnspacing;
     int rowspacing = _rowspacing;
     
-    CxIterator i = cxListIterator(_children);
-    cx_foreach(GridElm *, elm, i) {
-        NSSize size = elm->view.intrinsicContentSize;
-        NSEdgeInsets alignment = elm->view.alignmentRectInsets;
-        if(size.width != NSViewNoIntrinsicMetric) {
-            CGFloat width = size.width + alignment.left + alignment.right;
-            if(width > coldef[elm->x].preferred_size) {
-                coldef[elm->x].preferred_size = width;
+    int span_max = 1;
+    for(int r=0;r<2;r++) {
+        CxIterator i = cxListIterator(_children);
+        cx_foreach(GridElm *, elm, i) {
+            int x = elm->x;
+            int y = elm->y;
+            GridDef *col = &cols[x];
+            GridDef *row = &rows[y];
+            
+            NSSize size = elm->view.intrinsicContentSize;
+            NSEdgeInsets alignment = elm->view.alignmentRectInsets;
+            if(size.width != NSViewNoIntrinsicMetric) {
+                CGFloat width = size.width + alignment.left + alignment.right;
+                if(width > cols[elm->x].preferred_size && elm->colspan <= 1 && span_max == 1) {
+                    cols[elm->x].preferred_size = width;
+                }
+                elm->preferred_width = width;
+            }
+            if(size.height != NSViewNoIntrinsicMetric) {
+                CGFloat height = size.height + alignment.top + alignment.right;
+                //CGFloat height = size.height;
+                if(height > rows[elm->y].preferred_size && elm->rowspan <= 1 && span_max == 1) {
+                    rows[elm->y].preferred_size = height;
+                }
+                elm->preferred_height = height;
+            }
+            
+            if(elm->rowspan > span_max || elm->colspan > span_max) {
+                continue;
+            }
+            
+            int end_col = x+elm->colspan;
+            if(end_col > ncols) {
+                end_col = ncols;
+            }
+            int end_row = y+elm->rowspan;
+            if(end_row > nrows) {
+                end_row = nrows;
+            }
+            
+            // are all columns in the span > preferred_width?
+            if(elm->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->preferred_width) {
+                    last_col->size += elm->preferred_width - span_width;
+                }
+            }
+            // are all rows in the span > preferred_height?
+            if(elm->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->preferred_height) {
+                    last_row->size += elm->preferred_height - span_height;
+                }
+            }
+            
+            if(elm->hexpand) {
+                if(elm->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->vexpand) {
+                if(elm->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;
+                }
             }
         }
-        if(size.height != NSViewNoIntrinsicMetric) {
-            CGFloat height = size.height + alignment.top + alignment.right;
-            //CGFloat height = size.height;
-            if(height > rowdef[elm->y].preferred_size) {
-                rowdef[elm->y].preferred_size = height;
-            }
-        }
-        
-        if(elm->hexpand) {
-            coldef[elm->x].extend = TRUE;
-        }
-        if(elm->vexpand) {
-            rowdef[elm->y].extend = 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 += coldef[j].preferred_size + colspacing;
-        if(coldef[j].extend) {
+        preferred_width += cols[j].preferred_size + colspacing;
+        if(cols[j].expand) {
             col_ext++;
         }
     }
     for(int j=0;j<nrows;j++) {
-        preferred_height += rowdef[j].preferred_size + rowspacing;
-        if(rowdef[j].extend) {
+        preferred_height += rows[j].preferred_size + rowspacing;
+        if(rows[j].expand) {
             row_ext++;
         }
     }
@@ -124,16 +199,16 @@
     int vext = vremaining/row_ext;
     
     for(int j=0;j<ncols;j++) {
-        GridDef *col = &coldef[j];
-        if(col->extend) {
+        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 = &rowdef[j];
-        if(row->extend) {
+        GridDef *row = &rows[j];
+        if(row->expand) {
             row->size = row->preferred_size + vext;
         } else {
             row->size = row->preferred_size;
@@ -142,32 +217,63 @@
     
     int pos = 0;
     for(int j=0;j<ncols;j++) {
-        coldef[j].pos = pos;
-        pos += coldef[j].size + colspacing;
+        cols[j].pos = pos;
+        pos += cols[j].size + colspacing;
     }
     pos = 0;
     for(int j=0;j<nrows;j++) {
-        rowdef[j].pos = pos;
-        pos += rowdef[j].size + rowspacing;
+        rows[j].pos = pos;
+        pos += rows[j].size + rowspacing;
     }
     
-    i = cxListIterator(_children);
+    CxIterator i = cxListIterator(_children);
     cx_foreach(GridElm *, elm, i) {
         //NSSize size = elm->view.intrinsicContentSize;
-        GridDef *col = &coldef[elm->x];
-        GridDef *row = &rowdef[elm->y];
+        GridDef *col = &cols[elm->x];
+        GridDef *row = &rows[elm->y];
         
         NSEdgeInsets alignment = elm->view.alignmentRectInsets;
         NSRect frame;
-        frame.size.width = col->size;
-        frame.size.height = row->size;
+        if(elm->hfill) {
+            if(elm->colspan > 1) {
+                int cwidth = 0;
+                int end_col = elm->x + elm->colspan;
+                if(end_col > ncols) {
+                    end_col = ncols;
+                }
+                for(int c=elm->x;c<end_col;c++) {
+                    cwidth += cols[c].size;
+                }
+                frame.size.width = cwidth;
+            } else {
+                frame.size.width = col->size;
+            }
+        } else {
+            frame.size.width = elm->preferred_width;
+        }
+        if(elm->vfill) {
+            if(elm->rowspan > 1) {
+                int rheight = 0;
+                int end_row = elm->y + elm->rowspan;
+                if(end_row > nrows) {
+                    end_row = nrows;
+                }
+                for(int r=elm->y;r<end_row;r++) {
+                    rheight += rows[r].size;
+                }
+                frame.size.height = rheight;
+            }
+            frame.size.height = row->size;
+        } else {
+            frame.size.height = elm->preferred_height;
+        }
         frame.origin.x = col->pos - (alignment.left+alignment.right)/2;
         frame.origin.y = viewFrame.size.height - row->pos - frame.size.height + ((alignment.top+alignment.right)/2);
         elm->view.frame = frame;
     }
     
-    free(coldef);
-    free(rowdef);
+    free(cols);
+    free(rows);
 }