1 /* ************************************************************************
3 qooxdoo - the new era of web development
11 LGPL: http://www.gnu.org/licenses/lgpl.html
12 EPL: http://www.eclipse.org/org/documents/epl-v10.php
13 See the LICENSE file in the project's top-level directory for details.
16 * Derrell Lipman (derrell)
18 ************************************************************************ */
20 /* ************************************************************************
24 ************************************************************************ */
27 * The default resize behavior. Until a resize model is loaded, the default
31 * Upon the table initially appearing, and upon any window resize, divide
32 * the table space equally between the visible columns.
35 * When a column is increased in width, all columns to its right are
36 * pushed to the right with no change to their widths. This may push some
37 * columns off the right edge of the table, causing a horizontal scroll
41 * When a column is decreased in width, if the total width of all columns
42 * is <i>greater than</i> the table width, no additional column wiidth
46 * When a column is decreased in width, if the total width of all columns
47 * is <i>less than</i> the width of the table, the visible column
48 * immediately to the right of the column which decreased in width has its
49 * width increased to fill the remaining space.
53 * A resize model may be loaded to provide more guidance on how to adjust
54 * column width upon each of the events: initial appear, window resize, and
55 * column resize. *** TO BE FILLED IN ***
57 qx.OO.defineClass("qx.ui.table.DefaultResizeBehavior",
58 qx.ui.table.AbstractResizeBehavior,
61 qx.ui.table.AbstractResizeBehavior.call(this);
66 * A function to instantiate a resize behavior column data object.
71 "newResizeBehaviorColumnData",
79 return new qx.ui.table.ResizeBehaviorColumnData();
85 * Set the width of a column.
87 * @param col {Integer}
88 * The column whose width is to be set
90 * @param width {Integer, String}
91 * The width of the specified column. The width may be specified as integer
92 * number of pixels (e.g. 100), a string representing percentage of the
93 * inner width of the Table (e.g. "25%"), or a string representing a flex
96 qx.Proto.setWidth = function(col, width)
98 // Ensure the column is within range
99 if (col >= this._resizeColumnData.length)
101 throw new Error("Column number out of range");
105 this._resizeColumnData[col].setWidth(width);
110 * Set the minimum width of a column.
112 * @param col {Integer}
113 * The column whose minimum width is to be set
115 * @param width {Integer}
116 * The minimum width of the specified column.
118 qx.Proto.setMinWidth = function(col, width)
120 // Ensure the column is within range
121 if (col >= this._resizeColumnData.length)
123 throw new Error("Column number out of range");
127 this._resizeColumnData[col].setMinWidth(width);
132 * Set the maximum width of a column.
134 * @param col {Integer}
135 * The column whose maximum width is to be set
137 * @param width {Integer}
138 * The maximum width of the specified column.
140 qx.Proto.setMaxWidth = function(col, width)
142 // Ensure the column is within range
143 if (col >= this._resizeColumnData.length)
145 throw new Error("Column number out of range");
149 this._resizeColumnData[col].setMaxWidth(width);
154 * Set any or all of the width, minimum width, and maximum width of a column
158 * A map containing any or all of the property names "width", "minWidth",
159 * and "maxWidth". The property values are as described for
160 * {@link #setWidth}, {@link #setMinWidth} and {@link #setMaxWidth}
163 qx.Proto.set = function(col, map)
165 for (var prop in map)
170 this.setWidth(col, map[prop]);
174 this.setMinWidth(col, map[prop]);
178 this.setMaxWidth(col, map[prop]);
182 throw new Error("Unknown property: " + prop);
189 qx.Proto.onAppear = function(tableColumnModel, event)
191 // Get the initial available width so we know whether a resize caused an
192 // increase or decrease in the available space.
193 this._width = this._getAvailableWidth(tableColumnModel);
195 // Calculate column widths
196 this._computeColumnsFlexWidth(tableColumnModel, event);
201 qx.Proto.onTableWidthChanged = function(tableColumnModel, event)
203 // Calculate column widths
204 this._computeColumnsFlexWidth(tableColumnModel, event);
209 qx.Proto.onColumnWidthChanged = function(tableColumnModel, event)
211 // Extend the next column to fill blank space
212 this._extendNextColumn(tableColumnModel, event);
217 qx.Proto.onVisibilityChanged = function(tableColumnModel, event)
219 // Extend the last column to fill blank space
220 this._extendLastColumn(tableColumnModel, event);
225 qx.Proto._setNumColumns = function(numColumns)
227 // Are there now fewer (or the same number of) columns than there were
229 if (numColumns <= this._resizeColumnData.length)
231 // Yup. Delete the extras.
232 this._resizeColumnData.splice(numColumns);
236 // There are more columns than there were previously. Allocate more.
237 for (var i = this._resizeColumnData.length; i < numColumns; i++)
239 this._resizeColumnData[i] = this.getNewResizeBehaviorColumnData()();
240 this._resizeColumnData[i]._columnNumber = i;
246 * Computes the width of all flexible children (based loosely on the method of
247 * the same name in HorizontalBoxLayoutImpl).
249 * @param tableColumnModel {qx.ui.table.ResizeTableColumnModel}
250 * The table column model in use.
255 qx.Proto._computeColumnsFlexWidth = function(tableColumnModel, event)
257 // Semi-permanent configuration settings
262 this.debug("computeColumnsFlexWidth");
265 var visibleColumns = tableColumnModel._visibleColumnArr;
266 var visibleColumnsLength = visibleColumns.length;
268 var flexibleColumns = [ ];
272 // Determine the available width
273 var width = this._getAvailableWidth(tableColumnModel);
276 // *************************************************************
277 // 1. Compute the sum of all static sized columns and find
278 // all flexible columns.
279 // *************************************************************
280 for (i = 0; i < visibleColumnsLength; i++)
282 // Get the current column's column data
283 columnData = this._resizeColumnData[visibleColumns[i]];
285 // Is this column width type "auto"?
286 if (columnData._computedWidthTypeAuto)
288 // Yup. Convert it to a Flex "1*"
289 columnData._computedWidthTypeAuto = false;
290 columnData._computedWidthTypeFlex = true;
291 columnData._computedWidthParsed = 1;
294 // Is this column a flex width?
295 if (columnData._computedWidthTypeFlex)
297 // Yup. Save it for future processing.
298 flexibleColumns.push(columnData);
300 else if (columnData._computedWidthTypePercent)
302 // We can calculate the width of a Percent type right now. Convert it
303 // to a Flex type that's already calculated (no further calculation
305 columnData._computedWidthPercentValue =
306 Math.round(width * (columnData._computedWidthParsed / 100));
307 widthUsed += columnData._computedWidthPercentValue;
311 // We have a fixed width. Track width already allocated.
312 widthUsed += columnData.getWidth();
318 this.debug("Width: " + widthUsed + "/" + width);
319 this.debug("Flexible Count: " + flexibleColumns.length);
323 // *************************************************************
324 // 2. Compute the sum of all flexible column widths
325 // *************************************************************
326 var widthRemaining = width - widthUsed;
327 var flexibleColumnsLength = flexibleColumns.length;
330 for (i = 0; i < flexibleColumnsLength; i++)
332 prioritySum += flexibleColumns[i]._computedWidthParsed;
336 // *************************************************************
337 // 3. Calculating the size of each 'part'.
338 // *************************************************************
339 var partWidth = widthRemaining / prioritySum;
341 // *************************************************************
342 // 4. Adjust flexible columns, taking min/max values into account
343 // *************************************************************
345 bSomethingChanged = true;
346 for (flexibleColumnsLength = flexibleColumns.length;
347 bSomethingChanged && flexibleColumnsLength > 0;
348 flexibleColumnsLength = flexibleColumns.length)
350 // Assume nothing will change
351 bSomethingChanged = false;
353 for (i = flexibleColumnsLength - 1; i >= 0; i--)
355 columnData = flexibleColumns[i];
357 computedFlexibleWidth =
358 columnData._computedWidthFlexValue =
359 columnData._computedWidthParsed * partWidth;
361 // If the part is not within its specified min/max range, adjust it.
362 var min = columnData.getMinWidthValue();
363 var max = columnData.getMaxWidthValue();
364 if (min && computedFlexibleWidth < min)
366 columnData._computedWidthFlexValue = Math.round(min);
367 widthUsed += columnData._computedWidthFlexValue;
368 qx.lang.Array.removeAt(flexibleColumns, i);
369 bSomethingChanged = true;
371 // Don't round fixed-width columns (in step 5)
374 else if (max && computedFlexibleWidth > max)
376 columnData._computedWidthFlexValue = Math.round(max);
377 widthUsed += columnData._computedWidthFlexValue;
378 qx.lang.Array.removeAt(flexibleColumns, i);
379 bSomethingChanged = true;
381 // Don't round fixed-width columns (in step 5)
387 // If any flexible columns remain, then allocate the remaining space to them
388 if (flexibleColumns.length > 0)
390 // Recalculate the priority sum of the remaining flexible columns
392 for (i = 0; i < flexibleColumnsLength; i++)
394 prioritySum += flexibleColumns[i]._computedWidthParsed;
397 // Recalculate the width remaining and part width
398 widthRemaining = width - widthUsed;
399 partWidth = widthRemaining / prioritySum;
401 // If there's no width remaining...
402 if (widthRemaining <= 0)
404 // ... then use minimum width * priority for all remaining columns
405 for (i = 0; i < flexibleColumnsLength; i++)
407 columnData = flexibleColumns[i];
409 computedFlexibleWidth =
410 columnData._computedWidthFlexValue =
411 (qx.ui.table.DefaultResizeBehavior.MIN_WIDTH *
412 flexibleColumns[i]._computedWidthParsed);
413 columnData._computedWidthFlexValue = Math.round(computedFlexibleWidth);
414 widthUsed += columnData._computedWidthFlexValue;
419 // Assign widths of remaining flexible columns
420 for (i = 0; i < flexibleColumnsLength; i++)
422 columnData = flexibleColumns[i];
424 computedFlexibleWidth =
425 columnData._computedWidthFlexValue =
426 columnData._computedWidthParsed * partWidth;
428 // If the computed width is less than our hard-coded minimum...
429 if (computedFlexibleWidth <
430 qx.ui.table.DefaultResizeBehavior.MIN_WIDTH)
432 // ... then use the hard-coded minimum
433 computedFlexibleWidth = qx.ui.table.DefaultResizeBehavior.MIN_WIDTH;
436 columnData._computedWidthFlexValue = Math.round(computedFlexibleWidth);
437 widthUsed += columnData._computedWidthFlexValue;
442 // *************************************************************
443 // 5. Fix rounding errors
444 // *************************************************************
445 if (columnData != null && widthRemaining > 0)
447 columnData._computedWidthFlexValue += width - widthUsed;
450 // *************************************************************
451 // 6. Set the column widths to what we have calculated
452 // *************************************************************
453 for (i = 0; i < visibleColumnsLength; i++)
457 // Get the current column's column data
458 columnData = this._resizeColumnData[visibleColumns[i]];
460 // Is this column a flex width?
461 if (columnData._computedWidthTypeFlex)
463 // Yup. Set the width to the calculated width value based on flex
464 colWidth = columnData._computedWidthFlexValue;
466 else if (columnData._computedWidthTypePercent)
468 // Set the width to the calculated width value based on percent
469 colWidth = columnData._computedWidthPercentValue;
473 colWidth = columnData.getWidth();
476 // Now that we've calculated the width, set it.
477 tableColumnModel.setColumnWidth(visibleColumns[i], colWidth);
481 this.debug("col " + columnData._columnNumber + ": width=" + colWidth);
488 * Extend the visible column to right of the column which just changed width,
489 * to fill any available space within the inner width of the table. This
490 * means that if the sum of the widths of all columns exceeds the inner width
491 * of the table, no change is made. If, on the other hand, the sum of the
492 * widths of all columns is less than the inner width of the table, the
493 * visible column to the right of the column which just changed width is
494 * extended to take up the width available within the inner width of the
497 * @param tableColumnModel {qx.ui.table.ResizeTableColumnModel}
498 * The table column model in use.
503 qx.Proto._extendNextColumn = function(tableColumnModel, event)
505 // Event data properties: col, oldWidth, newWidth
506 var data = event.getData();
508 var visibleColumns = tableColumnModel._visibleColumnArr;
510 // Determine the available width
511 var width = this._getAvailableWidth(tableColumnModel);
513 // Determine the number of visible columns
514 var numColumns = visibleColumns.length;
516 // Did this column become longer than it was?
517 if (data.newWidth > data.oldWidth)
519 // Yup. Don't resize anything else. The other columns will just get
520 // pushed off and require scrollbars be added (if not already there).
524 // This column became shorter. See if we no longer take up the full space
525 // that's available to us.
529 for (i = 0; i < numColumns; i++)
532 tableColumnModel.getColumnWidth(visibleColumns[i]);
535 // If the used width is less than the available width...
536 if (widthUsed < width)
538 // ... then determine the next visible column
539 for (i = 0; i < visibleColumns.length; i++)
541 if (visibleColumns[i] == data.col)
543 nextCol = visibleColumns[i + 1];
550 // Make the next column take up the available space.
551 var oldWidth = tableColumnModel.getColumnWidth(nextCol);
552 var newWidth = (width - (widthUsed -
553 tableColumnModel.getColumnWidth(nextCol)));
554 tableColumnModel.setColumnWidth(nextCol, newWidth);
561 * If a column was just made invisible, extend the last column to fill any
562 * available space within the inner width of the table. This means that if
563 * the sum of the widths of all columns exceeds the inner width of the table,
564 * no change is made. If, on the other hand, the sum of the widths of all
565 * columns is less than the inner width of the table, the last column is
566 * extended to take up the width available within the inner width of the
569 * @param tableColumnModel {qx.ui.table.ResizeTableColumnModel}
570 * The table column model in use.
575 qx.Proto._extendLastColumn = function(tableColumnModel, event)
577 // Event data properties: col, visible
578 var data = event.getData();
580 // If the column just became visible, don't make any width changes
586 // Get the array of visible columns
587 var visibleColumns = tableColumnModel._visibleColumnArr;
589 // Determine the available width
590 var width = this._getAvailableWidth(tableColumnModel);
592 // Determine the number of visible columns
593 var numColumns = visibleColumns.length;
595 // See if we no longer take up the full space that's available to us.
599 for (i = 0; i < numColumns; i++)
602 tableColumnModel.getColumnWidth(visibleColumns[i]);
605 // If the used width is less than the available width...
606 if (widthUsed < width)
608 // ... then get the last visible column
609 lastCol = visibleColumns[visibleColumns.length - 1];
611 // Make the last column take up the available space.
612 var oldWidth = tableColumnModel.getColumnWidth(lastCol);
613 var newWidth = (width - (widthUsed -
614 tableColumnModel.getColumnWidth(lastCol)));
615 tableColumnModel.setColumnWidth(lastCol, newWidth);
621 qx.Class.MIN_WIDTH = 10;