icloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsapp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
15 KiB
388 lines
15 KiB
/** |
|
* @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', |
|
|
|
requires: [ |
|
'Ext.panel.Table' |
|
], |
|
|
|
firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first', |
|
lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last', |
|
|
|
initLayout: function() { |
|
this.callParent(); |
|
|
|
if (this.scrollbarWidth === undefined) { |
|
this.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width; |
|
} |
|
}, |
|
|
|
beginLayout: function (ownerContext) { |
|
var me = this, |
|
owner = me.owner, |
|
view = owner.grid ? owner.grid.getView() : null, |
|
firstCls = me.firstHeaderCls, |
|
lastCls = me.lastHeaderCls, |
|
bothCls = [firstCls, lastCls], |
|
items = me.getVisibleItems(), |
|
len = items.length, |
|
i, item; |
|
|
|
if (view && view.scrollFlags.x) { |
|
me.viewScrollX = view.getScrollX(); |
|
owner.suspendEvent('scroll'); |
|
view.suspendEvent('scroll'); |
|
} |
|
|
|
me.callParent([ ownerContext ]); |
|
|
|
// Sync the first/lastCls states for all the headers. |
|
for (i = 0; i < len; i++) { |
|
item = items[i]; |
|
|
|
if (len === 1) { |
|
// item is the only item so it is both first and last |
|
item.addCls(bothCls); |
|
} |
|
else if (i === 0) { |
|
// item is the first of 2+ items |
|
item.addCls(firstCls); |
|
item.removeCls(lastCls); |
|
} |
|
else if (i === len - 1) { |
|
// item is the last of 2+ items |
|
item.removeCls(firstCls); |
|
item.addCls(lastCls); |
|
} |
|
else { |
|
item.removeCls(bothCls); |
|
} |
|
} |
|
|
|
// Start this at 0 and for the root headerCt call determineScrollbarWidth to get |
|
// it set properly. Typically that amounts to a "delete" to expose the system's |
|
// scrollbar width stored on our prototype. |
|
|
|
me.scrollbarWidth = 0; |
|
|
|
if (owner.isRootHeader) { |
|
me.determineScrollbarWidth(ownerContext); |
|
} |
|
if (!me.scrollbarWidth) { |
|
// By default Mac OS X has overlay scrollbars that do not take space, but also |
|
// the RTL override may have set this to 0... so make sure we don't try to |
|
// compensate for a scrollbar when there isn't one. |
|
ownerContext.manageScrollbar = false; |
|
} |
|
}, |
|
|
|
moveItemBefore: function (item, before) { |
|
var prevOwner = item.ownerCt; |
|
|
|
// Due to the nature of grid headers, index calculation for |
|
// moving items is complicated, especially since removals can trigger |
|
// groups to be removed (and thus alter indexes). As such, the logic |
|
// is simplified by removing the item first, then calculating the index |
|
// and inserting it |
|
if (item !== before && prevOwner) { |
|
prevOwner.remove(item, false); |
|
} |
|
return this.callParent([item, before]); |
|
}, |
|
|
|
determineScrollbarWidth: function (ownerContext) { |
|
var me = this, |
|
owner = me.owner, |
|
grid = owner.grid, |
|
// locking headerCt can refuse to reserveScrollbar, even if the locking grid |
|
// view does reserveScrollbar (special technique for hiding the vertical |
|
// scrollbar on the locked side) |
|
vetoReserveScrollbar = owner.reserveScrollbar === false, |
|
// We read this value off of the immediate grid since the locked side of a |
|
// locking grid will not have this set. The ownerGrid in that case would have |
|
// it set but will pass along true only to the normal side. |
|
reserveScrollbar = grid.reserveScrollbar && !vetoReserveScrollbar, |
|
manageScrollbar = !reserveScrollbar && !vetoReserveScrollbar && |
|
grid.view.scrollFlags.y; |
|
|
|
// If we have reserveScrollbar then we will always have a vertical scrollbar so |
|
// manageScrollbar should be false. Otherwise it is based on overflow-y: |
|
ownerContext.manageScrollbar = manageScrollbar; |
|
|
|
// Determine if there is any need to deal with the width of the vertical scrollbar |
|
// and set "scrollbarWidth" to 0 if not or the system determined value (stored on |
|
// our prototype). |
|
// |
|
if (!grid.ownerGrid.collapsed && (reserveScrollbar || manageScrollbar)) { |
|
// Ensure the real scrollbarWidth value is exposed from the prototype. This |
|
// may be needed if the scrollFlags have changed since we may have a 0 set on |
|
// this instance from a previous layout run. |
|
delete me.scrollbarWidth; |
|
} |
|
|
|
// On return, the RTL override (Ext.rtl.grid.ColumnLayout) will deal with various |
|
// browser bugs and may set me.scrollbarWidth to 0 or a negative value. |
|
}, |
|
|
|
calculate: function (ownerContext) { |
|
var me = this, |
|
grid = me.owner.grid, |
|
// Our TableLayout buddy sets this in its beginLayout so we can work this |
|
// out together: |
|
viewContext = ownerContext.viewContext, |
|
state = ownerContext.state, |
|
context = ownerContext.context, |
|
lockingPartnerContext, ownerGrid, |
|
columnsChanged, columns, len, i, column, scrollbarAdjustment, viewOverflowY; |
|
|
|
me.callParent([ ownerContext ]); |
|
|
|
if (grid && state.parallelDone) { |
|
lockingPartnerContext = viewContext.lockingPartnerContext; |
|
ownerGrid = grid.ownerGrid; |
|
|
|
// A force-fit needs to be "reflexed" so check that now. If we have to reflex |
|
// the items, we need to re-cacheFlexes and invalidate ourselves. |
|
if (ownerGrid.forceFit && !state.reflexed) { |
|
if (me.convertWidthsToFlexes(ownerContext)) { |
|
me.cacheFlexes(ownerContext); |
|
me.done = false; |
|
ownerContext.invalidate({ |
|
state: { |
|
reflexed: true, |
|
scrollbarAdjustment: me.getScrollbarAdjustment(ownerContext) |
|
} |
|
}); |
|
return; |
|
} |
|
} |
|
|
|
// Once the parallelDone flag goes up, we need to pack up the changed column |
|
// widths for our TableLayout partner. |
|
if ((columnsChanged = state.columnsChanged) === undefined) { |
|
columns = ownerContext.target.getVisibleGridColumns(); |
|
columnsChanged = false; |
|
|
|
for (i = 0, len = columns.length; i < len; i++) { |
|
column = context.getCmp(columns[i]); |
|
// Since we are parallelDone, all of the children should have width, |
|
// so we can |
|
|
|
if (!column.lastBox || column.props.width !== column.lastBox.width) { |
|
(columnsChanged || (columnsChanged = []))[i] = column; |
|
} |
|
} |
|
|
|
state.columnsChanged = columnsChanged; |
|
// This will trigger our TableLayout partner and allow it to proceed. |
|
ownerContext.setProp('columnsChanged', columnsChanged); |
|
} |
|
|
|
if (ownerContext.manageScrollbar) { |
|
// If we changed the column widths, we need to wait for the TableLayout to |
|
// return whether or not we have overflowY... well, that is, if we are |
|
// needing to tweak the scrollbarAdjustment... |
|
scrollbarAdjustment = me.getScrollbarAdjustment(ownerContext); |
|
|
|
if (scrollbarAdjustment) { |
|
// Since we start with the assumption that we will need the scrollbar, |
|
// we now need to wait to see if our guess was correct. |
|
viewOverflowY = viewContext.getProp('viewOverflowY'); |
|
if (viewOverflowY === undefined) { |
|
// The TableLayout has not determined this yet, so park it. |
|
me.done = false; |
|
return; |
|
} |
|
|
|
if (!viewOverflowY) { |
|
// We have our answer, and it turns out the view did not overflow |
|
// (even with the reduced width we gave it), so we need to remove |
|
// the scrollbarAdjustment and go again. |
|
if (lockingPartnerContext) { |
|
// In a locking grid, only the normal side plays this game, |
|
// so now that we know the resolution, we need to invalidate |
|
// the locking view and its headerCt. |
|
lockingPartnerContext.invalidate(); |
|
lockingPartnerContext.headerContext.invalidate(); |
|
} |
|
viewContext.invalidate(); |
|
ownerContext.invalidate({ |
|
state: { |
|
// Pass a 0 adjustment on into our next life. If this is |
|
// the invalidate that resets ownerContext then this is |
|
// put onto the new state. If not, it will reset back to |
|
// undefined and we'll have to begin again (which is the |
|
// correct thing to do in that case). |
|
scrollbarAdjustment: 0 |
|
} |
|
}); |
|
} |
|
} |
|
// else { |
|
// We originally assumed we would need the scrollbar and since we do |
|
// not now, we must be on the second pass, so we can move on... |
|
// } |
|
} |
|
} |
|
}, |
|
|
|
finishedLayout: function(ownerContext) { |
|
var me = this, |
|
owner = me.owner, |
|
view = owner.grid ? owner.grid.getView() : null, |
|
viewScrollX = me.viewScrollX; |
|
|
|
me.callParent([ ownerContext ]); |
|
|
|
// Keep the HeaderContainer's horizontal scroll position synced with where the user |
|
// has scrolled the view to (it will reset during a layout) IF this is a top lever |
|
// grid HeaderContainer and the view is doing horizontal scrolling and the |
|
// HeaderContainer overflows. Only do this after the initial layout where scroll |
|
// position will be at default. |
|
if (view && view.scrollFlags.x) { |
|
if (viewScrollX !== undefined && owner.tooNarrow && owner.componentLayoutCounter) { |
|
owner.setScrollX(viewScrollX); |
|
} |
|
view.resumeEvent('scroll'); |
|
owner.resumeEvent('scroll'); |
|
} |
|
}, |
|
|
|
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; |
|
}, |
|
|
|
getScrollbarAdjustment: function (ownerContext) { |
|
var me = this, |
|
state = ownerContext.state, |
|
grid = me.owner.grid, |
|
scrollbarAdjustment = state.scrollbarAdjustment; |
|
|
|
// If there is potential for a vertical scrollbar, then we start by assuming |
|
// we will need to reserve space for it. Unless, of course, there are no |
|
// records! |
|
if (scrollbarAdjustment === undefined) { |
|
scrollbarAdjustment = 0; |
|
|
|
if (grid.reserveScrollbar || (ownerContext.manageScrollbar && |
|
!grid.ownerGrid.layout.ownerContext.heightModel.shrinkWrap)) { |
|
scrollbarAdjustment = me.scrollbarWidth; |
|
} |
|
|
|
state.scrollbarAdjustment = scrollbarAdjustment; |
|
} |
|
|
|
return scrollbarAdjustment; |
|
}, |
|
|
|
/** |
|
* @private |
|
* Local getContainerSize implementation accounts for vertical scrollbar in the view. |
|
*/ |
|
getContainerSize: function (ownerContext) { |
|
var me = this, |
|
got, needed, padding, gotWidth, gotHeight, width, height, result; |
|
|
|
if (me.owner.isRootHeader) { |
|
result = me.callParent([ ownerContext ]); |
|
|
|
if (result.gotWidth) { |
|
result.width -= me.getScrollbarAdjustment(ownerContext); |
|
} |
|
} else { |
|
padding = ownerContext.paddingContext.getPaddingInfo(); |
|
got = needed = 0; |
|
|
|
// The container size here has to be provided by the ColumnComponentLayout to |
|
// account for borders in its odd way. |
|
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 |
|
}; |
|
} |
|
|
|
return result; |
|
}, |
|
|
|
publishInnerCtSize: function(ownerContext) { |
|
var me = this, |
|
owner = me.owner, |
|
cw = ownerContext.peek('contentWidth'), |
|
adjustment = 0; |
|
|
|
// Pass negative "reservedSpace", so that the innerCt gets *extra* size to accommodate the view's vertical scrollbar |
|
if (cw != null && owner.isRootHeader) { |
|
adjustment = -ownerContext.state.scrollbarAdjustment; |
|
} |
|
|
|
return me.callParent([ownerContext, adjustment]); |
|
} |
|
});
|
|
|