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

rwt.widgets.util.HorizontalBoxLayoutImpl.js Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2004, 2015 1&1 Internet AG, Germany, http://www.1und1.de,
 *                          EclipseSource and others.
 * 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:
 *    1&1 Internet AG and others - original API and implementation
 *    EclipseSource - adaptation for the Eclipse Remote Application Platform
 ******************************************************************************/

rwt.qx.Class.define("rwt.widgets.util.HorizontalBoxLayoutImpl",
{
  extend : rwt.widgets.util.LayoutImpl,



  /*
  *****************************************************************************
     PROPERTIES
  *****************************************************************************
  */

  properties :
  {
    enableFlexSupport :
    {
      check : "Boolean",
      init : true
    }
  },




  /*
  *****************************************************************************
     MEMBERS
  *****************************************************************************
  */

  members :
  {
    /*
    ---------------------------------------------------------------------------
      [01] COMPUTE BOX DIMENSIONS FOR AN INDIVIDUAL CHILD
    ---------------------------------------------------------------------------
    */

    /** Compute and return the box width of the given child. */
    /**
     * Global Structure:
     *  [01] COMPUTE BOX DIMENSIONS FOR AN INDIVIDUAL CHILD
     *  [02] COMPUTE NEEDED DIMENSIONS FOR AN INDIVIDUAL CHILD
     *  [03] COMPUTE NEEDED DIMENSIONS FOR ALL CHILDREN
     *  [04] UPDATE LAYOUT WHEN A CHILD CHANGES ITS OUTER DIMENSIONS
     *  [05] UPDATE CHILD ON INNER DIMENSION CHANGES OF LAYOUT
     *  [06] UPDATE LAYOUT ON JOB QUEUE FLUSH
     *  [07] UPDATE CHILDREN ON JOB QUEUE FLUSH
     *  [08] CHILDREN ADD/REMOVE/MOVE HANDLING
     *  [09] FLUSH LAYOUT QUEUES OF CHILDREN
     *  [10] LAYOUT CHILD
     *
     *  Inherits from rwt.widgets.util.LayoutImpl:
     *  [02] COMPUTE NEEDED DIMENSIONS FOR AN INDIVIDUAL CHILD
     *
     * @type member
     * @param vChild {var} TODOC
     * @return {var} TODOC
     */
    computeChildBoxWidth : function(vChild) {
      return vChild.getWidthValue() || vChild._computeBoxWidthFallback();
    },


    /**
     * Compute and return the box height of the given child.
     *
     * @type member
     * @param vChild {var} TODOC
     * @return {var} TODOC
     */
    computeChildBoxHeight : function(vChild)
    {
      if (this.getWidget().getStretchChildrenOrthogonalAxis() && vChild._computedHeightTypeNull && vChild.getAllowStretchY()) {
        return this.getWidget().getInnerHeight();
      }

      return vChild.getHeightValue() || vChild._computeBoxHeightFallback();
    },


    /**
     * Computes the width of all flexible children.
     *
     * @type member
     * @return {void}
     */
    computeChildrenFlexWidth : function()
    {
      if (this._childrenFlexWidthComputed || !this.getEnableFlexSupport()) {
        return;
      }

      this._childrenFlexWidthComputed = true;

      var vWidget = this.getWidget();
      var vChildren = vWidget.getVisibleChildren();
      var vChildrenLength = vChildren.length;
      var vCurrentChild;
      var vFlexibleChildren = [];
      var vAvailWidth = vWidget.getInnerWidth();
      var vUsedWidth = vWidget.getSpacing() * (vChildrenLength - 1);
      var vIterator;

      // *************************************************************
      // 1. Compute the sum of all static sized children and finding
      //    all flexible children.
      // *************************************************************
      for (vIterator=0; vIterator 0;

          for (vIterator=vFlexibleChildrenLength-1; vIterator>=0; vIterator--)
          {
            vCurrentChild = vFlexibleChildren[vIterator];

            if (vUp)
            {
              vAdjust = (vCurrentChild.getMaxWidthValue() || Infinity) - vCurrentChild._computedWidthFlexValue;

              if (vAdjust > 0) {
                vCurrentChild._allocationLoops = Math.floor(vAdjust / vCurrentChild._computedWidthParsed);
              }
              else
              {
                rwt.util.Arrays.removeAt(vFlexibleChildren, vIterator);

                vCurrentChild._computedWidthFlexValue = Math.round(vCurrentChild._computedWidthFlexValue);
                vUsedWidth += Math.round(vCurrentChild._computedWidthFlexValue + vAdjust);
              }
            }
            else
            {
              vAdjust = rwt.util.Number.isNumber(vCurrentChild.getMinWidthValue()) ? vCurrentChild._computedWidthFlexValue - vCurrentChild.getMinWidthValue() : vCurrentChild._computedWidthFlexValue;

              if (vAdjust > 0) {
                vCurrentChild._allocationLoops = Math.floor(vAdjust / vCurrentChild._computedWidthParsed);
              }
              else
              {
                rwt.util.Arrays.removeAt(vFlexibleChildren, vIterator);

                vCurrentChild._computedWidthFlexValue = Math.round(vCurrentChild._computedWidthFlexValue);
                vUsedWidth += Math.round(vCurrentChild._computedWidthFlexValue - vAdjust);
              }
            }
          }

          // *************************************************************
          // 6. Try to reallocate the width between flexible children
          //    so that the requirements through min/max limits
          //    are satisfied.
          // *************************************************************
          while (vAllocationDiff !== 0 && vFlexibleChildrenLength > 0)
          {
            vFlexibleChildrenLength = vFlexibleChildren.length;
            vMinAllocationLoops = Infinity;
            vFactorSum = 0;

            // Find minimal loop amount
            for (vIterator=0; vIterator=0; vIterator--)
            {
              vCurrentChild = vFlexibleChildren[vIterator];
              vCurrentChild._computedWidthFlexValue += vCurrentAllocationSum / vFactorSum * vCurrentChild._computedWidthParsed;

              if (vCurrentChild._allocationLoops == vMinAllocationLoops)
              {
                vCurrentChild._computedWidthFlexValue = Math.round(vCurrentChild._computedWidthFlexValue);

                vUsedWidth += vCurrentChild._computedWidthFlexValue;
                delete vCurrentChild._allocationLoops;
                rwt.util.Arrays.removeAt(vFlexibleChildren, vIterator);
              }
              else
              {
                if (vAllocationDiff === 0)
                {
                  vCurrentChild._computedWidthFlexValue = Math.round(vCurrentChild._computedWidthFlexValue);
                  vUsedWidth += vCurrentChild._computedWidthFlexValue;
                  delete vCurrentChild._allocationLoops;
                }
                else
                {
                  vCurrentChild._allocationLoops -= vMinAllocationLoops;
                }
              }
            }
          }
        }
      }

      // *************************************************************
      // 7. Fix rounding errors
      // *************************************************************
      vCurrentChild._computedWidthFlexValue += vAvailWidth - vUsedWidth;
    },


    /**
     * TODOC
     *
     * @type member
     * @return {void}
     */
    invalidateChildrenFlexWidth : function() {
      delete this._childrenFlexWidthComputed;
    },




    /*
    ---------------------------------------------------------------------------
      [03] COMPUTE NEEDED DIMENSIONS FOR ALL CHILDREN
    ---------------------------------------------------------------------------
    */

    /**
     * Compute and return the width needed by all children of this widget
     *
     * @type member
     * @return {var} TODOC
     */
    computeChildrenNeededWidth : function()
    {
      var w = this.getWidget();
      return rwt.widgets.util.LayoutImpl.prototype.computeChildrenNeededWidth_sum.call(this) + ((w.getVisibleChildrenLength() - 1) * w.getSpacing());
    },




    /*
    ---------------------------------------------------------------------------
      [04] UPDATE LAYOUT WHEN A CHILD CHANGES ITS OUTER DIMENSIONS
    ---------------------------------------------------------------------------
    */

    /**
     * Things to do and layout when any of the childs changes its outer width.
     *  Needed by layouts where the children depends on each-other, like flow- or box-layouts.
     *
     * @type member
     * @param vChild {var} TODOC
     * @return {void}
     */
    updateSelfOnChildOuterWidthChange : function()
    {
      // if a childrens outer width changes we need to update our accumulated
      // width of all childrens (used for center or right alignments)
      this.getWidget()._invalidateAccumulatedChildrenOuterWidth();
    },




    /*
    ---------------------------------------------------------------------------
      [05] UPDATE CHILD ON INNER DIMENSION CHANGES OF LAYOUT
    ---------------------------------------------------------------------------
    */

    /**
     * Actions that should be done if the inner width of the widget was changed.
     *  Normally this includes update to percent values and ranges.
     *
     * @type member
     * @param vChild {var} TODOC
     * @return {var} TODOC
     */
    updateChildOnInnerWidthChange : function(vChild)
    {
      if (this.getWidget().getHorizontalChildrenAlign() == "center") {
        vChild.addToLayoutChanges("locationX");
      }

      // use variables here to be sure to call both methods.
      var vUpdatePercent = vChild._recomputePercentX();
      var vUpdateFlex = vChild._recomputeFlexX();

      // inform the caller if there were any notable changes occured
      return vUpdatePercent || vUpdateFlex;
    },


    /**
     * Actions that should be done if the inner height of the widget was changed.
     *  Normally this includes update to percent values and ranges.
     *
     * @type member
     * @param vChild {var} TODOC
     * @return {var} TODOC
     */
    updateChildOnInnerHeightChange : function(vChild)
    {
      // use variables here to be sure to call both methods.
      var vUpdatePercent = vChild._recomputePercentY();
      var vUpdateStretch = vChild._recomputeStretchingY();

      // priority to childs internal alignment
      if ((vChild.getVerticalAlign() || this.getWidget().getVerticalChildrenAlign()) == "middle") {
        vChild.addToLayoutChanges("locationY");
      }

      // inform the caller if there were any notable changes occured
      return vUpdatePercent || vUpdateStretch;
    },




    /*
    ---------------------------------------------------------------------------
      [06] UPDATE LAYOUT ON JOB QUEUE FLUSH
    ---------------------------------------------------------------------------
    */

    /**
     * Invalidate and recompute things because of job in queue (before the rest of job handling will be executed).
     *
     * @type member
     * @param vJobQueue {var} TODOC
     * @return {void}
     */
    updateSelfOnJobQueueFlush : function(vJobQueue)
    {
      if (vJobQueue.addChild || vJobQueue.removeChild) {
        this.getWidget()._invalidateAccumulatedChildrenOuterWidth();
      }
    },




    /*
    ---------------------------------------------------------------------------
      [07] UPDATE CHILDREN ON JOB QUEUE FLUSH
    ---------------------------------------------------------------------------
    */

    /**
     * Updates children on special jobs
     *
     * @type member
     * @param vQueue {var} TODOC
     * @return {boolean}
     */
    updateChildrenOnJobQueueFlush : function(vQueue)
    {
      var vStretchX = false, vStretchY = false;
      var vWidget = this.getWidget();

      // switching the orientation need updates for stretching on both axis
      if (vQueue.orientation) {
        vStretchX = vStretchY = true;
      }

      // different updates depending from the current orientation (or the new one)
      if (vQueue.spacing || vQueue.orientation || vQueue.reverseChildrenOrder || vQueue.horizontalChildrenAlign) {
        vWidget._addChildrenToLayoutQueue("locationX");
      }

      if (vQueue.verticalChildrenAlign) {
        vWidget._addChildrenToLayoutQueue("locationY");
      }

      if (vQueue.stretchChildrenOrthogonalAxis) {
        vStretchY = true;
      }

      // if stretching should be reworked reset the previous one and add
      // a layout job to update the width respectively height.
      if (vStretchX)
      {
        vWidget._recomputeChildrenStretchingX();
        vWidget._addChildrenToLayoutQueue("width");
      }

      if (vStretchY)
      {
        vWidget._recomputeChildrenStretchingY();
        vWidget._addChildrenToLayoutQueue("height");
      }

      return true;
    },




    /*
    ---------------------------------------------------------------------------
      [08] CHILDREN ADD/REMOVE/MOVE HANDLING
    ---------------------------------------------------------------------------
    */

    /**
     * This method combines calls of methods which should be done if a widget should be removed from the current layout.
     *  Needed by layouts where the children depends on each-other, like flow- or box-layouts.
     *
     * @type member
     * @param vChild {var} TODOC
     * @param vIndex {var} TODOC
     * @return {void}
     */
    updateChildrenOnRemoveChild : function(vChild, vIndex)
    {
      var w = this.getWidget(), ch = w.getVisibleChildren(), chl = ch.length, chc, i = -1;

      // Fix index to be at the first flex child
      if (this.getEnableFlexSupport())
      {
        for (i=0; i=0&&!vChildrenQueue[ch[i].toHashCode()]; i--) {}

          // layout all children before this last child
          for (var j=0; j<=i; j++) {
            w._layoutChild(chc = ch[j]);
          }

          break;

        case "center":
        case "center-reversed":
          // re-layout all children
          i = -1;

          chc = ch[++i];
          while( chc ) {
            w._layoutChild(chc);
            chc = ch[++i];
          }

          break;

        default:
          // layout all childs from the first child
          // with an own layout request to the end
          i = -1;
          var changed = false;

          chc = ch[++i];
          while( chc ) {
            if (changed || vChildrenQueue[chc.toHashCode()])
            {
              w._layoutChild(chc);
              changed = true;
            }
            chc = ch[++i];
          }
      }
    },




    /*
    ---------------------------------------------------------------------------
      [10] LAYOUT CHILD
    ---------------------------------------------------------------------------
    */

    /**
     * This is called from rwt.widgets.base.Widget and  it's task is to apply the layout
     *  (excluding border and padding) to the child.
     *
     * @type member
     * @param vChild {var} TODOC
     * @param vJobs {var} TODOC
     * @return {void}
     */
    layoutChild : function(vChild, vJobs)
    {
      this.layoutChild_sizeX(vChild, vJobs);
      this.layoutChild_sizeY(vChild, vJobs);

      this.layoutChild_sizeLimitX(vChild, vJobs);
      this.layoutChild_sizeLimitY(vChild, vJobs);

      this.layoutChild_locationX(vChild, vJobs);
      this.layoutChild_locationY(vChild, vJobs);

      this.layoutChild_marginX(vChild, vJobs);
      this.layoutChild_marginY(vChild, vJobs);
    },


    /**
     * TODOC
     *
     * @type member
     * @param vChild {var} TODOC
     * @param vJobs {var} TODOC
     * @return {void}
     * @signature function(vChild, vJobs)
     */
    layoutChild_sizeX : rwt.util.Variant.select("qx.client",
    {
      "trident|webkit|blink" : function(vChild, vJobs)
      {
        if (vJobs.initial || vJobs.width || vJobs.minWidth || vJobs.maxWidth)
        {
          if (vChild._isWidthEssential() && (!vChild._computedWidthTypeNull || !vChild._computedMinWidthTypeNull || !vChild._computedMaxWidthTypeNull)) {
            vChild._renderRuntimeWidth(vChild.getBoxWidth());
          } else {
            vChild._resetRuntimeWidth();
          }
        }
      },

      "default" : function(vChild, vJobs)
      {
        if (vJobs.initial || vJobs.width)
        {
          if (vChild._isWidthEssential() && !vChild._computedWidthTypeNull) {
            vChild._renderRuntimeWidth(vChild.getWidthValue());
          } else {
            vChild._resetRuntimeWidth();
          }
        }
      }
    }),


    /**
     * TODOC
     *
     * @type member
     * @param vChild {var} TODOC
     * @param vJobs {var} TODOC
     * @return {void}
     * @signature function(vChild, vJobs)
     */
    layoutChild_sizeY : rwt.util.Variant.select("qx.client",
    {
      "trident|webkit|blink" : function(vChild, vJobs)
      {
        if (vJobs.initial || vJobs.height || vJobs.minHeight || vJobs.maxHeight)
        {
          if ((vChild._isHeightEssential() && (!vChild._computedHeightTypeNull || !vChild._computedMinHeightTypeNull || !vChild._computedMaxHeightTypeNull)) || (vChild.getAllowStretchY() && this.getWidget().getStretchChildrenOrthogonalAxis())) {
            vChild._renderRuntimeHeight(vChild.getBoxHeight());
          } else {
            vChild._resetRuntimeHeight();
          }
        }
      },

      "default" : function(vChild, vJobs)
      {
        if (vJobs.initial || vJobs.height)
        {
          if (vChild._isHeightEssential() && !vChild._computedHeightTypeNull) {
            vChild._renderRuntimeHeight(vChild.getHeightValue());
          } else {
            vChild._resetRuntimeHeight();
          }
        }
      }
    }),


    /**
     * TODOC
     *
     * @type member
     * @param vChild {var} TODOC
     * @param vJobs {var} TODOC
     * @return {void}
     */
    layoutChild_locationX : function(vChild)
    {
      var vWidget = this.getWidget();

      // handle first child
      if (vWidget.getFirstVisibleChild() == vChild)
      {
        switch(vWidget.getLayoutMode())
        {
          case "right":
          case "left-reversed":
            var vPos = vWidget.getPaddingRight() + vWidget.getAccumulatedChildrenOuterWidth() - vChild.getOuterWidth();
            break;

          case "center":
          case "center-reversed":
            var vPos = vWidget.getPaddingLeft() + Math.round((vWidget.getInnerWidth() - vWidget.getAccumulatedChildrenOuterWidth()) / 2);
            break;

          default:
            var vPos = vWidget.getPaddingLeft();
        }
      }

      // handle any following child
      else
      {
        var vPrev = vChild.getPreviousVisibleSibling();

        switch(vWidget.getLayoutMode())
        {
          case "right":
          case "left-reversed":
            var vPos = vPrev._cachedLocationHorizontal - vChild.getOuterWidth() - vWidget.getSpacing();
            break;

          default:
            var vPos = vPrev._cachedLocationHorizontal + vPrev.getOuterWidth() + vWidget.getSpacing();
        }
      }

      // store for next sibling
      vChild._cachedLocationHorizontal = vPos;

      // apply styles
      switch(vWidget.getLayoutMode())
      {
        case "right":
        case "right-reversed":
        case "center-reversed":
          // add relative positions (like 'position:relative' in css)
          vPos += !vChild._computedRightTypeNull ? vChild.getRightValue() : !vChild._computedLeftTypeNull ? -(vChild.getLeftValue()) : 0;

          vChild._resetRuntimeLeft();
          vChild._renderRuntimeRight(vPos);
          break;

        default:
          // add relative positions (like 'position:relative' in css)
          vPos += !vChild._computedLeftTypeNull ? vChild.getLeftValue() : !vChild._computedRightTypeNull ? -(vChild.getRightValue()) : 0;

          vChild._resetRuntimeRight();
          vChild._renderRuntimeLeft(vPos);
      }
    },


    /**
     * TODOC
     *
     * @type member
     * @param vChild {var} TODOC
     * @param vJobs {var} TODOC
     * @return {void}
     */
    layoutChild_locationY : function(vChild)
    {
      var vWidget = this.getWidget();

      // special stretching support
      if( rwt.client.Client.isGecko() )
      {
        if (vChild.getAllowStretchY() && vWidget.getStretchChildrenOrthogonalAxis() && vChild._computedHeightTypeNull)
        {
          vChild._renderRuntimeTop(vWidget.getPaddingTop() || 0);
          vChild._renderRuntimeBottom(vWidget.getPaddingBottom() || 0);

          return;
        }
      }

      // priority to childs internal alignment
      var vAlign = vChild.getVerticalAlign() || vWidget.getVerticalChildrenAlign();

      // handle middle alignment
      var vPos = vAlign == "middle" ? Math.round((vWidget.getInnerHeight() - vChild.getOuterHeight()) / 2) : 0;

      // the bottom alignment use the real 'bottom' styleproperty to
      // use the best available method in modern browsers
      if (vAlign == "bottom")
      {
        // add parent padding
        vPos += vWidget.getPaddingBottom();

        // relative positions (like 'position:relative' in css)
        if (!vChild._computedBottomTypeNull) {
          vPos += vChild.getBottomValue();
        } else if (!vChild._computedTopTypeNull) {
          vPos -= vChild.getTopValue();
        }

        // apply styles
        vChild._resetRuntimeTop();
        vChild._renderRuntimeBottom(vPos);
      }
      else
      {
        // add parent padding
        vPos += vWidget.getPaddingTop();

        // relative positions (like 'position:relative' in css)
        if (!vChild._computedTopTypeNull) {
          vPos += vChild.getTopValue();
        } else if (!vChild._computedBottomTypeNull) {
          vPos -= vChild.getBottomValue();
        }

        // apply styles
        vChild._resetRuntimeBottom();
        vChild._renderRuntimeTop(vPos);
      }
    }
  }
});




© 2015 - 2025 Weber Informatics LLC | Privacy Policy