/** * @private * * This class is used only by the grid's HeaderContainer docked child. * * It adds the ability to shrink the vertical size of the inner container element back if a grouped * column header has all its child columns dragged out, and the whole HeaderContainer needs to shrink back down. * * Also, after every layout, after all headers have attained their 'stretchmax' height, it goes through and calls * `setPadding` on the columns so that they lay out correctly. */ Ext.define('Ext.grid.ColumnLayout', { extend: 'Ext.layout.container.HBox', alias: 'layout.gridcolumn', type : 'gridcolumn', reserveOffset: false, firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first', lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last', initLayout: function() { if (!this.scrollbarWidth) { this.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width; } this.grid = this.owner.up('[scrollerOwner]'); this.callParent(); }, // Collect the height of the table of data upon layout begin beginLayout: function (ownerContext) { var me = this, owner = me.owner, grid = me.grid, view = grid.view, items = me.getVisibleItems(), len = items.length, firstCls = me.firstHeaderCls, lastCls = me.lastHeaderCls, i, item; // If we are one side of a locking grid, then if we are on the "normal" side, we have to grab the normal view // for use in determining whether to subtract scrollbar width from available width. // The locked side does not have scrollbars, so it should not look at the view. if (grid.lockable) { if (owner.up('tablepanel') === view.normalGrid) { view = view.normalGrid.getView(); } else { view = null; } } for (i = 0; i < len; i++) { item = items[i]; // Keep the isLast flag correct so that the column's component layout can know whether or not // it needs a right border. See ColumnComponentLayout.beginLayoutCycle item.isLast = false; item.removeCls([firstCls, lastCls]); if (i === 0) { item.addCls(firstCls); } if (i === len - 1) { item.addCls(lastCls); item.isLast = true; } } me.callParent(arguments); // If the owner is the grid's HeaderContainer, and the UI displays old fashioned scrollbars and there is a rendered View with data in it, // collect the View context to interrogate it for overflow, and possibly invalidate it if there is overflow if (!owner.isColumn && Ext.getScrollbarSize().width && !grid.collapsed && view && view.rendered && (ownerContext.viewTable = view.body.dom)) { ownerContext.viewContext = ownerContext.context.getCmp(view); } }, roundFlex: function(width) { return Math.floor(width); }, calculate: function(ownerContext) { this.callParent(arguments); // If we have calculated the widths, then if forceFit, and there are no flexes, we cannot tell the // TableLayout we are done. We will have to go through the convertWidthsToFlexes stage. if (ownerContext.state.parallelDone && (!this.owner.forceFit || ownerContext.flexedItems.length)) { // TODO: auto width columns aren't necessarily done here. // see view.TableLayout, there is a work around for that there ownerContext.setProp('columnWidthsDone', true); } // Collect the height of the data table if we need it to determine overflow if (ownerContext.viewContext) { ownerContext.state.tableHeight = ownerContext.viewTable.offsetHeight; } }, completeLayout: function(ownerContext) { var me = this, owner = me.owner, state = ownerContext.state; me.callParent(arguments); // If we have not been through this already, and the owning Container is configured // forceFit, is not a group column and and there is a valid width, then convert // widths to flexes, and loop back. if (!ownerContext.flexedItems.length && !state.flexesCalculated && owner.forceFit && // Recalculate based upon all columns now being flexed instead of sized. // Set flag, so that we do not do this infinitely me.convertWidthsToFlexes(ownerContext)) { me.cacheFlexes(ownerContext); ownerContext.invalidate({ state: { flexesCalculated: true } }); } else { ownerContext.setProp('columnWidthsDone', true); } }, convertWidthsToFlexes: function(ownerContext) { var me = this, totalWidth = 0, calculated = me.sizeModels.calculated, childItems, len, i, childContext, item; childItems = ownerContext.childItems; len = childItems.length; for (i = 0; i < len; i++) { childContext = childItems[i]; item = childContext.target; totalWidth += childContext.props.width; // Only allow to be flexed if it's a resizable column if (!(item.fixed || item.resizable === false)) { // For forceFit, just use allocated width as the flex value, and the proportions // will end up the same whatever HeaderContainer width they are being forced into. item.flex = ownerContext.childItems[i].flex = childContext.props.width; item.width = null; childContext.widthModel = calculated; } } // Only need to loop back if the total column width is not already an exact fit return totalWidth !== ownerContext.props.width; }, /** * @private * Local getContainerSize implementation accounts for vertical scrollbar in the view. */ getContainerSize: function(ownerContext) { var me = this, result, viewContext = ownerContext.viewContext, viewHeight; // Column, NOT the main grid's HeaderContainer if (me.owner.isColumn) { result = me.getColumnContainerSize(ownerContext); } // This is the maingrid's HeaderContainer else { result = me.callParent(arguments); // If we've collected a viewContext and we're not shrinkwrapping the height // then we see if we have to narrow the width slightly to account for scrollbar if (viewContext && !viewContext.heightModel.shrinkWrap && viewContext.target.componentLayout.ownerContext) { // if (its layout is running) viewHeight = viewContext.getProp('height'); if (isNaN(viewHeight)) { me.done = false; } else if (ownerContext.state.tableHeight > viewHeight) { result.width -= Ext.getScrollbarSize().width; ownerContext.state.parallelDone = false; viewContext.invalidate(); } } } // TODO - flip the initial assumption to "we have a vscroll" to avoid the invalidate in most // cases (and the expensive ones to boot) return result; }, getColumnContainerSize : function(ownerContext) { var padding = ownerContext.paddingContext.getPaddingInfo(), got = 0, needed = 0, gotWidth, gotHeight, width, height; // In an shrinkWrap width/height case, we must not ask for any of these dimensions // because they will be determined by contentWidth/Height which is calculated by // this layout... // Fit/Card layouts are able to set just the width of children, allowing child's // resulting height to autosize the Container. // See examples/tabs/tabs.html for an example of this. if (!ownerContext.widthModel.shrinkWrap) { ++needed; width = ownerContext.getProp('innerWidth'); gotWidth = (typeof width == 'number'); if (gotWidth) { ++got; width -= padding.width; if (width < 0) { width = 0; } } } if (!ownerContext.heightModel.shrinkWrap) { ++needed; height = ownerContext.getProp('innerHeight'); gotHeight = (typeof height == 'number'); if (gotHeight) { ++got; height -= padding.height; if (height < 0) { height = 0; } } } return { width: width, height: height, needed: needed, got: got, gotAll: got == needed, gotWidth: gotWidth, gotHeight: gotHeight }; }, // FIX: when flexing we actually don't have enough space as we would // typically because of the scrollOffset on the GridView, must reserve this publishInnerCtSize: function(ownerContext) { var me = this, size = ownerContext.state.boxPlan.targetSize, cw = ownerContext.peek('contentWidth'), view; // Allow the other co-operating objects to know whether the columns overflow the available width. me.owner.tooNarrow = ownerContext.state.boxPlan.tooNarrow; // InnerCt MUST stretch to accommodate all columns so that left/right scrolling is enabled in the header container. if ((cw != null) && !me.owner.isColumn) { size.width = cw; // innerCt must also encompass any vertical scrollbar width if there may be one view = me.owner.ownerCt.view; if (view.scrollFlags.y) { size.width += Ext.getScrollbarSize().width; } } return me.callParent(arguments); } });