UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 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 #import "GridLayout.h" 30 31 32 33 @implementation GridLayout 34 35 @synthesize container = _container; 36 37 - (GridLayout*)init { 38 self = [super init]; 39 _columnspacing = 0; 40 _rowspacing = 0; 41 _children = cxArrayListCreateSimple(sizeof(GridElm), 32); 42 _preferredSize.width = -1; 43 _preferredSize.height = -1; 44 45 return self; 46 } 47 48 /* 49 - (void) layout { 50 [super layout]; 51 52 NSRect r1 = _test.frame; 53 NSSize s1 = _test.intrinsicContentSize; 54 NSEdgeInsets e1 = _test.alignmentRectInsets; 55 56 } 57 */ 58 59 - (BOOL)isFlipped { 60 return YES; 61 } 62 63 - (void) layout { 64 int ncols = _cols+1; 65 int nrows = _rows+1; 66 67 GridDef *cols = calloc(ncols, sizeof(GridDef)); 68 GridDef *rows = calloc(nrows, sizeof(GridDef)); 69 70 //NSRect viewFrame = self.frame; 71 NSRect viewFrame = self.bounds; 72 73 int colspacing = _columnspacing; 74 int rowspacing = _rowspacing; 75 76 int span_max = 1; 77 for(int r=0;r<2;r++) { 78 CxIterator i = cxListIterator(_children); 79 cx_foreach(GridElm *, elm, i) { 80 int x = elm->x; 81 int y = elm->y; 82 GridDef *col = &cols[x]; 83 GridDef *row = &rows[y]; 84 85 NSSize size = elm->view.intrinsicContentSize; 86 NSSize size2 = elm->view.fittingSize; 87 if(size.width == NSViewNoIntrinsicMetric) { 88 size.width = size2.width; 89 } 90 if(size.height == NSViewNoIntrinsicMetric) { 91 size.height = size2.height; 92 } 93 if(size.width != NSViewNoIntrinsicMetric) { 94 CGFloat width = size.width + elm->margin.left + elm->margin.right; 95 if(width > cols[elm->x].preferred_size && elm->colspan <= 1 && span_max == 1) { 96 cols[elm->x].preferred_size = width; 97 } 98 elm->preferred_width = width; 99 } 100 if(size.height != NSViewNoIntrinsicMetric) { 101 CGFloat height = size.height + elm->margin.top + elm->margin.bottom; 102 if(height > rows[elm->y].preferred_size && elm->rowspan <= 1 && span_max == 1) { 103 rows[elm->y].preferred_size = height; 104 } 105 elm->preferred_height = height; 106 } 107 108 109 if(elm->rowspan > span_max || elm->colspan > span_max) { 110 continue; 111 } 112 113 int end_col = x+elm->colspan; 114 if(end_col > ncols) { 115 end_col = ncols; 116 } 117 int end_row = y+elm->rowspan; 118 if(end_row > nrows) { 119 end_row = nrows; 120 } 121 122 // are all columns in the span > preferred_width? 123 if(elm->colspan > 1) { 124 int span_width = 0; 125 GridDef *last_col = col; 126 for(int c=x;c<end_col;c++) { 127 span_width += cols[c].size; 128 last_col = &cols[c]; 129 } 130 if(span_width < elm->preferred_width) { 131 last_col->size += elm->preferred_width - span_width; 132 } 133 } 134 // are all rows in the span > preferred_height? 135 if(elm->rowspan > 1) { 136 int span_height = 0; 137 GridDef *last_row = row; 138 for(int c=x;c<end_row;c++) { 139 span_height += rows[c].size; 140 last_row = &rows[c]; 141 } 142 if(span_height < elm->preferred_height) { 143 last_row->size += elm->preferred_height - span_height; 144 } 145 } 146 147 if(elm->hexpand) { 148 if(elm->colspan > 1) { 149 // check if any column in the span is expanding 150 // if not, make the last column expanding 151 GridDef *last_col = col; 152 for(int c=x;c<end_col;c++) { 153 last_col = &cols[c]; 154 if(last_col->expand) { 155 break; 156 } 157 } 158 last_col->expand = TRUE; 159 } else { 160 col->expand = TRUE; 161 } 162 } 163 if(elm->vexpand) { 164 if(elm->rowspan > 1) { 165 // same as colspan 166 GridDef *last_row = row; 167 for(int c=x;c<nrows;c++) { 168 last_row = &rows[c]; 169 if(last_row->expand) { 170 break; 171 } 172 } 173 last_row->expand = TRUE; 174 } else { 175 row->expand = TRUE; 176 } 177 } 178 } 179 span_max = 50000; // not sure if this is unreasonable low or high 180 } 181 182 183 int col_ext = 0; 184 int row_ext = 0; 185 186 int preferred_width = 0; 187 int preferred_height = 0; 188 for(int j=0;j<ncols;j++) { 189 preferred_width += cols[j].preferred_size; 190 if(cols[j].expand) { 191 col_ext++; 192 } 193 } 194 for(int j=0;j<nrows;j++) { 195 preferred_height += rows[j].preferred_size; 196 if(rows[j].expand) { 197 row_ext++; 198 } 199 } 200 if(ncols > 0) { 201 preferred_width += (ncols-1) * colspacing; 202 } 203 if(nrows > 0) { 204 preferred_height += (nrows-1) * rowspacing; 205 } 206 207 _preferredSize.width = preferred_width; 208 _preferredSize.height = preferred_height; 209 210 211 int hremaining = viewFrame.size.width - preferred_width; 212 int vremaining = viewFrame.size.height - preferred_height; 213 int hext = hremaining/col_ext; 214 int vext = vremaining/row_ext; 215 216 for(int j=0;j<ncols;j++) { 217 GridDef *col = &cols[j]; 218 if(col->expand) { 219 col->size = col->preferred_size + hext; 220 } else { 221 col->size = col->preferred_size; 222 } 223 } 224 for(int j=0;j<nrows;j++) { 225 GridDef *row = &rows[j]; 226 if(row->expand) { 227 row->size = row->preferred_size + vext; 228 } else { 229 row->size = row->preferred_size; 230 } 231 } 232 233 int pos = 0; 234 for(int j=0;j<ncols;j++) { 235 cols[j].pos = pos; 236 pos += cols[j].size + colspacing; 237 } 238 pos = 0; 239 for(int j=0;j<nrows;j++) { 240 rows[j].pos = pos; 241 pos += rows[j].size + rowspacing; 242 } 243 244 CxIterator i = cxListIterator(_children); 245 cx_foreach(GridElm *, elm, i) { 246 //NSSize size = elm->view.intrinsicContentSize; 247 GridDef *col = &cols[elm->x]; 248 GridDef *row = &rows[elm->y]; 249 250 NSRect frame; 251 if(elm->hfill) { 252 if(elm->colspan > 1) { 253 int cwidth = 0; 254 int end_col = elm->x + elm->colspan; 255 if(end_col > ncols) { 256 end_col = ncols; 257 } 258 int real_span = 0; 259 for(int c=elm->x;c<end_col;c++) { 260 cwidth += cols[c].size; 261 real_span++; 262 } 263 if(real_span > 0) { 264 cwidth += (real_span-1) * colspacing; 265 } 266 frame.size.width = cwidth; 267 } else { 268 frame.size.width = col->size; 269 } 270 } else { 271 frame.size.width = elm->preferred_width; 272 } 273 frame.size.width -= elm->margin.left + elm->margin.right; 274 if(elm->vfill) { 275 if(elm->rowspan > 1) { 276 int rheight = 0; 277 int end_row = elm->y + elm->rowspan; 278 if(end_row > nrows) { 279 end_row = nrows; 280 } 281 int real_span = 0; 282 for(int r=elm->y;r<end_row;r++) { 283 rheight += rows[r].size; 284 real_span++; 285 } 286 if(real_span > 0) { 287 rheight += (real_span-1) * rowspacing; 288 } 289 frame.size.height = rheight; 290 } 291 frame.size.height = row->size; 292 } else { 293 frame.size.height = elm->preferred_height; 294 } 295 frame.size.height -= elm->margin.top + elm->margin.bottom; 296 297 frame.origin.x = col->pos + elm->margin.left; 298 frame.origin.y = row->pos + elm->margin.top; 299 NSRect viewFrame = [elm->view frameForAlignmentRect:frame]; 300 elm->view.frame = viewFrame; 301 } 302 303 free(cols); 304 free(rows); 305 } 306 307 308 - (NSSize)intrinsicContentSize { 309 if(_preferredSize.width == -1) { 310 [self layout]; 311 } 312 return self.preferredSize; 313 } 314 315 - (void) addView:(NSView*)view layout:(UiLayout*)layout { 316 _preferredSize.width = -1; 317 _preferredSize.height = -1; 318 319 if(self.container != nil && self.container->newline) { 320 _y++; 321 _x = 0; 322 self.container->newline = FALSE; 323 } 324 325 GridElm elm; 326 elm.x = _x; 327 elm.y = _y; 328 elm.margin = NSEdgeInsetsMake(layout->margin_top, layout->margin_left, layout->margin_bottom, layout->margin_right); 329 elm.colspan = layout->colspan; 330 elm.rowspan = layout->rowspan; 331 if(layout->fill) { 332 elm.hfill = TRUE; 333 elm.vfill = TRUE; 334 elm.hexpand = TRUE; 335 elm.vexpand = TRUE; 336 } else { 337 elm.hfill = layout->hfill; 338 elm.vfill = layout->vfill; 339 elm.hexpand = layout->hexpand; 340 elm.vexpand = layout->vexpand; 341 } 342 elm.view = view; 343 cxListAdd(_children, &elm); 344 345 [self addSubview:view]; 346 self.needsLayout = YES; 347 348 if(_x > _cols) { 349 _cols = _x; 350 } 351 if(_y > _rows) { 352 _rows = _y; 353 } 354 _x++; 355 } 356 357 - (void) dealloc { 358 cxListFree(_children); 359 } 360 361 @end 362