/** * This is a base class for layouts that contain a single item that automatically expands to fill * the layout's container. This class is intended to be extended or created via the `layout: 'fit'` * {@link Ext.container.Container#layout} config, and should generally not need to be created * directly via the new keyword. * * Fit layout does not have any direct config options (other than inherited ones). To fit a panel * to a container using Fit layout, simply set `layout: 'fit'` on the container and add * a single panel to it. * * @example * Ext.create('Ext.panel.Panel', { * title: 'Fit Layout', * width: 300, * height: 150, * layout:'fit', * items: { * title: 'Inner Panel', * html: 'This is the inner panel content', * bodyPadding: 20, * border: false * }, * renderTo: Ext.getBody() * }); * * If the container has multiple items, all of the items will all be equally sized. This is usually * not desired, so to avoid this, place only a **single** item in the container. This sizing * of all items can be used to provide a background {@link Ext.Img image} that is "behind" * another item such as a {@link Ext.view.View dataview} if you also absolutely position the items. */Ext.define('Ext.layout.container.Fit', { extend: 'Ext.layout.container.Container', alternateClassName: ['Ext.layout.FitLayout', 'Ext.layout.Fit'], alias: 'layout.fit', /** * @cfg itemCls * @inheritdoc Ext.layout.container.Container#cfg-itemCls */ itemCls: Ext.baseCSSPrefix + 'fit-item', type: 'fit', manageMargins: true, sizePolicies: [ { readsWidth: 1, readsHeight: 1, setsWidth: 0, setsHeight: 0 }, { readsWidth: 0, readsHeight: 1, setsWidth: 1, setsHeight: 0 }, { readsWidth: 1, readsHeight: 0, setsWidth: 0, setsHeight: 1 }, { readsWidth: 0, readsHeight: 0, setsWidth: 1, setsHeight: 1 } ], getItemSizePolicy: function(item, ownerSizeModel) { // this layout's sizePolicy is derived from its owner's sizeModel: var sizeModel = ownerSizeModel || this.owner.getSizeModel(), mode = (sizeModel.width.shrinkWrap ? 0 : 1) | (sizeModel.height.shrinkWrap ? 0 : 2); return this.sizePolicies[mode]; }, beginLayoutCycle: function(ownerContext, firstCycle) { var me = this, // determine these before the lastSizeModels get updated: resetHeight = me.lastHeightModel && me.lastHeightModel.calculated, resetWidth = me.lastWidthModel && me.lastWidthModel.calculated, resetSizes = resetWidth || resetHeight, maxChildMinHeight = 0, maxChildMinWidth = 0, c, childItems, i, item, length, margins, minHeight, minWidth, style, undef; me.callParent(arguments); // Clear any dimensions which we set before calculation, in case the current // settings affect the available size. This particularly effects self-sizing // containers such as fields, in which the target element is naturally sized, // and should not be stretched by a sized child item. if (resetSizes && ownerContext.targetContext.el.dom.tagName.toUpperCase() !== 'TD') { resetSizes = resetWidth = resetHeight = false; } childItems = ownerContext.childItems; length = childItems.length; for (i = 0; i < length; ++i) { item = childItems[i]; // On the firstCycle, we determine the max of the minWidth/Height of the items // since these can cause the container to grow scrollbars despite our attempts // to fit the child to the container. if (firstCycle) { c = item.target; minHeight = c.minHeight; minWidth = c.minWidth; if (minWidth || minHeight) { margins = item.marginInfo || item.getMarginInfo(); // if the child item has undefined minWidth/Height, these will become // NaN by adding the margins... minHeight += margins.height; minWidth += margins.height; // if the child item has undefined minWidth/Height, these comparisons // will evaluate to false... that is, "0 < NaN" == false... if (maxChildMinHeight < minHeight) { maxChildMinHeight = minHeight; } if (maxChildMinWidth < minWidth) { maxChildMinWidth = minWidth; } } } if (resetSizes) { style = item.el.dom.style; if (resetHeight) { style.height = ''; } if (resetWidth) { style.width = ''; } } } if (firstCycle) { ownerContext.maxChildMinHeight = maxChildMinHeight; ownerContext.maxChildMinWidth = maxChildMinWidth; } // Cache the overflowX/Y flags, but make them false in shrinkWrap mode (since we // won't be triggering overflow in that case) and false if we have no minSize (so // no child to trigger an overflow). c = ownerContext.target; ownerContext.overflowX = (!ownerContext.widthModel.shrinkWrap && ownerContext.maxChildMinWidth && c.scrollFlags.x) || undef; ownerContext.overflowY = (!ownerContext.heightModel.shrinkWrap && ownerContext.maxChildMinHeight && c.scrollFlags.y) || undef; }, calculate: function(ownerContext) { var me = this, childItems = ownerContext.childItems, length = childItems.length, containerSize = me.getContainerSize(ownerContext), info = { length: length, ownerContext: ownerContext, targetSize: containerSize }, shrinkWrapWidth = ownerContext.widthModel.shrinkWrap, shrinkWrapHeight = ownerContext.heightModel.shrinkWrap, overflowX = ownerContext.overflowX, overflowY = ownerContext.overflowY, scrollbars, scrollbarSize, padding, i, contentWidth, contentHeight; ownerContext.state.info = info; if (overflowX || overflowY) { // If we have children that have minHeight/Width, we may be forced to overflow // and gain scrollbars. If so, we want to remove their space from the other // axis so that we fit things inside the scrollbars rather than under them. scrollbars = me.getScrollbarsNeeded( overflowX && containerSize.width, overflowY && containerSize.height, ownerContext.maxChildMinWidth, ownerContext.maxChildMinHeight ); if (scrollbars) { scrollbarSize = Ext.getScrollbarSize(); if (scrollbars & 1) { // if we need the hscrollbar, remove its height containerSize.height -= scrollbarSize.height; } if (scrollbars & 2) { // if we need the vscrollbar, remove its width containerSize.width -= scrollbarSize.width; } } } // If length === 0, it means we either have no child items, or the children are hidden if (length > 0) { // Size the child items to the container (if non-shrinkWrap): for (i = 0; i < length; ++i) { info.index = i; me.fitItem(childItems[i], info); } } else { info.contentWidth = info.contentHeight = 0; } if (shrinkWrapHeight || shrinkWrapWidth) { padding = ownerContext.targetContext.getPaddingInfo(); if (shrinkWrapWidth) { if (overflowY && !containerSize.gotHeight) { // if we might overflow vertically and don't have the container height, // we don't know if we will need a vscrollbar or not, so we must wait // for that height so that we can determine the contentWidth... me.done = false; } else { contentWidth = info.contentWidth + padding.width; // the scrollbar flag (if set) will indicate that an overflow exists on // the horz(1) or vert(2) axis... if not set, then there could never be // an overflow... if (scrollbars & 2) { // if we need the vscrollbar, add its width contentWidth += scrollbarSize.width; } if (!ownerContext.setContentWidth(contentWidth)) { me.done = false; } } } if (shrinkWrapHeight) { if (overflowX && !containerSize.gotWidth) { // if we might overflow horizontally and don't have the container width, // we don't know if we will need a hscrollbar or not, so we must wait // for that width so that we can determine the contentHeight... me.done = false; } else { contentHeight = info.contentHeight + padding.height; // the scrollbar flag (if set) will indicate that an overflow exists on // the horz(1) or vert(2) axis... if not set, then there could never be // an overflow... if (scrollbars & 1) { // if we need the hscrollbar, add its height contentHeight += scrollbarSize.height; } if (!ownerContext.setContentHeight(contentHeight)) { me.done = false; } } } } }, fitItem: function(itemContext, info) { var me = this; if (itemContext.invalid) { me.done = false; return; } info.margins = itemContext.getMarginInfo(); info.needed = info.got = 0; me.fitItemWidth(itemContext, info); me.fitItemHeight(itemContext, info); // If not all required dimensions have been satisfied, we're not done. if (info.got !== info.needed) { me.done = false; } }, fitItemWidth: function(itemContext, info) { var contentWidth, width; // Attempt to set only dimensions that are being controlled, not shrinkWrap dimensions if (info.ownerContext.widthModel.shrinkWrap) { // contentWidth must include the margins to be consistent with setItemWidth width = itemContext.getProp('width') + info.margins.width; // because we add margins, width will be NaN or a number (not undefined) contentWidth = info.contentWidth; if (contentWidth === undefined) { info.contentWidth = width; } else { info.contentWidth = Math.max(contentWidth, width); } } else if (itemContext.widthModel.calculated) { ++info.needed; if (info.targetSize.gotWidth) { ++info.got; this.setItemWidth(itemContext, info); } else { // Too early to position return; } } this.positionItemX(itemContext, info); }, fitItemHeight: function(itemContext, info) { var contentHeight, height; if (info.ownerContext.heightModel.shrinkWrap) { // contentHeight must include the margins to be consistent with setItemHeight height = itemContext.getProp('height') + info.margins.height; // because we add margins, height will be NaN or a number (not undefined) contentHeight = info.contentHeight; if (contentHeight === undefined) { info.contentHeight = height; } else { info.contentHeight = Math.max(contentHeight, height); } } else if (itemContext.heightModel.calculated) { ++info.needed; if (info.targetSize.gotHeight) { ++info.got; this.setItemHeight(itemContext, info); } else { // Too early to position return; } } this.positionItemY(itemContext, info); }, positionItemX: function(itemContext, info) { var margins = info.margins; // Adjust position to account for configured margins or if we have multiple items // (all items should overlap): if (info.index || margins.left) { itemContext.setProp('x', margins.left); } if (margins.width && info.ownerContext.widthModel.shrinkWrap) { // Need the margins for shrink-wrapping but old IE sometimes collapses // the left margin into the padding itemContext.setProp('margin-right', margins.width); } }, positionItemY: function(itemContext, info) { var margins = info.margins; if (info.index || margins.top) { itemContext.setProp('y', margins.top); } if (margins.height && info.ownerContext.heightModel.shrinkWrap) { // Need the margins for shrink-wrapping but old IE sometimes collapses // the top margin into the padding itemContext.setProp('margin-bottom', margins.height); } }, setItemHeight: function(itemContext, info) { itemContext.setHeight(info.targetSize.height - info.margins.height); }, setItemWidth: function(itemContext, info) { itemContext.setWidth(info.targetSize.width - info.margins.width); }});