All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scout.layout.LogicalGridLayoutInfo.js Maven / Gradle / Ivy

There is a newer version: 25.1.0-beta.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2014-2015 BSI Business Systems Integration AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     BSI Business Systems Integration AG - initial API and implementation
 ******************************************************************************/
/**
 * JavaScript port of org.eclipse.scout.rt.ui.swing.LogicalGridLayoutInfo.
 */
scout.LogicalGridLayoutInfo = function($components, cons, hgap, vgap) {
  this.gridDatas = [];
  this.$components = $components;
  this.cols;
  this.rows;
  this.width = [];
  this.height = [];
  this.weightX;
  this.weightY;
  this.m_hgap = hgap;
  this.m_vgap = vgap;
  this.m_cellBounds = [];

  // create a modifiable copy of the grid datas
  var i, gd, x, y;
  for (i = 0; i < cons.length; i++) {
    this.gridDatas[i] = new scout.LogicalGridData(cons[i]);
  }
  if ($components.length === 0) {
    this.cols = 0;
    this.rows = 0;
    this.width = [];
    this.height = [];
    this.weightX = [];
    this.weightY = [];
    return;
  }
  // eliminate unused rows and columns
  var usedCols = new scout.TreeSet();
  var usedRows = new scout.TreeSet();
  // ticket 86645 use member gridDatas instead of param cons
  for (i = 0; i < this.gridDatas.length; i++) {
    gd = this.gridDatas[i];
    if (gd.gridx < 0) {
      gd.gridx = 0;
    }
    if (gd.gridy < 0) {
      gd.gridy = 0;
    }
    if (gd.gridw < 1) {
      gd.gridw = 1;
    }
    if (gd.gridh < 1) {
      gd.gridh = 1;
    }
    for (x = gd.gridx; x < gd.gridx + gd.gridw; x++) {
      usedCols.add(x);
    }
    for (y = gd.gridy; y < gd.gridy + gd.gridh; y++) {
      usedRows.add(y);
    }
  }
  var maxCol = usedCols.last();
  for (x = maxCol; x >= 0; x--) {
    if (!usedCols.contains(x)) {
      // eliminate column
      // ticket 86645 use member gridDatas instead of param cons
      for (i = 0; i < this.gridDatas.length; i++) {
        gd = this.gridDatas[i];
        if (gd.gridx > x) {
          gd.gridx--;
        }
      }
    }
  }
  var maxRow = usedRows.last();
  for (y = maxRow; y >= 0; y--) {
    if (!usedRows.contains(y)) {
      // eliminate row
      // ticket 86645 use member gridDatas instead of param cons
      for (i = 0; i < this.gridDatas.length; i++) {
        gd = this.gridDatas[i];
        if (gd.gridy > y) {
          // ticket 86645
          gd.gridy--;
        }
      }
    }
  }
  //
  this.cols = usedCols.size();
  this.rows = usedRows.size();
  this.width = [];
  this.height = [];
  this.weightX = [];
  this.weightY = [];
  $.log.trace('(LogicalGridLayoutInfo#CTOR) $components.length=' + $components.length + ' usedCols=' + this.cols + ' usedRows=' + this.rows);
  this._initializeInfo(hgap, vgap);
};

scout.LogicalGridLayoutInfo.prototype._initializeInfo = function(hgap, vgap) {
  var comp,
    compCount = this.$components.length,
    compSize = [];
  // cache component sizes and cleanup constraints
  var $comp, cons, d;
  for (var i = 0; i < compCount; i++) {
    $comp = this.$components[i];
    cons = this.gridDatas[i];
    if (cons.useUiHeight || cons.useUiWidth || !cons.fillVertical || !cons.fillHorizontal) {
      // Only read preferred size if really needed by the logical grid layout
      d = this.uiSizeInPixel($comp);
      if ($.log.isTraceEnabled()) {
        comp = scout.HtmlComponent.optGet($comp);
        $.log.trace('(LogicalGridLayoutInfo#initializeInfo $comp = ' + comp ? comp.debug() : '' + ' size=' + d);
      }
    } else {
      d = new scout.Dimension(0, 0);
    }
    if (cons.widthHint > 0) {
      d.width = cons.widthHint;
    }
    if (cons.heightHint > 0) {
      d.height = cons.heightHint;
    }
    compSize[i] = d;
    if (cons.gridx < 0) {
      cons.gridx = 0;
    }
    if (cons.gridy < 0) {
      cons.gridy = 0;
    }
    if (cons.gridw < 1) {
      cons.gridw = 1;
    }
    if (cons.gridh < 1) {
      cons.gridh = 1;
    }
    if (cons.gridx >= this.cols) {
      cons.gridx = this.cols - 1;
    }
    if (cons.gridy >= this.rows) {
      cons.gridy = this.rows - 1;
    }
    if (cons.gridx + cons.gridw - 1 >= this.cols) {
      cons.gridw = this.cols - cons.gridx;
    }
    if (cons.gridy + cons.gridh >= this.rows) {
      cons.gridh = this.rows - cons.gridy;
    }
  }
  this.compSize = compSize;
  this._initializeColumns(compSize, hgap);
  this._initializeRows(compSize, vgap);
};

scout.LogicalGridLayoutInfo.prototype._initializeColumns = function(compSize, hgap) {
  var compCount = compSize.length;
  var prefWidths = scout.arrays.init(this.cols, 0);
  var fixedWidths = scout.arrays.init(this.cols, false);
  var i, j, k, prefw, cons;
  for (i = 0; i < compCount; i++) {
    cons = this.gridDatas[i];
    if (cons.gridw === 1) {
      if (cons.widthHint > 0) {
        prefw = cons.widthHint;
      } else if (cons.useUiWidth) {
        prefw = compSize[i].width;
      } else {
        prefw = this.logicalWidthInPixel(cons);
      }
      prefw = Math.floor(prefw);
      for (j = cons.gridx; j < cons.gridx + cons.gridw && j < this.cols; j++) {
        prefWidths[j] = Math.max(prefWidths[j], prefw);
        if (cons.weightx === 0) {
          fixedWidths[j] = true;
        }
      }
    }
  }
  for (i = 0; i < compCount; i++) {
    cons = this.gridDatas[i];
    if (cons.gridw > 1) {
      var hSpan = cons.gridw;
      var spanWidth = 0;
      var distWidth;
      // pref
      for (j = cons.gridx; j < cons.gridx + cons.gridw && j < this.cols; j++) {
        if (!fixedWidths[j]) {
          spanWidth += prefWidths[j];
        }
      }
      if (cons.widthHint > 0) {
        distWidth = cons.widthHint - spanWidth - (hSpan - 1) * hgap;
      } else if (cons.useUiWidth) {
        distWidth = compSize[i].width - spanWidth - (hSpan - 1) * hgap;
      } else {
        distWidth = this.logicalWidthInPixel(cons) - spanWidth - (hSpan - 1) * hgap;
      }
      if (distWidth > 0) {
        var equalWidth = Math.floor((distWidth + spanWidth) / hSpan);
        var remainder = (distWidth + spanWidth) % hSpan;
        var last = -1;
        for (j = cons.gridx; j < cons.gridx + cons.gridw && j < this.cols; j++) {
          if (fixedWidths[j]) {
            prefWidths[last = j] = prefWidths[j];
          } else {
            prefWidths[last = j] = Math.max(equalWidth, prefWidths[j]);
          }
          if (cons.weightx === 0) {
            fixedWidths[j] = true;
          }
        }
        if (last > -1) {
          prefWidths[last] += remainder;
        }
      }
    }
  }

  var lc = scout.LayoutConstants;
  for (i = 0; i < this.cols; i++) {
    this.width[i] = [];
    if (fixedWidths[i]) {
      this.width[i][lc.MIN] = prefWidths[i];
      this.width[i][lc.PREF] = prefWidths[i];
      this.width[i][lc.MAX] = prefWidths[i];
    } else {
      this.width[i][lc.MIN] = 0; // must be exactly 0!
      this.width[i][lc.PREF] = prefWidths[i];
      this.width[i][lc.MAX] = 10240;
    }
  }

  // averaged column weights, normalized so that sum of weights is equal to
  // 1.0
  for (i = 0; i < this.cols; i++) {
    if (fixedWidths[i]) {
      this.weightX[i] = 0;
    } else {
      var weightSum = 0;
      var weightCount = 0;
      for (k = 0; k < compCount; k++) {
        cons = this.gridDatas[k];
        if (cons.weightx > 0 && cons.gridx <= i && i <= cons.gridx + cons.gridw - 1) {
          weightSum += (cons.weightx / cons.gridw);
          weightCount++;
        }
      }
      this.weightX[i] = (weightCount > 0 ? weightSum / weightCount : 0);
    }
  }
  var sumWeightX = 0;
  for (i = 0; i < this.cols; i++) {
    sumWeightX += this.weightX[i];
  }
  if (sumWeightX >= 1e-6) {
    var f = 1.0 / sumWeightX;
    for (i = 0; i < this.cols; i++) {
      this.weightX[i] = this.weightX[i] * f;
    }
  }
};

scout.LogicalGridLayoutInfo.prototype._initializeRows = function(compSize, vgap) {
  var compCount = compSize.length;
  var prefHeights = scout.arrays.init(this.rows, 0);
  var fixedHeights = scout.arrays.init(this.rows, false);
  var i, j, k, prefh, cons;
  for (i = 0; i < compCount; i++) {
    cons = this.gridDatas[i];
    if (cons.gridh === 1) {
      if (cons.heightHint > 0) {
        prefh = cons.heightHint;
      } else if (cons.useUiHeight) {
        prefh = compSize[i].height;
      } else {
        prefh = this.logicalHeightInPixel(cons);
      }
      prefh = Math.floor(prefh);
      for (j = cons.gridy; j < cons.gridy + cons.gridh && j < this.rows; j++) {
        prefHeights[j] = Math.max(prefHeights[j], prefh);
        if (cons.weighty === 0) {
          fixedHeights[j] = true;
        }
      }
    }
  }
  for (i = 0; i < compCount; i++) {
    cons = this.gridDatas[i];
    if (cons.gridh > 1) {
      var vSpan = cons.gridh;
      var spanHeight = 0;
      var distHeight;
      // pref
      for (j = cons.gridy; j < cons.gridy + cons.gridh && j < this.rows; j++) {
        if (!fixedHeights[j]) {
          spanHeight += prefHeights[j];
        }
      }
      if (cons.heightHint > 0) {
        distHeight = cons.heightHint - spanHeight - (vSpan - 1) * vgap;
      } else if (cons.useUiHeight) {
        distHeight = compSize[i].height - spanHeight - (vSpan - 1) * vgap;
      } else {
        distHeight = this.logicalHeightInPixel(cons) - spanHeight - (vSpan - 1) * vgap;
      }
      if (distHeight > 0) {
        var equalHeight = Math.floor((distHeight + spanHeight) / vSpan);
        var remainder = (distHeight + spanHeight) % vSpan;
        var last = -1;
        for (j = cons.gridy; j < cons.gridy + cons.gridh && j < this.rows; j++) {
          if (fixedHeights[j]) {
            prefHeights[last = j] = prefHeights[j];
          } else {
            prefHeights[last = j] = Math.max(equalHeight, prefHeights[j]);
          }
          if (cons.weighty === 0) {
            fixedHeights[j] = true;
          }
        }
        if (last > -1) {
          prefHeights[last] += remainder;
        }
      }
    }
  }

  var lc = scout.LayoutConstants;
  for (i = 0; i < this.rows; i++) {
    this.height[i] = [];
    if (fixedHeights[i]) {
      this.height[i][lc.MIN] = prefHeights[i];
      this.height[i][lc.PREF] = prefHeights[i];
      this.height[i][lc.MAX] = prefHeights[i];
    } else {
      this.height[i][lc.MIN] = 0; // must be exactly 0!
      this.height[i][lc.PREF] = prefHeights[i];
      this.height[i][lc.MAX] = 10240;
    }
  }

  // averaged row weights, normalized so that sum of weights is equal to 1.0
  for (i = 0; i < this.rows; i++) {
    if (fixedHeights[i]) {
      this.weightY[i] = 0;
    } else {
      var weightSum = 0;
      var weightCount = 0;
      for (k = 0; k < compCount; k++) {
        cons = this.gridDatas[k];
        if (cons.weighty > 0 && cons.gridy <= i && i <= cons.gridy + cons.gridh - 1) {
          weightSum += (cons.weighty / cons.gridh);
          weightCount++;
        }
      }
      this.weightY[i] = (weightCount > 0 ? weightSum / weightCount : 0);
    }
  }
  var sumWeightY = 0;
  for (i = 0; i < this.rows; i++) {
    sumWeightY += this.weightY[i];
  }
  if (sumWeightY >= 1e-6) {
    var f = 1.0 / sumWeightY;
    for (i = 0; i < this.rows; i++) {
      this.weightY[i] = this.weightY[i] * f;
    }
  }
};

scout.LogicalGridLayoutInfo.prototype.layoutCellBounds = function(size, insets) {
  var w = this.layoutSizes(size.width - insets.horizontal() - Math.max(0, (this.cols - 1) * this.m_hgap), this.width, this.weightX);
  var h = this.layoutSizes(size.height - insets.vertical() - Math.max(0, (this.rows - 1) * this.m_vgap), this.height, this.weightY);
  this.m_cellBounds = scout.arrays.init(this.rows, null);
  var y = insets.top,
    r, x, c;
  for (r = 0; r < this.rows; r++) {
    x = insets.left;
    this.m_cellBounds[r] = scout.arrays.init(this.cols, null);
    for (c = 0; c < this.cols; c++) {
      this.m_cellBounds[r][c] = new scout.Rectangle(x, y, w[c], h[r]);
      x += w[c];
      x += this.m_hgap;
    }
    y += h[r];
    y += this.m_vgap;
  }
  return this.m_cellBounds;
};

scout.LogicalGridLayoutInfo.prototype.layoutSizes = function(targetSize, sizes, weights) {
  var i;
  var outSizes = scout.arrays.init(sizes.length, 0);
  if (targetSize <= 0) {
    for (i = 0; i < sizes.length; i++) {
      outSizes[i] = sizes[i][scout.LayoutConstants.MIN];
    }
    return outSizes;
  }
  var sumSize = 0;
  var tmpWeight = scout.arrays.init(weights.length, 0.0);
  var sumWeight = 0;
  for (i = 0; i < sizes.length; i++) {
    outSizes[i] = sizes[i][scout.LayoutConstants.PREF];
    sumSize += outSizes[i];
    tmpWeight[i] = weights[i];
    /**
     * auto correction: if weight is 0 and min / max sizes are NOT equal then
     * set weight to 1; if weight sizes[i][scout.LayoutConstants.MIN]) {
        tmpWeight[i] = 1;
      } else {
        tmpWeight[i] = 0;
      }
    }
    sumWeight += tmpWeight[i];
  }
  // normalize weights
  if (sumWeight > 0) {
    for (i = 0; i < tmpWeight.length; i++) {
      tmpWeight[i] = tmpWeight[i] / sumWeight;
    }
  }
  var deltaInt = targetSize - sumSize;
  // expand or shrink
  if (Math.abs(deltaInt) > 0) {
    // setup accumulators
    /*float[]*/
    var accWeight = scout.arrays.init(tmpWeight.length, 0.0);
    var hasTargets;
    if (deltaInt > 0) {
      // expand
      hasTargets = true;
      while (deltaInt > 0 && hasTargets) {
        hasTargets = false;
        for (i = 0; i < outSizes.length && deltaInt > 0; i++) {
          if (tmpWeight[i] > 0 && outSizes[i] < sizes[i][scout.LayoutConstants.MAX]) {
            hasTargets = true;
            accWeight[i] += tmpWeight[i];
            if (accWeight[i] > 0) {
              accWeight[i] -= 1;
              outSizes[i] += 1;
              deltaInt -= 1;
            }
          }
        }
      }
    } else { // delta<0
      // shrink
      hasTargets = true;
      while (deltaInt < 0 && hasTargets) {
        hasTargets = false;
        for (i = 0; i < outSizes.length && deltaInt < 0; i++) {
          if (tmpWeight[i] > 0 && outSizes[i] > sizes[i][scout.LayoutConstants.MIN]) {
            hasTargets = true;
            accWeight[i] += tmpWeight[i];
            if (accWeight[i] > 0) {
              accWeight[i] -= 1;
              outSizes[i] -= 1;
              deltaInt += 1;
            }
          }
        }
      }
    }
  }
  return outSizes;
};

scout.LogicalGridLayoutInfo.prototype.logicalWidthInPixel = function(cons) {
  var gridW = cons.gridw;
  return (scout.HtmlEnvironment.formColumnWidth * gridW) + (this.m_hgap * Math.max(0, gridW - 1));
};

scout.LogicalGridLayoutInfo.prototype.logicalHeightInPixel = function(cons) {
  var gridH = cons.gridh,
    addition = cons.logicalRowHeightAddition || 0;
  return (scout.HtmlEnvironment.formRowHeight * gridH) + (this.m_vgap * Math.max(0, gridH - 1)) + addition;
};

scout.LogicalGridLayoutInfo.prototype.uiSizeInPixel = function($comp) {
  return scout.HtmlComponent.get($comp).getPreferredSize();
};




© 2015 - 2025 Weber Informatics LLC | Privacy Policy