UNPKG

40.7 kBJavaScriptView Raw
1/*
2* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
3*/
4/*
5* This is an extension and not part of the main GoJS library.
6* Note that the API for this class may change with any version, even point releases.
7* If you intend to use an extension in production, you should copy the code to your own source directory.
8* Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
9* See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
10*/
11import * as go from '../release/go-module.js';
12/**
13 * This {@link Layout} positions non-Link Parts into a table according to the values of
14 * {@link GraphObject#row}, {@link GraphObject#column}, {@link GraphObject#rowSpan}, {@link GraphObject#columnSpan},
15 * {@link GraphObject#alignment}, {@link GraphObject#stretch}.
16 * If the value of GraphObject.stretch is not {@link GraphObject.None}, the Part will be sized
17 * according to the available space in the cell(s).
18 *
19 * You can specify constraints for whole rows or columns by calling
20 * {@link #getRowDefinition} or {@link #getColumnDefinition} and setting one of the following properties:
21 * {@link RowColumnDefinition#alignment}, {@link RowColumnDefinition#height}, {@link RowColumnDefinition#width},
22 * {@link RowColumnDefinition#maximum}, {@link RowColumnDefinition#minimum}, {@link RowColumnDefinition#stretch}.
23 *
24 * The {@link #defaultAlignment} and {@link #defaultStretch} properties apply to all parts if not specified
25 * on the individual Part or in the corresponding row or column definition.
26 *
27 * At the current time, there is no support for separator lines
28 * ({@link RowColumnDefinition#separatorStroke}, {@link RowColumnDefinition#separatorStrokeWidth},
29 * and {@link RowColumnDefinition#separatorDashArray} properties)
30 * nor background ({@link RowColumnDefinition#background} and {@link RowColumnDefinition#coversSeparators} properties).
31 * There is no support for {@link RowColumnDefinition#sizing}, either.
32 *
33 * If you want to experiment with this extension, try the <a href="../../extensionsJSM/Table.html">Table Layout</a> sample.
34 * @category Layout Extension
35 */
36export class TableLayout extends go.Layout {
37 constructor() {
38 super(...arguments);
39 this._defaultAlignment = go.Spot.Default;
40 this._defaultStretch = go.GraphObject.Default;
41 this._rowDefs = [];
42 this._colDefs = [];
43 }
44 /**
45 * Gets or sets the alignment to use by default for Parts in rows (vertically) and in columns (horizontally).
46 *
47 * The default value is {@link Spot.Default}.
48 * Setting this property does not raise any events.
49 */
50 get defaultAlignment() { return this._defaultAlignment; }
51 set defaultAlignment(val) { this._defaultAlignment = val; }
52 /**
53 * Gets or sets whether Parts should be stretched in rows (vertically) and in columns (horizontally).
54 *
55 * The default value is {@link GraphObject.Default}.
56 * Setting this property does not raise any events.
57 */
58 get defaultStretch() { return this._defaultStretch; }
59 set defaultStretch(val) { this._defaultStretch = val; }
60 /**
61 * This read-only property returns the number of rows in this TableLayout.
62 * This value is only valid after the layout has been performed.
63 */
64 get rowCount() { return this._rowDefs.length; }
65 /**
66 * This read-only property returns the number of columns in this TableLayout.
67 * This value is only valid after the layout has been performed.
68 */
69 get columnCount() { return this._colDefs.length; }
70 /**
71 * Copies properties to a cloned Layout.
72 */
73 cloneProtected(copy) {
74 super.cloneProtected(copy);
75 copy._defaultAlignment = this._defaultAlignment;
76 copy._defaultStretch = this._defaultStretch;
77 for (let i = 0; i < this._rowDefs.length; i++) {
78 const def = this._rowDefs[i];
79 copy._rowDefs.push(def !== undefined ? def.copy() : def);
80 }
81 for (let i = 0; i < this._colDefs.length; i++) {
82 const def = this._colDefs[i];
83 copy._colDefs.push(def !== undefined ? def.copy() : def);
84 }
85 }
86 /**
87 * Gets the {@link RowColumnDefinition} for a particular row in this TableLayout.
88 * If you ask for the definition of a row at or beyond the {@link #rowCount},
89 * it will automatically create one and return it.
90 * @param {number} idx the non-negative zero-based integer row index.
91 * @return {RowColumnDefinition}
92 */
93 getRowDefinition(idx) {
94 if (idx < 0)
95 throw new Error('Row index must be non-negative, not: ' + idx);
96 idx = Math.round(idx);
97 const defs = this._rowDefs;
98 let d = defs[idx];
99 if (d === undefined) {
100 d = new go.RowColumnDefinition();
101 // .panel remains null
102 d.isRow = true;
103 d.index = idx;
104 defs[idx] = d;
105 }
106 return d;
107 }
108 /**
109 * Returns the row at a given y-coordinate in document coordinates.
110 * This information is only valid when this layout has been performed and {@link Layout#isValidLayout} is true.
111 *
112 * If the point is above row 0, this method returns -1.
113 * If the point below the last row, this returns the last row + 1.
114 * @param {number} y
115 * @return {number} a zero-based integer
116 * @see {@link #findColumnForDocumentX}
117 */
118 findRowForDocumentY(y) {
119 y -= this.arrangementOrigin.y;
120 if (y < 0)
121 return -1;
122 let total = 0.0;
123 const it = this._rowDefs;
124 const l = it.length;
125 for (let i = 0; i < l; i++) {
126 const def = it[i];
127 if (def === undefined)
128 continue;
129 total += def.total;
130 if (y < total) {
131 return i;
132 }
133 }
134 return l;
135 }
136 /**
137 * Gets the {@link RowColumnDefinition} for a particular column in this TableLayout.
138 * If you ask for the definition of a column at or beyond the {@link #columnCount},
139 * it will automatically create one and return it.
140 * @param {number} idx the non-negative zero-based integer column index.
141 * @return {RowColumnDefinition}
142 */
143 getColumnDefinition(idx) {
144 if (idx < 0)
145 throw new Error('Column index must be non-negative, not: ' + idx);
146 idx = Math.round(idx);
147 const defs = this._colDefs;
148 let d = defs[idx];
149 if (d === undefined) {
150 d = new go.RowColumnDefinition();
151 // .panel remains null
152 d.isRow = false;
153 d.index = idx;
154 defs[idx] = d;
155 }
156 return d;
157 }
158 /**
159 * Returns the cell at a given x-coordinate in document coordinates.
160 * This information is only valid when this layout has been performed and {@link Layout#isValidLayout} is true.
161 *
162 * If the point is to left of the column 0, this method returns -1.
163 * If the point to to the right of the last column, this returns the last column + 1.
164 * @param {number} x
165 * @return {number} a zero-based integer
166 * @see {@link #findRowForDocumentY}
167 */
168 findColumnForDocumentX(x) {
169 x -= this.arrangementOrigin.x;
170 if (x < 0)
171 return -1;
172 let total = 0.0;
173 const it = this._colDefs;
174 const l = it.length;
175 for (let i = 0; i < l; i++) {
176 const def = it[i];
177 if (def === undefined)
178 continue;
179 total += def.total;
180 if (x < total) {
181 return i;
182 }
183 }
184 return l;
185 }
186 /**
187 * @hidden @internal
188 * Only ever called from TableLayout's measure and arrange
189 */
190 getEffectiveTableStretch(child, row, col) {
191 const effectivestretch = child.stretch;
192 if (effectivestretch !== go.GraphObject.Default)
193 return effectivestretch;
194 // which directions are we stretching?
195 // undefined = default
196 let horizontal;
197 let vertical;
198 switch (row.stretch) {
199 case go.GraphObject.Default:
200 case go.GraphObject.Horizontal: break;
201 case go.GraphObject.Vertical:
202 vertical = true;
203 break;
204 case go.GraphObject.Fill:
205 vertical = true;
206 break;
207 }
208 switch (col.stretch) {
209 case go.GraphObject.Default:
210 case go.GraphObject.Vertical: break;
211 case go.GraphObject.Horizontal:
212 horizontal = true;
213 break;
214 case go.GraphObject.Fill:
215 horizontal = true;
216 break;
217 }
218 const str = this.defaultStretch;
219 if (horizontal === undefined && (str === go.GraphObject.Horizontal || str === go.GraphObject.Fill)) {
220 horizontal = true;
221 }
222 else {
223 horizontal = false;
224 }
225 if (vertical === undefined && (str === go.GraphObject.Vertical || str === go.GraphObject.Fill)) {
226 vertical = true;
227 }
228 else {
229 vertical = false;
230 }
231 if (horizontal === true && vertical === true)
232 return go.GraphObject.Fill;
233 if (horizontal === true)
234 return go.GraphObject.Horizontal;
235 if (vertical === true)
236 return go.GraphObject.Vertical;
237 return go.GraphObject.None; // Everything else is none by default
238 }
239 /**
240 * This method performs the measuring and arranging of the table, assiging positions to each part.
241 * @param {Iterable.<Part>} coll A collection of {@link Part}s.
242 */
243 doLayout(coll) {
244 this.arrangementOrigin = this.initialOrigin(this.arrangementOrigin);
245 // put all eligible Parts that are not Links into an Array
246 const parts = new go.List();
247 this.collectParts(coll).each((p) => {
248 if (!(p instanceof go.Link)) {
249 parts.add(p);
250 }
251 });
252 if (this.diagram !== null) {
253 this.diagram.startTransaction('TableLayout');
254 const union = new go.Size();
255 // this calls .beforeMeasure(parts, rowcol)
256 const rowcol = this.measureTable(Infinity, Infinity, parts, union, 0, 0);
257 this.arrangeTable(parts, union, rowcol);
258 this.afterArrange(parts, rowcol);
259 this.diagram.commitTransaction('TableLayout');
260 }
261 }
262 /**
263 * Override this method in order to perform some operations before measuring.
264 * By default this method does nothing.
265 * @expose
266 */
267 beforeMeasure(parts, rowcol) { }
268 /**
269 * Override this method in order to perform some operations after arranging.
270 * By default this method does nothing.
271 * @expose
272 */
273 afterArrange(parts, rowcol) { }
274 /**
275 * @hidden @internal
276 */
277 measureTable(width, height, children, union, minw, minh) {
278 let l = children.length;
279 // Make the array that holds [rows][cols] of the table
280 const rowcol = []; // saved (so no temp array) starts as an array of rows, will end up [row][col][cell]
281 for (let i = 0; i < l; i++) {
282 const child = children.elt(i);
283 if (!rowcol[child.row]) {
284 rowcol[child.row] = []; // make new column for this row
285 }
286 if (!rowcol[child.row][child.column]) {
287 rowcol[child.row][child.column] = []; // new list for this cell
288 }
289 rowcol[child.row][child.column].push(child); // push child into right cell
290 }
291 this.beforeMeasure(children, rowcol);
292 // Reset the row/col definitions because the ones from last measure are irrelevant
293 const resetCols = []; // keep track of which columns we've already reset
294 // Objects that span multiple columns and
295 const spanners = [];
296 const nosize = [];
297 // These hashes are used to tally the number of rows and columns that do not have a size
298 const nosizeCols = { count: 0 };
299 const nosizeRows = { count: 0 };
300 let colleft = width;
301 let rowleft = height;
302 let defs = this._rowDefs;
303 l = defs.length;
304 for (let i = 0; i < l; i++) {
305 const def = defs[i];
306 if (def !== undefined)
307 def.actual = 0;
308 }
309 defs = this._colDefs;
310 l = defs.length;
311 for (let i = 0; i < l; i++) {
312 const def = defs[i];
313 if (def !== undefined)
314 def.actual = 0;
315 }
316 let lrow = rowcol.length; // number of rows
317 let lcol = 0;
318 for (let i = 0; i < lrow; i++) {
319 if (!rowcol[i])
320 continue;
321 lcol = Math.max(lcol, rowcol[i].length); // column length in this row
322 }
323 // Go through each cell (first pass)
324 let amt = 0.0;
325 lrow = rowcol.length; // number of rows
326 for (let i = 0; i < lrow; i++) {
327 if (!rowcol[i])
328 continue;
329 lcol = rowcol[i].length; // column length in this row
330 const rowHerald = this.getRowDefinition(i);
331 rowHerald.measured = 0; // Reset rows (only on first pass)
332 for (let j = 0; j < lcol; j++) {
333 // foreach column j in row i...
334 if (!rowcol[i][j])
335 continue;
336 const colHerald = this.getColumnDefinition(j);
337 if (resetCols[j] === undefined) { // make sure we only reset these once
338 colHerald.measured = 0;
339 resetCols[j] = true;
340 }
341 const cell = rowcol[i][j];
342 const len = cell.length;
343 for (let k = 0; k < len; k++) {
344 // foreach element in cell, measure
345 const child = cell[k];
346 // Skip children that span more than one row or column or do not have a set size
347 const spanner = (child.rowSpan > 1 || child.columnSpan > 1);
348 if (spanner) {
349 spanners.push(child);
350 // We used to not measure spanners twice, but now we do
351 // The reason is that there may be a row whose size
352 // is dictated by an object with columnSpan 2+ and vice versa
353 // continue;
354 }
355 const marg = child.margin;
356 const margw = marg.right + marg.left;
357 const margh = marg.top + marg.bottom;
358 const stretch = this.getEffectiveTableStretch(child, rowHerald, colHerald);
359 const dsize = child.resizeObject.desiredSize;
360 const realwidth = !(isNaN(dsize.width));
361 const realheight = !(isNaN(dsize.height));
362 const realsize = realwidth && realheight;
363 if (!spanner && stretch !== go.GraphObject.None && !realsize) {
364 if (nosizeCols[j] === undefined && (stretch === go.GraphObject.Fill || stretch === go.GraphObject.Horizontal)) {
365 nosizeCols[j] = -1;
366 nosizeCols.count++;
367 }
368 if (nosizeRows[i] === undefined && (stretch === go.GraphObject.Fill || stretch === go.GraphObject.Vertical)) {
369 nosizeRows[i] = -1;
370 nosizeRows.count++;
371 }
372 nosize.push(child);
373 }
374 if (stretch !== go.GraphObject.None) {
375 const unrestrictedSize = new go.Size(NaN, NaN);
376 // if (stretch !== go.GraphObject.Horizontal) unrestrictedSize.height = rowHerald.minimum;
377 // if (stretch !== go.GraphObject.Vertical) unrestrictedSize.width = colHerald.minimum;
378 // ??? allow resizing during measure phase
379 child.resizeObject.desiredSize = unrestrictedSize;
380 child.ensureBounds();
381 }
382 const m = this.getLayoutBounds(child);
383 const mwidth = Math.max(m.width + margw, 0);
384 const mheight = Math.max(m.height + margh, 0);
385 // Make sure the heralds have the right layout size
386 // the row/column should use the largest measured size of any
387 // GraphObject contained, constrained by mins and maxes
388 if (child.rowSpan === 1 && (realheight || stretch === go.GraphObject.None || stretch === go.GraphObject.Horizontal)) {
389 const def = this.getRowDefinition(i);
390 amt = Math.max(mheight - def.actual, 0);
391 if (amt > rowleft)
392 amt = rowleft;
393 def.measured = def.measured + amt;
394 def.actual = def.actual + amt;
395 rowleft = Math.max(rowleft - amt, 0);
396 }
397 if (child.columnSpan === 1 && (realwidth || stretch === go.GraphObject.None || stretch === go.GraphObject.Vertical)) {
398 const def = this.getColumnDefinition(j);
399 amt = Math.max(mwidth - def.actual, 0);
400 if (amt > colleft)
401 amt = colleft;
402 def.measured = def.measured + amt;
403 def.actual = def.actual + amt;
404 colleft = Math.max(colleft - amt, 0);
405 }
406 } // end cell
407 } // end col
408 } // end row
409 // For objects of no desired size we allocate what is left as we go,
410 // or else what is already in the column
411 let totalColWidth = 0.0;
412 let totalRowHeight = 0.0;
413 l = this.columnCount;
414 for (let i = 0; i < l; i++) {
415 if (this._colDefs[i] === undefined)
416 continue;
417 totalColWidth += this.getColumnDefinition(i).measured;
418 }
419 l = this.rowCount;
420 for (let i = 0; i < l; i++) {
421 if (this._rowDefs[i] === undefined)
422 continue;
423 totalRowHeight += this.getRowDefinition(i).measured;
424 }
425 colleft = Math.max(width - totalColWidth, 0);
426 rowleft = Math.max(height - totalRowHeight, 0);
427 const originalrowleft = rowleft;
428 const originalcolleft = colleft;
429 // Determine column sizes for the yet-to-be-sized columns
430 l = nosize.length;
431 for (let i = 0; i < l; i++) {
432 const child = nosize[i];
433 const rowHerald = this.getRowDefinition(child.row);
434 const colHerald = this.getColumnDefinition(child.column);
435 // We want to gather the largest difference between desired and expected col/row sizes
436 const mb = this.getLayoutBounds(child);
437 const marg = child.margin;
438 const margw = marg.right + marg.left;
439 const margh = marg.top + marg.bottom;
440 if (colHerald.measured === 0 && nosizeCols[child.column] !== undefined) {
441 nosizeCols[child.column] = Math.max(mb.width + margw, nosizeCols[child.column]);
442 }
443 else {
444 nosizeCols[child.column] = null; // obey the column herald
445 }
446 if (rowHerald.measured === 0 && nosizeRows[child.row] !== undefined) {
447 nosizeRows[child.row] = Math.max(mb.height + margh, nosizeRows[child.row]);
448 }
449 else {
450 nosizeRows[child.row] = null; // obey the row herald
451 }
452 }
453 // we now have the size that all these columns prefer to be
454 // we also have the amount left over
455 let desiredRowTotal = 0.0;
456 let desiredColTotal = 0.0;
457 for (const i in nosizeRows) {
458 if (i !== 'count')
459 desiredRowTotal += nosizeRows[i];
460 }
461 for (const i in nosizeCols) {
462 if (i !== 'count')
463 desiredColTotal += nosizeCols[i];
464 }
465 const allowedSize = new go.Size(); // used in stretch and span loops
466 // Deal with objects that have a stretch
467 for (let i = 0; i < l; i++) {
468 const child = nosize[i];
469 const rowHerald = this.getRowDefinition(child.row);
470 const colHerald = this.getColumnDefinition(child.column);
471 let w = 0.0;
472 if (isFinite(colHerald.width)) {
473 w = colHerald.width;
474 }
475 else {
476 if (isFinite(colleft) && nosizeCols[child.column] !== null) {
477 if (desiredColTotal === 0)
478 w = colHerald.actual + colleft;
479 else
480 w = /*colHerald.actual +*/ ((nosizeCols[child.column] / desiredColTotal) * originalcolleft);
481 }
482 else {
483 // Only use colHerald.actual if it was nonzero before this loop
484 if (nosizeCols[child.column] !== null)
485 w = colleft;
486 else
487 w = colHerald.actual || colleft;
488 // w = nosizeCols[child.column] || colleft; // Older, less correct way
489 }
490 w = Math.max(0, w - colHerald.computeEffectiveSpacing());
491 }
492 let h = 0.0;
493 if (isFinite(rowHerald.height)) {
494 h = rowHerald.height;
495 }
496 else {
497 if (isFinite(rowleft) && nosizeRows[child.row] !== null) {
498 if (desiredRowTotal === 0)
499 h = rowHerald.actual + rowleft;
500 else
501 h = /*rowHerald.actual +*/ ((nosizeRows[child.row] / desiredRowTotal) * originalrowleft);
502 }
503 else {
504 // Only use rowHerald.actual if it was nonzero before this loop
505 if (nosizeRows[child.row] !== null)
506 h = rowleft;
507 else
508 h = rowHerald.actual || rowleft;
509 // h = nosizeRows[child.row] || rowleft; // Older, less correct way
510 }
511 h = Math.max(0, h - rowHerald.computeEffectiveSpacing());
512 }
513 allowedSize.setTo(Math.max(colHerald.minimum, Math.min(w, colHerald.maximum)), Math.max(rowHerald.minimum, Math.min(h, rowHerald.maximum)));
514 // Which way do we care about fill:
515 const stretch = this.getEffectiveTableStretch(child, rowHerald, colHerald);
516 // This used to set allowedSize height/width to Infinity,
517 // but we can only set it to the current row/column space, plus rowleft/colleft values, at most.
518 switch (stretch) {
519 case go.GraphObject.Horizontal: // H stretch means it can be as large as its wants vertically
520 allowedSize.height = Math.max(allowedSize.height, rowHerald.actual + rowleft);
521 break;
522 case go.GraphObject.Vertical: // vice versa
523 allowedSize.width = Math.max(allowedSize.width, colHerald.actual + colleft);
524 break;
525 }
526 const marg = child.margin;
527 const margw = marg.right + marg.left;
528 const margh = marg.top + marg.bottom;
529 const m = this.getLayoutBounds(child);
530 let mwidth = Math.max(m.width + margw, 0);
531 let mheight = Math.max(m.height + margh, 0);
532 if (isFinite(colleft))
533 mwidth = Math.min(mwidth, allowedSize.width);
534 if (isFinite(rowleft))
535 mheight = Math.min(mheight, allowedSize.height);
536 let oldAmount = 0.0;
537 oldAmount = rowHerald.actual;
538 rowHerald.actual = Math.max(rowHerald.actual, mheight);
539 rowHerald.measured = Math.max(rowHerald.measured, mheight);
540 amt = rowHerald.actual - oldAmount;
541 rowleft = Math.max(rowleft - amt, 0);
542 oldAmount = colHerald.actual;
543 colHerald.actual = Math.max(colHerald.actual, mwidth);
544 colHerald.measured = Math.max(colHerald.measured, mwidth);
545 amt = colHerald.actual - oldAmount;
546 colleft = Math.max(colleft - amt, 0);
547 } // end no fixed size objects
548 // Go through each object that spans multiple rows or columns
549 const additionalSpan = new go.Size();
550 const actualSizeRows = [];
551 const actualSizeColumns = [];
552 l = spanners.length;
553 if (l !== 0) {
554 // record the actual sizes of every row/column before measuring spanners
555 // because they will change during the loop and we want to use their 'before' values
556 for (let i = 0; i < lrow; i++) {
557 if (!rowcol[i])
558 continue;
559 lcol = rowcol[i].length; // column length in this row
560 const rowHerald = this.getRowDefinition(i);
561 actualSizeRows[i] = rowHerald.actual;
562 for (let j = 0; j < lcol; j++) {
563 // foreach column j in row i...
564 if (!rowcol[i][j])
565 continue;
566 const colHerald = this.getColumnDefinition(j);
567 actualSizeColumns[j] = colHerald.actual;
568 }
569 }
570 }
571 for (let i = 0; i < l; i++) {
572 const child = spanners[i];
573 const rowHerald = this.getRowDefinition(child.row);
574 const colHerald = this.getColumnDefinition(child.column);
575 // If there's a set column width/height we don't care about the given width/height
576 allowedSize.setTo(Math.max(colHerald.minimum, Math.min(width, colHerald.maximum)), Math.max(rowHerald.minimum, Math.min(height, rowHerald.maximum)));
577 // If it is a spanner and has a fill:
578 const stretch = this.getEffectiveTableStretch(child, rowHerald, colHerald);
579 switch (stretch) {
580 case go.GraphObject.Fill:
581 if (actualSizeColumns[colHerald.index] !== 0)
582 allowedSize.width = Math.min(allowedSize.width, actualSizeColumns[colHerald.index]);
583 if (actualSizeRows[rowHerald.index] !== 0)
584 allowedSize.height = Math.min(allowedSize.height, actualSizeRows[rowHerald.index]);
585 break;
586 case go.GraphObject.Horizontal:
587 if (actualSizeColumns[colHerald.index] !== 0)
588 allowedSize.width = Math.min(allowedSize.width, actualSizeColumns[colHerald.index]);
589 break;
590 case go.GraphObject.Vertical:
591 if (actualSizeRows[rowHerald.index] !== 0)
592 allowedSize.height = Math.min(allowedSize.height, actualSizeRows[rowHerald.index]);
593 break;
594 }
595 // If there's a set column width/height we don't care about any of the above:
596 if (isFinite(colHerald.width))
597 allowedSize.width = colHerald.width;
598 if (isFinite(rowHerald.height))
599 allowedSize.height = rowHerald.height;
600 // take into account rowSpan and columnSpan
601 let def = this.getRowDefinition(child.row);
602 additionalSpan.setTo(0, 0);
603 for (let n = 1; n < child.rowSpan; n++) {
604 if (child.row + n >= this.rowCount)
605 break; // if the row exists at all
606 def = this.getRowDefinition(child.row + n);
607 amt = 0;
608 if (stretch === go.GraphObject.Fill || stretch === go.GraphObject.Vertical) {
609 amt = Math.max(def.minimum, (actualSizeRows[child.row + n] === 0) ? def.maximum : Math.min(actualSizeRows[child.row + n], def.maximum));
610 }
611 else {
612 amt = Math.max(def.minimum, isNaN(def.height) ? def.maximum : Math.min(def.height, def.maximum));
613 }
614 additionalSpan.height += amt;
615 }
616 for (let n = 1; n < child.columnSpan; n++) {
617 if (child.column + n >= this.columnCount)
618 break; // if the col exists at all
619 def = this.getColumnDefinition(child.column + n);
620 amt = 0;
621 if (stretch === go.GraphObject.Fill || stretch === go.GraphObject.Horizontal) {
622 amt = Math.max(def.minimum, (actualSizeColumns[child.column + n] === 0) ? def.maximum : Math.min(actualSizeColumns[child.column + n], def.maximum));
623 }
624 else {
625 amt = Math.max(def.minimum, isNaN(def.width) ? def.maximum : Math.min(def.width, def.maximum));
626 }
627 additionalSpan.width += amt;
628 }
629 allowedSize.width += additionalSpan.width;
630 allowedSize.height += additionalSpan.height;
631 const marg = child.margin;
632 const margw = marg.right + marg.left;
633 const margh = marg.top + marg.bottom;
634 const m = this.getLayoutBounds(child);
635 const mwidth = Math.max(m.width + margw, 0);
636 const mheight = Math.max(m.height + margh, 0);
637 let totalRow = 0.0;
638 for (let n = 0; n < child.rowSpan; n++) {
639 if (child.row + n >= this.rowCount)
640 break; // if the row exists at all
641 def = this.getRowDefinition(child.row + n);
642 totalRow += def.total || 0;
643 }
644 // def is the last row definition
645 if (totalRow < mheight) {
646 let roomLeft = mheight - totalRow;
647 while (roomLeft > 0) { // Add the extra to the first row that allows us to
648 const act = def.actual || 0;
649 if (isNaN(def.height) && def.maximum > act) {
650 def.actual = Math.min(def.maximum, act + roomLeft);
651 if (def.actual !== act)
652 roomLeft -= def.actual - act;
653 }
654 if (def.index - 1 === -1)
655 break;
656 def = this.getRowDefinition(def.index - 1);
657 }
658 }
659 let totalCol = 0.0;
660 for (let n = 0; n < child.columnSpan; n++) {
661 if (child.column + n >= this.columnCount)
662 break; // if the col exists at all
663 def = this.getColumnDefinition(child.column + n);
664 totalCol += def.total || 0;
665 }
666 // def is the last col definition
667 if (totalCol < mwidth) {
668 let roomLeft = mwidth - totalCol;
669 while (roomLeft > 0) { // Add the extra to the first row that allows us to
670 const act = def.actual || 0;
671 if (isNaN(def.width) && def.maximum > act) {
672 def.actual = Math.min(def.maximum, act + roomLeft);
673 if (def.actual !== act)
674 roomLeft -= def.actual - act;
675 }
676 if (def.index - 1 === -1)
677 break;
678 def = this.getColumnDefinition(def.index - 1);
679 }
680 }
681 } // end spanning objects
682 l = this.columnCount;
683 for (let i = 0; i < l; i++) {
684 if (this._colDefs[i] === undefined)
685 continue;
686 const def = this.getColumnDefinition(i);
687 def.position = union.width;
688 if (def.actual !== 0) {
689 union.width += def.actual;
690 union.width += def.computeEffectiveSpacing();
691 }
692 }
693 l = this.rowCount;
694 for (let i = 0; i < l; i++) {
695 if (this._rowDefs[i] === undefined)
696 continue;
697 const def = this.getRowDefinition(i);
698 def.position = union.height;
699 if (def.actual !== 0) {
700 union.height += def.actual;
701 union.height += def.computeEffectiveSpacing();
702 }
703 }
704 // save these for arrange (destroy them or not? Possibly needed for drawing spacers)
705 return rowcol;
706 } // end measureTable
707 /**
708 * @hidden @internal
709 */
710 arrangeTable(children, union, rowcol) {
711 const l = children.length;
712 const originx = this.arrangementOrigin.x;
713 const originy = this.arrangementOrigin.y;
714 let x = 0.0;
715 let y = 0.0;
716 const lrow = rowcol.length; // number of rows
717 let lcol = 0;
718 for (let i = 0; i < lrow; i++) {
719 if (!rowcol[i])
720 continue;
721 lcol = Math.max(lcol, rowcol[i].length); // column length in this row
722 }
723 const additionalSpan = new go.Size();
724 // Find cell space and arrange objects:
725 for (let i = 0; i < lrow; i++) {
726 if (!rowcol[i])
727 continue;
728 lcol = rowcol[i].length; // column length in this row
729 const rowHerald = this.getRowDefinition(i);
730 y = originy + rowHerald.position + rowHerald.computeEffectiveSpacingTop();
731 for (let j = 0; j < lcol; j++) {
732 // foreach column j in row i...
733 if (!rowcol[i][j])
734 continue;
735 const colHerald = this.getColumnDefinition(j);
736 x = originx + colHerald.position + colHerald.computeEffectiveSpacingTop();
737 const cell = rowcol[i][j];
738 const len = cell.length;
739 for (let k = 0; k < len; k++) {
740 // foreach element in cell
741 const child = cell[k];
742 // add to layoutWidth/Height any additional span
743 additionalSpan.setTo(0, 0);
744 for (let n = 1; n < child.rowSpan; n++) {
745 // if the row exists at all
746 if (i + n >= this.rowCount)
747 break;
748 const rh = this.getRowDefinition(i + n);
749 additionalSpan.height += rh.total;
750 }
751 for (let n = 1; n < child.columnSpan; n++) {
752 // if the col exists at all
753 if (j + n >= this.columnCount)
754 break;
755 const ch = this.getColumnDefinition(j + n);
756 additionalSpan.width += ch.total;
757 }
758 // Construct containing rect (cell):
759 // total width and height of the cell that an object could possibly be created in
760 const colwidth = colHerald.actual + additionalSpan.width;
761 const rowheight = rowHerald.actual + additionalSpan.height;
762 // construct a rect that represents the total cell size allowed for this object
763 const ar = new go.Rect();
764 ar.x = x;
765 ar.y = y;
766 ar.width = colwidth;
767 ar.height = rowheight;
768 // Also keep them for clip values
769 const cellx = x;
770 const celly = y;
771 let cellw = colwidth;
772 let cellh = rowheight;
773 // Ending rows/col might have actual spaces that are larger than the remaining space
774 // Modify them for clipping regions
775 if (x + colwidth > union.width)
776 cellw = Math.max(union.width - x, 0);
777 if (y + rowheight > union.height)
778 cellh = Math.max(union.height - y, 0);
779 // Construct alignment:
780 let align = child.alignment;
781 let alignx = 0.0;
782 let aligny = 0.0;
783 let alignoffsetX = 0.0;
784 let alignoffsetY = 0.0;
785 if (align.isDefault()) {
786 align = this.defaultAlignment;
787 if (!align.isSpot())
788 align = go.Spot.Center;
789 alignx = align.x;
790 aligny = align.y;
791 alignoffsetX = align.offsetX;
792 alignoffsetY = align.offsetY;
793 const ca = colHerald.alignment;
794 const ra = rowHerald.alignment;
795 if (ca.isSpot()) {
796 alignx = ca.x;
797 alignoffsetX = ca.offsetX;
798 }
799 if (ra.isSpot()) {
800 aligny = ra.y;
801 alignoffsetY = ra.offsetY;
802 }
803 }
804 else {
805 alignx = align.x;
806 aligny = align.y;
807 alignoffsetX = align.offsetX;
808 alignoffsetY = align.offsetY;
809 }
810 // same as if (!align.isSpot()) align = go.Spot.Center;
811 if (isNaN(alignx) || isNaN(aligny)) {
812 alignx = 0.5;
813 aligny = 0.5;
814 alignoffsetX = 0;
815 alignoffsetY = 0;
816 }
817 let width = 0.0;
818 let height = 0.0;
819 const marg = child.margin;
820 const margw = marg.left + marg.right;
821 const margh = marg.top + marg.bottom;
822 const stretch = this.getEffectiveTableStretch(child, rowHerald, colHerald);
823 if ( /* isNaN(child.resizeObject.desiredSize.width) && */(stretch === go.GraphObject.Fill || stretch === go.GraphObject.Horizontal)) {
824 width = Math.max(colwidth - margw, 0);
825 }
826 else {
827 width = this.getLayoutBounds(child).width;
828 }
829 if ( /* isNaN(child.resizeObject.desiredSize.height) && */(stretch === go.GraphObject.Fill || stretch === go.GraphObject.Vertical)) {
830 height = Math.max(rowheight - margh, 0);
831 }
832 else {
833 height = this.getLayoutBounds(child).height;
834 }
835 // min and max override any stretch values
836 const max = child.maxSize;
837 const min = child.minSize;
838 width = Math.min(max.width, width);
839 height = Math.min(max.height, height);
840 width = Math.max(min.width, width);
841 height = Math.max(min.height, height);
842 const widthmarg = width + margw;
843 const heightmarg = height + margh;
844 ar.x += (ar.width * alignx) - (widthmarg * alignx) + alignoffsetX + marg.left;
845 ar.y += (ar.height * aligny) - (heightmarg * aligny) + alignoffsetY + marg.top;
846 child.moveTo(ar.x, ar.y);
847 if (stretch !== go.GraphObject.None) {
848 child.resizeObject.desiredSize = new go.Size(width, height);
849 }
850 } // end cell
851 } // end col
852 } // end row
853 } // end arrangeTable
854} // end TableLayout class