UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2025 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "grid.h" 30 31 #include "../../ucx/cx/array_list.h" 32 #include "../common/context.h" 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 37 UiGridLayout* ui_grid_layout_create(const CxAllocator *a, short columnspacing, short rowspacing) { 38 UiGridLayout *grid = cxZalloc(a, sizeof(UiGridLayout)); 39 grid->widgets = cxArrayListCreate(a, NULL, sizeof(GridElm), 32); 40 grid->columnspacing = columnspacing; 41 grid->rowspacing = rowspacing; 42 return grid; 43 } 44 45 void ui_grid_add_widget( 46 UiGridLayout *grid, 47 short x, 48 short y, 49 W32Widget *widget, 50 GridLayoutInfo *layout) 51 { 52 // add the widget 53 GridElm elm; 54 elm.widget = widget; 55 elm.gridx = x; 56 elm.gridy = y; 57 elm.layout = *layout; 58 cxListAdd(grid->widgets, &elm); 59 60 // adjust max col/row count 61 if (x > grid->max_column) { 62 grid->max_column = x; 63 } 64 if (y > grid->max_row) { 65 grid->max_row = y; 66 } 67 } 68 69 void ui_grid_layout(UiGridLayout *grid, int width, int height) { 70 if (width == 0 || height == 0) { 71 return; 72 } 73 74 int ncols = grid->max_column+1; 75 int nrows = grid->max_row+1; 76 77 GridDef *cols = calloc(ncols, sizeof(GridDef)); 78 GridDef *rows = calloc(nrows, sizeof(GridDef)); 79 80 int colspacing = grid->columnspacing; 81 int rowspacing = grid->rowspacing; 82 83 int span_max = 1; 84 for(int r=0;r<2;r++) { 85 CxIterator i = cxListIterator(grid->widgets); 86 cx_foreach(GridElm *, elm, i) { 87 int x = elm->gridx; 88 int y = elm->gridy; 89 GridDef *col = &cols[x]; 90 GridDef *row = &rows[y]; 91 92 W32Size size = w32_widget_get_preferred_size(elm->widget); 93 elm->layout.preferred_width = size.width; 94 elm->layout.preferred_height = size.height; 95 96 int elm_width = size.width + elm->layout.margin.left + elm->layout.margin.right; 97 if(elm_width > cols[elm->gridx].preferred_size && elm->layout.colspan <= 1 && span_max == 1) { 98 cols[elm->gridx].preferred_size = elm_width; 99 } 100 int elm_height = size.height + elm->layout.margin.top + elm->layout.margin.bottom; 101 if(elm_height > rows[elm->gridy].preferred_size && elm->layout.rowspan <= 1 && span_max == 1) { 102 rows[elm->gridy].preferred_size = elm_height; 103 } 104 105 if(elm->layout.rowspan > span_max || elm->layout.colspan > span_max) { 106 continue; 107 } 108 109 int end_col = x+elm->layout.colspan; 110 if(end_col > ncols) { 111 end_col = ncols; 112 } 113 int end_row = y+elm->layout.rowspan; 114 if(end_row > nrows) { 115 end_row = nrows; 116 } 117 118 // are all columns in the span > preferred_width? 119 if(elm->layout.colspan > 1) { 120 int span_width = 0; 121 GridDef *last_col = col; 122 for(int c=x;c<end_col;c++) { 123 span_width += cols[c].size; 124 last_col = &cols[c]; 125 } 126 if(span_width < elm->layout.preferred_width) { 127 last_col->size += elm->layout.preferred_width - span_width; 128 } 129 } 130 131 // are all rows in the span > preferred_height? 132 if(elm->layout.rowspan > 1) { 133 int span_height = 0; 134 GridDef *last_row = row; 135 for(int c=x;c<end_row;c++) { 136 span_height += rows[c].size; 137 last_row = &rows[c]; 138 } 139 if(span_height < elm->layout.preferred_height) { 140 last_row->size += elm->layout.preferred_height - span_height; 141 } 142 } 143 144 if(elm->layout.hexpand) { 145 if(elm->layout.colspan > 1) { 146 // check if any column in the span is expanding 147 // if not, make the last column expanding 148 GridDef *last_col = col; 149 for(int c=x;c<end_col;c++) { 150 last_col = &cols[c]; 151 if(last_col->expand) { 152 break; 153 } 154 } 155 last_col->expand = TRUE; 156 } else { 157 col->expand = TRUE; 158 } 159 } 160 if(elm->layout.vexpand) { 161 if(elm->layout.rowspan > 1) { 162 // same as colspan 163 GridDef *last_row = row; 164 for(int c=x;c<nrows;c++) { 165 last_row = &rows[c]; 166 if(last_row->expand) { 167 break; 168 } 169 } 170 last_row->expand = TRUE; 171 } else { 172 row->expand = TRUE; 173 } 174 } 175 } 176 span_max = 50000; // not sure if this is unreasonable low or high 177 } 178 179 int col_ext = 0; 180 int row_ext = 0; 181 182 int preferred_width = 0; 183 int preferred_height = 0; 184 for(int j=0;j<ncols;j++) { 185 preferred_width += cols[j].preferred_size; 186 if(cols[j].expand) { 187 col_ext++; 188 } 189 } 190 for(int j=0;j<nrows;j++) { 191 preferred_height += rows[j].preferred_size; 192 if(rows[j].expand) { 193 row_ext++; 194 } 195 } 196 if(ncols > 0) { 197 preferred_width += (ncols-1) * colspacing; 198 } 199 if(nrows > 0) { 200 preferred_height += (nrows-1) * rowspacing; 201 } 202 203 grid->preferred_width = preferred_width; 204 grid->preferred_height = preferred_height; 205 206 int hremaining = width - preferred_width; 207 int vremaining = height - preferred_height; 208 int hext = col_ext > 0 ? hremaining/col_ext : 0; 209 int vext = row_ext > 0 ? vremaining/row_ext : 0; 210 211 for(int j=0;j<ncols;j++) { 212 GridDef *col = &cols[j]; 213 if(col->expand) { 214 col->size = col->preferred_size + hext; 215 } else { 216 col->size = col->preferred_size; 217 } 218 } 219 for(int j=0;j<nrows;j++) { 220 GridDef *row = &rows[j]; 221 if(row->expand) { 222 row->size = row->preferred_size + vext; 223 } else { 224 row->size = row->preferred_size; 225 } 226 } 227 228 int pos = 0; 229 for(int j=0;j<ncols;j++) { 230 cols[j].pos = pos; 231 pos += cols[j].size + colspacing; 232 } 233 pos = 0; 234 for(int j=0;j<nrows;j++) { 235 rows[j].pos = pos; 236 pos += rows[j].size + rowspacing; 237 } 238 239 CxIterator i = cxListIterator(grid->widgets); 240 cx_foreach(GridElm *, elm, i) { 241 GridDef *col = &cols[elm->gridx]; 242 GridDef *row = &rows[elm->gridy]; 243 244 int child_width = 0; 245 int child_height = 0; 246 int child_x = 0; 247 int child_y = 0; 248 if(elm->layout.hfill) { 249 if(elm->layout.colspan > 1) { 250 int cwidth = 0; 251 int end_col = elm->gridx + elm->layout.colspan; 252 if(end_col > ncols) { 253 end_col = ncols; 254 } 255 int real_span = 0; 256 for(int c=elm->gridx;c<end_col;c++) { 257 cwidth += cols[c].size; 258 real_span++; 259 } 260 if(real_span > 0) { 261 cwidth += (real_span-1) * colspacing; 262 } 263 child_width = cwidth; 264 } else { 265 child_width = col->size; 266 } 267 child_width -= elm->layout.margin.left + elm->layout.margin.right; 268 } else { 269 child_width = elm->layout.preferred_width; 270 } 271 272 if(elm->layout.vfill) { 273 if(elm->layout.rowspan > 1) { 274 int rheight = 0; 275 int end_row = elm->gridy + elm->layout.rowspan; 276 if(end_row > nrows) { 277 end_row = nrows; 278 } 279 int real_span = 0; 280 for(int r=elm->gridy;r<end_row;r++) { 281 rheight += rows[r].size; 282 real_span++; 283 } 284 if(real_span > 0) { 285 rheight += (real_span-1) * rowspacing; 286 } 287 child_height = rheight; 288 } 289 child_height = row->size - elm->layout.margin.top - elm->layout.margin.bottom; 290 } else { 291 child_height = elm->layout.preferred_height; 292 } 293 294 child_x = col->pos + elm->layout.margin.left; 295 child_y = row->pos + elm->layout.margin.top; 296 SetWindowPos(elm->widget->hwnd, NULL, child_x, child_y, child_width, child_height, SWP_NOZORDER | SWP_NOACTIVATE); 297 InvalidateRect(elm->widget->hwnd, NULL, TRUE); 298 //UpdateWindow(elm->widget->hwnd); 299 if (elm->widget->layout) { 300 elm->widget->layout(elm->widget->layoutmanager, child_width, child_height); 301 } 302 } 303 304 free(cols); 305 free(rows); 306 } 307