/** * This class is intended to be extended or created via the * {@link Ext.Component#componentLayout layout} configuration property. * See {@link Ext.Component#componentLayout} for additional details. * @private */Ext.define('Ext.layout.component.Component', { extend: 'Ext.layout.Layout', type: 'component', isComponentLayout: true, nullBox: {}, usesContentHeight: true, usesContentWidth: true, usesHeight: true, usesWidth: true, widthCache: {}, heightCache: {}, beginLayoutCycle: function(ownerContext, firstCycle) { var me = this, owner = me.owner, ownerCtContext = ownerContext.ownerCtContext, heightModel = ownerContext.heightModel, widthModel = ownerContext.widthModel, body = owner.el.dom === document.body, lastBox = owner.lastBox || me.nullBox, lastSize = owner.el.lastBox || me.nullBox, dirty = !body, isTopLevel = ownerContext.isTopLevel, ownerLayout, v, width, height, scroller; me.callParent([ownerContext, firstCycle]); if (firstCycle) { scroller = owner.getScrollable && owner.getScrollable(); if (scroller) { scroller.flushOnDomScrollEnd(); } if (me.usesContentWidth) { ++ownerContext.consumersContentWidth; } if (me.usesContentHeight) { ++ownerContext.consumersContentHeight; } if (me.usesWidth) { ++ownerContext.consumersWidth; } if (me.usesHeight) { ++ownerContext.consumersHeight; } if (ownerCtContext && !ownerCtContext.hasRawContent) { ownerLayout = owner.ownerLayout; if (ownerLayout) { if (ownerLayout.usesWidth) { ++ownerContext.consumersWidth; } if (ownerLayout.usesHeight) { ++ownerContext.consumersHeight; } } } } // we want to publish configured dimensions as early as possible and since this is // a write phase... if (widthModel.configured) { // If the owner.el is the body, owner.width is not dirty (we don't want to write // it to the body el). For other el's, the width may already be correct in the // DOM (e.g., it is rendered in the markup initially). If the width is not // correct in the DOM, this is only going to be the case on the first cycle. width = owner[widthModel.names.width]; if (isTopLevel && widthModel.calculatedFrom) { width = lastBox.width; } if (!body) { dirty = me.setWidthInDom || (firstCycle ? width !== lastSize.width : widthModel.constrained); } ownerContext.setWidth(width, dirty); } else if (isTopLevel) { if (widthModel.calculated) { v = lastBox.width; ownerContext.setWidth(v, /* dirty= */v !== lastSize.width); } else if (widthModel.calculatedFromNatural) { owner.el.dom.style.width = owner.width; } v = lastBox.x; ownerContext.setProp('x', v, /* dirty= */v !== lastSize.x); } if (heightModel.configured) { height = owner[heightModel.names.height]; if (isTopLevel && heightModel.calculatedFrom) { height = lastBox.height; } if (!body) { dirty = firstCycle ? height !== lastSize.height : heightModel.constrained; } ownerContext.setHeight(height, dirty); } else if (isTopLevel) { if (heightModel.calculated) { v = lastBox.height; ownerContext.setHeight(v, v !== lastSize.height); } else if (heightModel.calculatedFromNatural) { owner.el.dom.style.height = owner.height; } v = lastBox.y; ownerContext.setProp('y', v, /* dirty= */v !== lastSize.y); } }, finishedLayout: function(ownerContext) { var me = this, elementChildren = ownerContext.children, owner = me.owner, len, i, elContext, lastBox, props; // NOTE: In the code below we cannot use getProp because that will generate // a layout dependency // Set lastBox on managed child Elements. // So that ContextItem.constructor can snag the lastBox for use by its undo method. if (elementChildren) { len = elementChildren.length; for (i = 0; i < len; i++) { elContext = elementChildren[i]; elContext.el.lastBox = elContext.props; } } // Cache the size from which we are changing so that notifyOwner // can notify the owningComponent with all essential information ownerContext.previousSize = me.lastComponentSize; // Cache the currently layed out size me.lastComponentSize = owner.el.lastBox = props = ownerContext.props; // lastBox is a copy of the defined props to allow save/restore of these (panel // collapse needs this) lastBox = owner.lastBox || (owner.lastBox = {}); lastBox.x = props.x; lastBox.y = props.y; lastBox.width = props.width; lastBox.height = props.height; lastBox.invalid = false; me.callParent([ownerContext]); }, notifyOwner: function(ownerContext) { var me = this, currentSize = me.lastComponentSize, prevSize = ownerContext.previousSize; me.owner.afterComponentLayout( currentSize.width, currentSize.height, prevSize ? prevSize.width : undefined, prevSize ? prevSize.height : undefined ); }, /** * Returns the owner component's resize element. * @return {Ext.dom.Element} */ getTarget: function() { return this.owner.el; }, /** * Returns the element into which rendering must take place. * Defaults to the owner Component's encapsulating element. * * May be overridden in Component layout managers which implement an inner element. * @return {Ext.dom.Element} */ getRenderTarget: function() { return this.owner.el; }, cacheTargetInfo: function(ownerContext) { var me = this, targetInfo = me.targetInfo, target; if (!targetInfo) { target = ownerContext.getEl('getTarget', me); me.targetInfo = targetInfo = { padding: target.getPaddingInfo(), border: target.getBorderInfo() }; } return targetInfo; }, measureAutoDimensions: function(ownerContext, dimensions) { // Subtle But Important: // // We don't want to call getProp/hasProp et.al. unless we in fact need that value // for our results! If we call it and don't need it, the layout manager will think // we depend on it and will schedule us again should it change. var me = this, owner = me.owner, containerLayout = owner.layout, heightModel = ownerContext.heightModel, widthModel = ownerContext.widthModel, boxParent = ownerContext.boxParent, isBoxParent = ownerContext.isBoxParent, target = ownerContext.target, props = ownerContext.props, isContainer, ret = { gotWidth: false, gotHeight: false, isContainer: (isContainer = !ownerContext.hasRawContent) }, hv = dimensions || 3, zeroWidth, zeroHeight, needed = 0, got = 0, ready, size, temp, key, cache, dirty; // Note: this method is called *a lot*, so we have to be careful not to waste any // time or make useless calls or, especially, read the DOM when we can avoid it. //--------------------------------------------------------------------- // Width if (widthModel.shrinkWrap && ownerContext.consumersContentWidth) { ++needed; zeroWidth = !(hv & 1); if (isContainer) { // as a componentLayout for a container, we rely on the container layout to // produce contentWidth... if (zeroWidth) { ret.contentWidth = 0; ret.gotWidth = true; ++got; } else if ((ret.contentWidth = ownerContext.getProp('contentWidth')) !== undefined) { ret.gotWidth = true; ++got; } } else { size = props.contentWidth; if (typeof size === 'number') { // if (already determined) ret.contentWidth = size; ret.gotWidth = true; ++got; } else { if (zeroWidth) { ready = true; } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) { ready = false; } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) { // if we have no boxParent, we are ready, but a shrinkWrap boxParent // artificially provides width early in the measurement process so // we are ready to go in that case as well... ready = true; } else { // lastly, we have a boxParent that will be given a width, so we // can wait for that width to be set in order to properly measure // whatever is inside... ready = boxParent.hasDomProp('width'); } if (ready) { if (zeroWidth) { temp = 0; } else if (containerLayout && containerLayout.measureContentWidth) { // Allow the container layout to do the measurement since it // may have a better idea of how to do it even with no items: temp = containerLayout.measureContentWidth(ownerContext); } else { if (target.cacheWidth) { // if all instances of a given xtype/UI are the same size, // only read the DOM once to measure the first instance. // Thereafter, retrieve the width from the cache. key = target.xtype + '-' + target.ui; cache = me.widthCache; temp = cache[key] || (cache[key] = me.measureContentWidth(ownerContext)); } else { temp = me.measureContentWidth(ownerContext); } } if (!isNaN(ret.contentWidth = temp)) { ownerContext.setContentWidth(temp, true); ret.gotWidth = true; ++got; } } } } } else if (widthModel.natural && ownerContext.consumersWidth) { ++needed; size = props.width; // zeroWidth does not apply if (typeof size === 'number') { // if (already determined) ret.width = size; ret.gotWidth = true; ++got; } else { if (isBoxParent || !boxParent) { ready = true; } else { // lastly, we have a boxParent that will be given a width, so we // can wait for that width to be set in order to properly measure // whatever is inside... ready = boxParent.hasDomProp('width'); } if (ready) { if (!isNaN(ret.width = me.measureOwnerWidth(ownerContext))) { // if minWidth/maxWidth was specified, we need to mark this as dirty // so the new ret.width is applied to this context. dirty = !!((target.minWidth || target.maxWidth) && typeof target.width !== 'number'); ownerContext.setWidth(ret.width, dirty); ret.gotWidth = true; ++got; } } } } //--------------------------------------------------------------------- // Height if (heightModel.shrinkWrap && ownerContext.consumersContentHeight) { ++needed; zeroHeight = !(hv & 2); if (isContainer) { // don't ask unless we need to know... if (zeroHeight) { ret.contentHeight = 0; ret.gotHeight = true; ++got; } // eslint-disable-next-line no-cond-assign, max-len else if ((ret.contentHeight = ownerContext.getProp('contentHeight')) !== undefined) { ret.gotHeight = true; ++got; } } else { size = props.contentHeight; if (typeof size === 'number') { // if (already determined) ret.contentHeight = size; ret.gotHeight = true; ++got; } else { if (zeroHeight) { ready = true; } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) { ready = false; } else if (owner.noWrap) { ready = true; } else if (!widthModel.shrinkWrap) { // fixed width, so we need the width to determine the height... // eslint-disable-next-line max-len ready = (ownerContext.bodyContext || ownerContext).hasDomProp('width');// && (!ownerContext.bodyContext || ownerContext.bodyContext.hasDomProp('width')); } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) { // if we have no boxParent, we are ready, but an autoWidth boxParent // artificially provides width early in the measurement process so // we are ready to go in that case as well... ready = true; } else { // lastly, we have a boxParent that will be given a width, so we // can wait for that width to be set in order to properly measure // whatever is inside... ready = boxParent.hasDomProp('width'); } if (ready) { if (zeroHeight) { temp = 0; } else if (containerLayout && containerLayout.measureContentHeight) { // Allow the container layout to do the measurement since it // may have a better idea of how to do it even with no items: temp = containerLayout.measureContentHeight(ownerContext); } else { if (target.cacheHeight) { // if all instances of a given xtype/UI are the same size, // only read the DOM once to measure the first instance. // Thereafter, retrieve the height from the cache. key = target.xtype + '-' + target.ui; cache = me.heightCache; temp = cache[key] || (cache[key] = me.measureContentHeight(ownerContext)); } else { temp = me.measureContentHeight(ownerContext); } } if (!isNaN(ret.contentHeight = temp)) { ownerContext.setContentHeight(temp, true); ret.gotHeight = true; ++got; } } } } } else if (heightModel.natural && ownerContext.consumersHeight) { ++needed; size = props.height; // zeroHeight does not apply if (typeof size === 'number') { // if (already determined) ret.height = size; ret.gotHeight = true; ++got; } else { if (isBoxParent || !boxParent) { ready = true; } else { // lastly, we have a boxParent that will be given a width, so we // can wait for that width to be set in order to properly measure // whatever is inside... ready = boxParent.hasDomProp('width'); } if (ready) { if (!isNaN(ret.height = me.measureOwnerHeight(ownerContext))) { // if minHeight/maxHeight was specified, we need to mark this as dirty // so the new ret.height is applied to this context. dirty = !!((target.minHeight || target.maxHeight) && typeof target.height !== 'number'); ownerContext.setHeight(ret.height, dirty); ret.gotHeight = true; ++got; } } } } if (boxParent) { ownerContext.onBoxMeasured(); } ret.gotAll = got === needed; // see if we can avoid calling this method by storing something on ownerContext. return ret; }, measureContentWidth: function(ownerContext) { // contentWidth includes padding, but not border, framing or margins return ownerContext.el.getWidth() - ownerContext.getFrameInfo().width; }, measureContentHeight: function(ownerContext) { // contentHeight includes padding, but not border, framing or margins return ownerContext.el.getHeight() - ownerContext.getFrameInfo().height; }, measureOwnerHeight: function(ownerContext) { return ownerContext.el.getHeight(); }, measureOwnerWidth: function(ownerContext) { return ownerContext.el.getWidth(); }});