Source: controls/LayoutGroup.js

var Control = require('../core/Control');

/**
 * The LayoutGroup allows you to add PIXI.js children that will be positioned
 *
 * @class LayoutGroup
 * @extends GOWN.Control
 * @memberof GOWN
 * @constructor
 * @param layout The layout for the layout group {GOWN.LayoutAlignment}
 * @param [maxWidth=Infinity] The maximum width of the layout group {Number}
 * @param [maxHeight=Infinity] The maximum height of the layout group {Number}
 */
function LayoutGroup(layout, maxWidth, maxHeight) {
    /**
     * The layout for the layout group
     *
     * @type GOWN.LayoutAlignment
     */
    this.layout = layout;

    /**
     * The percentage width of the positioned children
     *
     * @type Number
     */
    this.percentWidth = this.percentWidth ;

    /**
     * The percentage height of the positioned children
     *
     * @type Number
     */
    this.percentHeight = this.percentHeight;

    /**
     * The maximum width of the layout group
     *
     * @type Number
     * @default Infinity
     */
    this.maxWidth = maxWidth || Infinity;

    /**
     * The maximum height of the layout group
     *
     * @type Number
     * @default Infinity
     */
    this.maxHeight = maxHeight || Infinity;

    Control.call(this);

    /**
     * Indicates if the layout group has changed
     *
     * @private
     * @type bool
     * @default true
     */
    this._needUpdate = true;

    /**
     * The layout group is resizable
     *
     * @private
     * @type bool
     * @default true
     */
    this.resizable = true;

    this.on('resize', this.onResize, this);
}

LayoutGroup.prototype = Object.create( Control.prototype );
LayoutGroup.prototype.constructor = LayoutGroup;
module.exports = LayoutGroup;

/**
 * Update before draw call (position label)
 * (called from Control.prototype.updateTransform every frame)
 *
 * @protected
 */
LayoutGroup.prototype.redraw = function() {
    var dimensionChanged = false;
    if (this._width && this.maxWidth !== this._width) {
        this._width = Math.min(this._width, this.maxWidth);
        dimensionChanged = true;
    }
    if (this._height && this.maxHeight !== this._height) {
        this._height = Math.min(this._height, this.maxHeight);
        dimensionChanged = true;
    }
    if (this.layout &&
        (this._needUpdate || dimensionChanged || this.layout.needUpdate)) {
        this.layout.layoutContainer(this);
        this._needUpdate = false;
    }
};

/**
 * onResize callback
 *
 * @protected
 */
LayoutGroup.prototype.onResize = function() {
    this._needUpdate = true;
};

/* istanbul ignore next */

/**
 * Adds one or more children to the container.
 *
 * Multiple items can be added like so: `myContainer.addChild(thingOne, thingTwo, thingThree)`
 *
 * @param {...PIXI.DisplayObject} child - The DisplayObject(s) to add to the container
 * @return {PIXI.DisplayObject} The first child that was added.
 */
LayoutGroup.prototype.addChild = function(child) {
    var re = Control.prototype.addChild.call(this, child);
    this._needUpdate = true;
    return re;
};

/* istanbul ignore next */

/**
 * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown
 *
 * @param {PIXI.DisplayObject} child - The child to add
 * @param {number} index - The index to place the child in
 * @return {PIXI.DisplayObject} The child that was added.
 */
LayoutGroup.prototype.addChildAt = function(child, pos) {
    var re = Control.prototype.addChildAt.call(this, child, pos);
    this._needUpdate = true;
    return re;
};

/**
 * Add some space between children
 *
 * @param space Space between children {Number}
 */
LayoutGroup.prototype.addSpacer = function(space) {
    var spacer = new Control();
    spacer.width = spacer.height = space;
    this.addChild(spacer);
};

/**
 * Indicates if the given child is inside the viewport (only used for scrolling)
 *
 * @param child One child with set coordinates and dimensions {PIXI.DisplayObject}
 * @param x X-position on the scroll-container {Number}
 * @param y Y-position on the scroll-container {Number}
 * @param width Width of the viewport {Number}
 * @param height Height of the viewport {Number}
 * @returns {boolean}
 */
LayoutGroup.prototype.childIsRenderAble = function(child, x, y, width, height) {
    return child.x < width + x &&
        child.y < height + y &&
        child.x > x - child.width &&
        child.y > y - child.height;
};

/**
 * Update renderable (culling of non visible objects)
 *
 * @param x X-position on the scroll-container {Number}
 * @param y Y-position on the scroll-container {Number}
 * @param width width of the viewport {Number}
 * @param height height of the viewport {Number}
 */
LayoutGroup.prototype.updateRenderable = function(x, y, width, height) {
    for(var i=0, j=this.children.length; i<j; i++) {
        var child = this.children[i];
        child.renderable = this.childIsRenderAble(child, x, y, width, height);
    }
};

/**
 * The width of the group, will get the position and the width of the right child.
 *
 * @name GOWN.LayoutGroup#width
 * @type Number
 */
Object.defineProperty(LayoutGroup.prototype, 'width', {
    set: function(width) {
        this._width = width;
    },
    get: function() {
        if (this._width > 0) {
            return this._width;
        }
        var width = 0;
        if (this.children) {
            for (var i = 0; i < this.children.length; i++) {
                var child = this.getChildAt(i);
                width = Math.max(width, child.x+child.width);
            }
        }
        return width;
    }
});

/**
 * The height of the group, will get the position and the height of the bottom child.
 *
 * @name GOWN.LayoutGroup#height
 * @type Number
 */
Object.defineProperty(LayoutGroup.prototype, 'height', {
    set: function(height) {
        this._height = height;
    },
    get: function() {
        if (this._height > 0) {
            return this._height;
        }
        var height = 0;
        if (this.children) {
            for (var i = 0; i < this.children.length; i++) {
                var child = this.getChildAt(i);
                height = Math.max(height, child.y+child.height);
            }
        }
        return height;
    }
});