slackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangouts
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.
1598 lines
62 KiB
1598 lines
62 KiB
/** |
|
* This ComponentLayout handles docking for Panels. It takes care of panels that are |
|
* part of a ContainerLayout that sets this Panel's size and Panels that are part of |
|
* an AutoContainerLayout in which this panel get his height based of the CSS or |
|
* its content. |
|
* @private |
|
*/ |
|
Ext.define('Ext.layout.component.Dock', { |
|
|
|
/* Begin Definitions */ |
|
|
|
extend: 'Ext.layout.component.Component', |
|
|
|
alias: 'layout.dock', |
|
|
|
alternateClassName: 'Ext.layout.component.AbstractDock', |
|
|
|
/* End Definitions */ |
|
|
|
type: 'dock', |
|
|
|
horzAxisProps: { |
|
name: 'horz', |
|
oppositeName: 'vert', |
|
dockBegin: 'left', |
|
dockEnd: 'right', |
|
horizontal: true, |
|
marginBegin: 'margin-left', |
|
maxSize: 'maxWidth', |
|
minSize: 'minWidth', |
|
pos: 'x', |
|
setSize: 'setWidth', |
|
shrinkWrapDock: 'shrinkWrapDockWidth', |
|
size: 'width', |
|
sizeModel: 'widthModel' |
|
}, |
|
|
|
vertAxisProps: { |
|
name: 'vert', |
|
oppositeName: 'horz', |
|
dockBegin: 'top', |
|
dockEnd: 'bottom', |
|
horizontal: false, |
|
marginBegin: 'margin-top', |
|
maxSize: 'maxHeight', |
|
minSize: 'minHeight', |
|
pos: 'y', |
|
setSize: 'setHeight', |
|
shrinkWrapDock: 'shrinkWrapDockHeight', |
|
size: 'height', |
|
sizeModel: 'heightModel' |
|
}, |
|
|
|
initializedBorders: -1, |
|
|
|
horizontalCollapsePolicy: { width: true, x: true }, |
|
|
|
verticalCollapsePolicy: { height: true, y: true }, |
|
|
|
finishRender: function () { |
|
var me = this, |
|
target, items; |
|
|
|
me.callParent(); |
|
|
|
target = me.getRenderTarget(); |
|
items = me.getDockedItems(); |
|
|
|
me.finishRenderItems(target, items); |
|
}, |
|
|
|
isItemBoxParent: function (itemContext) { |
|
return true; |
|
}, |
|
|
|
isItemShrinkWrap: function (item) { |
|
return true; |
|
}, |
|
|
|
noBorderClasses: [ |
|
Ext.baseCSSPrefix + 'docked-noborder-top', |
|
Ext.baseCSSPrefix + 'docked-noborder-right', |
|
Ext.baseCSSPrefix + 'docked-noborder-bottom', |
|
Ext.baseCSSPrefix + 'docked-noborder-left' |
|
], |
|
|
|
noBorderClassesSides: { |
|
top: Ext.baseCSSPrefix + 'docked-noborder-top', |
|
right: Ext.baseCSSPrefix + 'docked-noborder-right', |
|
bottom: Ext.baseCSSPrefix + 'docked-noborder-bottom', |
|
left: Ext.baseCSSPrefix + 'docked-noborder-left' |
|
}, |
|
|
|
borderWidthProps: { |
|
top: 'border-top-width', |
|
right: 'border-right-width', |
|
bottom: 'border-bottom-width', |
|
left: 'border-left-width' |
|
}, |
|
|
|
_itemCls: Ext.baseCSSPrefix + 'docked', |
|
|
|
handleItemBorders: function() { |
|
var me = this, |
|
owner = me.owner, |
|
borders, docked, |
|
lastItems = me.lastDockedItems, |
|
oldBorders = me.borders, |
|
currentGeneration = owner.dockedItems.generation, |
|
noBorderClassesSides = me.noBorderClassesSides, |
|
borderWidthProps = me.borderWidthProps, |
|
i, ln, item, dock, side, |
|
collapsed = me.collapsed; |
|
|
|
// If we're collapsed with mini, it means we're in a border layout so we'll |
|
// be visibility: hidden |
|
if (me.initializedBorders === currentGeneration || (owner.border && !owner.manageBodyBorders) || (owner.collapsed && owner.collapseMode === 'mini')) { |
|
return; |
|
} |
|
|
|
me.initializedBorders = currentGeneration; |
|
|
|
// Borders have to be calculated using expanded docked item collection. |
|
me.collapsed = false; |
|
me.lastDockedItems = docked = me.getLayoutItems(); |
|
me.collapsed = collapsed; |
|
|
|
borders = { top: [], right: [], bottom: [], left: [] }; |
|
|
|
for (i = 0, ln = docked.length; i < ln; i++) { |
|
item = docked[i]; |
|
dock = item.dock; |
|
|
|
if (item.ignoreBorderManagement) { |
|
continue; |
|
} |
|
|
|
if (!borders[dock].satisfied) { |
|
borders[dock].push(item); |
|
borders[dock].satisfied = true; |
|
} |
|
|
|
if (!borders.top.satisfied && dock !== 'bottom') { |
|
borders.top.push(item); |
|
} |
|
if (!borders.right.satisfied && dock !== 'left') { |
|
borders.right.push(item); |
|
} |
|
if (!borders.bottom.satisfied && dock !== 'top') { |
|
borders.bottom.push(item); |
|
} |
|
if (!borders.left.satisfied && dock !== 'right') { |
|
borders.left.push(item); |
|
} |
|
} |
|
|
|
if (lastItems) { |
|
for (i = 0, ln = lastItems.length; i < ln; i++) { |
|
item = lastItems[i]; |
|
if (!item.isDestroyed && !item.ignoreBorderManagement && !owner.manageBodyBorders) { |
|
item.removeCls(me.noBorderClasses); |
|
} |
|
} |
|
} |
|
|
|
if (oldBorders) { |
|
for (side in oldBorders) { |
|
if (owner.manageBodyBorders && oldBorders[side].satisfied) { |
|
owner.setBodyStyle(borderWidthProps[side], ''); |
|
} |
|
} |
|
} |
|
|
|
for (side in borders) { |
|
ln = borders[side].length; |
|
if (!owner.manageBodyBorders) { |
|
for (i = 0; i < ln; i++) { |
|
borders[side][i].addCls(noBorderClassesSides[side]); |
|
} |
|
if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) { |
|
owner.addBodyCls(noBorderClassesSides[side]); |
|
} else { |
|
owner.removeBodyCls(noBorderClassesSides[side]); |
|
} |
|
} |
|
else if (borders[side].satisfied) { |
|
owner.setBodyStyle(borderWidthProps[side], '1px'); |
|
} |
|
} |
|
|
|
me.borders = borders; |
|
}, |
|
|
|
beforeLayoutCycle: function (ownerContext) { |
|
var me = this, |
|
owner = me.owner, |
|
shrinkWrap = me.sizeModels.shrinkWrap, |
|
shrinkWrapDock = owner.shrinkWrapDock, |
|
collapsedHorz, collapsedVert; |
|
|
|
if (owner.collapsed) { |
|
if (owner.collapsedVertical()) { |
|
collapsedVert = true; |
|
ownerContext.measureDimensions = 1; |
|
} else { |
|
collapsedHorz = true; |
|
ownerContext.measureDimensions = 2; |
|
} |
|
} |
|
|
|
ownerContext.collapsedVert = collapsedVert; |
|
ownerContext.collapsedHorz = collapsedHorz; |
|
|
|
// If we are collapsed, we want to auto-layout using the placeholder/expander |
|
// instead of the normal items/dockedItems. This must be done here since we could |
|
// be in a box layout w/stretchmax which sets the width/heightModel to allow it to |
|
// control the size. |
|
if (collapsedVert) { |
|
ownerContext.heightModel = shrinkWrap; |
|
} else if (collapsedHorz) { |
|
ownerContext.widthModel = shrinkWrap; |
|
} |
|
|
|
shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0); |
|
ownerContext.shrinkWrapDockHeight = (shrinkWrapDock & 1) && // jshint ignore:line |
|
ownerContext.heightModel.shrinkWrap; |
|
ownerContext.shrinkWrapDockWidth = (shrinkWrapDock & 2) && // jshint ignore:line |
|
ownerContext.widthModel.shrinkWrap; |
|
}, |
|
|
|
beginLayout: function(ownerContext) { |
|
var me = this, |
|
owner = me.owner, |
|
docked = me.getLayoutItems(), |
|
layoutContext = ownerContext.context, |
|
dockedItemCount = docked.length, |
|
lastCollapsedState = me.lastCollapsedState, |
|
dockedItems, i, item, itemContext, offsets, |
|
collapsed, dock; |
|
|
|
me.callParent(arguments); |
|
|
|
// Cache the children as ContextItems (like a Container). Also setup to handle |
|
// collapsed state: |
|
collapsed = owner.getCollapsed(); |
|
if (collapsed !== lastCollapsedState && lastCollapsedState !== undefined) { |
|
// If we are collapsing... |
|
if (me.owner.collapsed) { |
|
ownerContext.isCollapsingOrExpanding = 1; |
|
// Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken by the layout. |
|
owner.addClsWithUI(owner.collapsedCls); |
|
} else { |
|
ownerContext.isCollapsingOrExpanding = 2; |
|
// Remove the collapsed class now, before layout calculations are done. |
|
owner.removeClsWithUI(owner.collapsedCls); |
|
ownerContext.lastCollapsedState = me.lastCollapsedState; |
|
} |
|
} |
|
me.lastCollapsedState = collapsed; |
|
|
|
ownerContext.dockedItems = dockedItems = []; |
|
|
|
for (i = 0; i < dockedItemCount; i++) { |
|
item = docked[i]; |
|
if (item.rendered) { |
|
dock = item.dock; |
|
itemContext = layoutContext.getCmp(item); |
|
itemContext.dockedAt = { x: 0, y: 0 }; |
|
itemContext.offsets = offsets = Ext.Element.parseBox(item.offsets || 0); |
|
itemContext.horizontal = dock === 'top' || dock === 'bottom'; |
|
offsets.width = offsets.left + offsets.right; |
|
offsets.height = offsets.top + offsets.bottom; |
|
dockedItems.push(itemContext); |
|
} |
|
} |
|
|
|
ownerContext.bodyContext = ownerContext.getEl('body'); |
|
}, |
|
|
|
beginLayoutCycle: function(ownerContext) { |
|
var me = this, |
|
docked = ownerContext.dockedItems, |
|
len = docked.length, |
|
owner = me.owner, |
|
frameBody = owner.frameBody, |
|
lastHeightModel = me.lastHeightModel, |
|
i, item, dock; |
|
|
|
me.callParent(arguments); |
|
|
|
if (me.owner.manageHeight) { |
|
// Reset in case manageHeight gets turned on during lifecycle. |
|
// See below for why display could be set to non-default value. |
|
if (me.lastBodyDisplay) { |
|
owner.body.dom.style.display = me.lastBodyDisplay = ''; |
|
} |
|
} else { |
|
// When manageHeight is false, the body stretches the outer el by using wide margins to force it to |
|
// accommodate the docked items. When overflow is visible (when panel is resizable and has embedded handles), |
|
// the body must be inline-block so as not to collapse its margins |
|
if (me.lastBodyDisplay !== 'inline-block') { |
|
owner.body.dom.style.display = me.lastBodyDisplay = 'inline-block'; |
|
} |
|
|
|
if (lastHeightModel && lastHeightModel.shrinkWrap && |
|
!ownerContext.heightModel.shrinkWrap) { |
|
owner.body.dom.style.marginBottom = ''; |
|
} |
|
} |
|
|
|
if (ownerContext.widthModel.auto) { |
|
if (ownerContext.widthModel.shrinkWrap) { |
|
owner.el.setWidth(null); |
|
} |
|
owner.body.setWidth(null); |
|
if (frameBody) { |
|
frameBody.setWidth(null); |
|
} |
|
} |
|
if (ownerContext.heightModel.auto) { |
|
owner.body.setHeight(null); |
|
//owner.el.setHeight(null); Disable this for now |
|
if (frameBody) { |
|
frameBody.setHeight(null); |
|
} |
|
} |
|
|
|
// Each time we begin (2nd+ would be due to invalidate) we need to publish the |
|
// known contentWidth/Height if we are collapsed: |
|
if (ownerContext.collapsedVert) { |
|
ownerContext.setContentHeight(0); |
|
} else if (ownerContext.collapsedHorz) { |
|
ownerContext.setContentWidth(0); |
|
} |
|
|
|
// dock: 'right' items, when a panel gets narrower get "squished". Moving them to |
|
// left:0px avoids this! |
|
for (i = 0; i < len; i++) { |
|
item = docked[i].target; |
|
dock = item.dock; |
|
|
|
if (dock === 'right') { |
|
item.setLocalX(0); |
|
} else if (dock !== 'left') { |
|
continue; |
|
} |
|
|
|
// TODO - clear width/height? |
|
} |
|
}, |
|
|
|
calculate: function (ownerContext) { |
|
var me = this, |
|
measure = me.measureAutoDimensions(ownerContext, ownerContext.measureDimensions), |
|
state = ownerContext.state, |
|
horzDone = state.horzDone, |
|
vertDone = state.vertDone, |
|
bodyContext = ownerContext.bodyContext, |
|
framing, horz, vert, forward, backward; |
|
|
|
// make sure we can use these value w/o calling methods to get them |
|
ownerContext.borderInfo || ownerContext.getBorderInfo(); // jshint ignore:line |
|
ownerContext.paddingInfo || ownerContext.getPaddingInfo(); // jshint ignore:line |
|
ownerContext.frameInfo || ownerContext.getFrameInfo(); // jshint ignore:line |
|
bodyContext.borderInfo || bodyContext.getBorderInfo(); // jshint ignore:line |
|
bodyContext.paddingInfo || bodyContext.getPaddingInfo(); // jshint ignore:line |
|
|
|
// On CSS3 browsers, the border and padding frame the outer el. On non-CSS3 |
|
// browsers, the outer el has no border or padding - all that appears on the |
|
// framing elements as padding and height. In CSS3, the border size effects the |
|
// origin of the dockedItems but the padding does not (so that must be added in |
|
// most of the time). In non-CSS3 mode, the dockedItems are outside the framing: |
|
// |
|
// ... top / left dockedItems ... |
|
// <div id="...-ml" style="padding-left: border-radius-left;"> |
|
// <div id="...-mr" style="padding-right: border-radius-right;"> |
|
// <div id="...-mc" style="padding: extra;"> |
|
// ... body ... |
|
// </div> |
|
// </div> |
|
// </div> |
|
// ... bottom / right dockedItems ... |
|
// |
|
// For the sake of sanity, we perform all the calculations in CSS3 mode. We test |
|
// for the presence of non-CSS3 framing only when necessary. |
|
// |
|
if (!ownerContext.frameBorder) { |
|
if (!(framing = ownerContext.framing)) { |
|
ownerContext.frameBorder = ownerContext.borderInfo; |
|
ownerContext.framePadding = ownerContext.paddingInfo; |
|
} else { |
|
// These values match what they would have been in CSS3. |
|
ownerContext.frameBorder = framing.border; |
|
ownerContext.framePadding = framing.padding; |
|
} |
|
} |
|
|
|
// Start the axes so they are ready to proceed inwards (fixed-size) or outwards |
|
// (shrinkWrap) and stash key property names as well: |
|
horz = !horzDone && |
|
me.createAxis(ownerContext, measure.contentWidth, ownerContext.widthModel, |
|
me.horzAxisProps, ownerContext.collapsedHorz); |
|
vert = !vertDone && |
|
me.createAxis(ownerContext, measure.contentHeight, ownerContext.heightModel, |
|
me.vertAxisProps, ownerContext.collapsedVert); |
|
|
|
// We iterate forward and backward over the dockedItems at the same time based on |
|
// whether an axis is shrinkWrap or fixed-size. For a fixed-size axis, the outer box |
|
// axis is allocated to docked items in forward order and is reduced accordingly. |
|
// To handle a shrinkWrap axis, the box starts at the inner (body) size and is used to |
|
// size docked items in backwards order. This is because the last docked item shares |
|
// an edge with the body. The item size is used to adjust the shrinkWrap axis outwards |
|
// until the first docked item (at the outermost edge) is processed. This backwards |
|
// order ensures that docked items never get an incorrect size for any dimension. |
|
for (forward = 0, backward = ownerContext.dockedItems.length; backward--; ++forward) { |
|
if (horz) { |
|
me.dockChild(ownerContext, horz, backward, forward); |
|
} |
|
if (vert) { |
|
me.dockChild(ownerContext, vert, backward, forward); |
|
} |
|
} |
|
|
|
if (horz && me.finishAxis(ownerContext, horz)) { |
|
state.horzDone = horzDone = horz; |
|
} |
|
|
|
if (vert && me.finishAxis(ownerContext, vert)) { |
|
state.vertDone = vertDone = vert; |
|
} |
|
|
|
// Once all items are docked, the final size of the outer panel or inner body can |
|
// be determined. If we can determine both width and height, we are done. |
|
if (horzDone && vertDone && me.finishConstraints(ownerContext, horzDone, vertDone)) { |
|
// Size information is published as we dock items but position is hard to do |
|
// that way (while avoiding published multiple times) so we publish all the |
|
// positions at the end. |
|
me.finishPositions(ownerContext, horzDone, vertDone); |
|
} else { |
|
me.done = false; |
|
} |
|
}, |
|
|
|
/** |
|
* Creates an axis object given the particulars. The process starts by placing the |
|
* dockedItems in an idealized box where this method is called once for each side. |
|
* The ideal box is defined by the CSS3 border and padding values (which account for |
|
* the influence of border-radius). The origin (the (0,0) point) of the ideal box is |
|
* the top-left edge of the border or the border-box. Normal dockedItems are placed |
|
* inside this box at an offset to clear the border and padding and sit properly in |
|
* the panel next to the body. |
|
* |
|
* The origin has to be started differently if the axis is in shrinkWrap mode. When |
|
* shrink-wrapping an axis, the axis starts at the edge of the body and expands |
|
* outwards as items are docked. This means the ideal (0,0) for shrinkWrap is on the |
|
* top-left corner of the body. |
|
* |
|
* The following diagram illustrates this using the vertical axis. |
|
* |
|
* +---------------------------+ 10px (border) |
|
* | | |
|
* | xxxxxxxxxxxxxxxxxxxxxxx | 5px (padding) shrinkWrap other |
|
* | +=====================+ | -50 15 |
|
* | | Header | | 30px |
|
* | | | | |
|
* | +=====================+ | |
|
* | +---------------------+ | -20 45 |
|
* | | tbar | | 20 px |
|
* | +---------------------+ | |
|
* | +---------------------+ | 0 65 |
|
* | | Body | | 100px |
|
* | | | | |
|
* | | | | |
|
* | +---------------------+ | |
|
* | +---------------------+ | 100 165 |
|
* | | bbar | | 15px |
|
* | +---------------------+ | |
|
* | xxxxxxxxxxxxxxxxxxxxxxx | 5px |
|
* | | |
|
* +---------------------------+ 10px |
|
* |
|
* These are sufficient to determine sizes of things, but to finalize this process |
|
* and assign proper positions, the tentative coordinates have to be adjusted by an |
|
* amount appropriate for the item. Because dockedItems are position:absolute, they |
|
* sit inside the border and so must be adjusted for padding. The body is different |
|
* because it is position:relative and so it naturally sits inside the padding and |
|
* the padding must not be included in its position. |
|
* |
|
* Headers and footers that use `ignoreParentFrame` interact with this process by |
|
* moving themselves outside the border and padding. So in the above diagram, the |
|
* Header would move up by 15px and *everything else* would move up by 10px. When |
|
* shrinkWrap is taking place, the 10px of border on the top is removed from the |
|
* height as well. |
|
* |
|
* The bbar behaves slightly different when it is `ignoreParentFrame`. In shrinkWrap |
|
* mode, it alone would move down by the padding and the bottom border would not be |
|
* included in the height. Otherwise, the bbar would be moved down 15px (since the |
|
* edge is fixed) and the next dockedItem would be placed at, or the body would be |
|
* stretched down to, 5px (padding) pixels above the bbar. |
|
* |
|
* @private |
|
*/ |
|
createAxis: function (ownerContext, contentSize, sizeModel, axisProps, collapsedAxis) { |
|
var me = this, |
|
begin = 0, |
|
owner = me.owner, |
|
maxSize = owner[axisProps.maxSize], |
|
minSize = owner[axisProps.minSize] || 0, |
|
dockBegin = axisProps.dockBegin, |
|
dockEnd = axisProps.dockEnd, |
|
posProp = axisProps.pos, |
|
sizeProp = axisProps.size, |
|
hasMaxSize = maxSize != null, // exactly the same as "maxSize !== null && maxSize !== undefined" |
|
shrinkWrap = sizeModel.shrinkWrap, |
|
bodyContext, framing, padding, end; |
|
|
|
if (shrinkWrap) { |
|
// End position before adding docks around the content is content size plus the body borders in this axis. |
|
// If collapsed in this axis, the body borders will not be shown. |
|
if (collapsedAxis) { |
|
end = 0; |
|
} else { |
|
bodyContext = ownerContext.bodyContext; |
|
end = contentSize + bodyContext.borderInfo[sizeProp]; |
|
} |
|
} else { |
|
framing = ownerContext.frameBorder; |
|
padding = ownerContext.framePadding; |
|
|
|
begin = framing[dockBegin] + padding[dockBegin]; |
|
end = ownerContext.getProp(sizeProp) - (framing[dockEnd] + padding[dockEnd]); |
|
} |
|
|
|
return { |
|
shrinkWrap: sizeModel.shrinkWrap, |
|
sizeModel: sizeModel, |
|
// An axis tracks start and end+1 px positions. eg 0 to 10 for 10px high |
|
initialBegin: begin, |
|
begin: begin, |
|
end: end, |
|
collapsed: collapsedAxis, |
|
horizontal: axisProps.horizontal, |
|
ignoreFrameBegin: null, |
|
ignoreFrameEnd: null, |
|
initialSize: end - begin, |
|
maxChildSize: 0, |
|
hasMinMaxConstraints: (minSize || hasMaxSize) && sizeModel.shrinkWrap, |
|
minSize: minSize, |
|
maxSize: hasMaxSize ? maxSize : 1e9, |
|
bodyPosProp: me.owner.manageHeight ? posProp : axisProps.marginBegin, |
|
dockBegin: dockBegin, // 'left' or 'top' |
|
dockEnd: dockEnd, // 'right' or 'end' |
|
posProp: posProp, // 'x' or 'y' |
|
sizeProp: sizeProp, // 'width' or 'height' |
|
setSize: axisProps.setSize, |
|
shrinkWrapDock: ownerContext[axisProps.shrinkWrapDock], |
|
sizeModelName: axisProps.sizeModel, |
|
dockedPixelsEnd: 0 |
|
}; |
|
}, |
|
|
|
/** |
|
* Docks a child item on the specified axis. This boils down to determining if the item |
|
* is docked at the "beginning" of the axis ("left" if horizontal, "top" if vertical), |
|
* the "end" of the axis ("right" if horizontal, "bottom" if vertical) or stretches |
|
* along the axis ("top" or "bottom" if horizontal, "left" or "right" if vertical). It |
|
* also has to differentiate between fixed and shrinkWrap sized dimensions. |
|
* @private |
|
*/ |
|
dockChild: function (ownerContext, axis, backward, forward) { |
|
var me = this, |
|
itemContext = ownerContext.dockedItems[axis.shrinkWrap ? backward : forward], |
|
item = itemContext.target, |
|
dock = item.dock, // left/top/right/bottom |
|
sizeProp = axis.sizeProp, |
|
pos, size; |
|
|
|
if (item.ignoreParentFrame && ownerContext.isCollapsingOrExpanding) { |
|
// collapsed window header margins may differ from expanded window header margins |
|
// so we need to make sure the old cached values are not used in axis calculations |
|
itemContext.clearMarginCache(); |
|
} |
|
|
|
if (!itemContext.marginInfo) { |
|
itemContext.getMarginInfo(); // get marginInfo ready |
|
} |
|
|
|
if (dock === axis.dockBegin) { |
|
if (axis.shrinkWrap) { |
|
pos = me.dockOutwardBegin(ownerContext, itemContext, item, axis); |
|
} else { |
|
pos = me.dockInwardBegin(ownerContext, itemContext, item, axis); |
|
} |
|
} else if (dock === axis.dockEnd) { |
|
if (axis.shrinkWrap) { |
|
pos = me.dockOutwardEnd(ownerContext, itemContext, item, axis); |
|
} else { |
|
pos = me.dockInwardEnd(ownerContext, itemContext, item, axis); |
|
} |
|
} else { |
|
if (axis.shrinkWrapDock) { |
|
// we are still shrinkwrapping transversely... so we need to include the |
|
// size of this item in the max calculation |
|
size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp]; |
|
axis.maxChildSize = Math.max(axis.maxChildSize, size); |
|
pos = 0; |
|
} else { |
|
pos = me.dockStretch(ownerContext, itemContext, item, axis); |
|
} |
|
} |
|
|
|
itemContext.dockedAt[axis.posProp] = pos; |
|
}, |
|
|
|
/** |
|
* Docks an item on a fixed-size axis at the "beginning". The "beginning" of the horizontal |
|
* axis is "left" and the vertical is "top". For a fixed-size axis, the size works from |
|
* the outer element (the panel) towards the body. |
|
* @private |
|
*/ |
|
dockInwardBegin: function (ownerContext, itemContext, item, axis) { |
|
var pos = axis.begin, |
|
sizeProp = axis.sizeProp, |
|
ignoreParentFrame = item.ignoreParentFrame, |
|
delta, |
|
size, |
|
dock; |
|
|
|
if (ignoreParentFrame) { |
|
axis.ignoreFrameBegin = itemContext; |
|
dock = item.dock; |
|
|
|
// We need to move everything up by the border-width. |
|
delta = ownerContext.frameBorder[dock]; |
|
|
|
// We need to move the header "up" by the padding as well. |
|
pos -= delta + ownerContext.framePadding[dock]; |
|
} |
|
|
|
if (!item.overlay) { |
|
size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp]; |
|
axis.begin += size; |
|
if (ignoreParentFrame) { |
|
axis.begin -= delta; |
|
} |
|
} |
|
|
|
return pos; |
|
}, |
|
|
|
/** |
|
* Docks an item on a fixed-size axis at the "end". The "end" of the horizontal axis is |
|
* "right" and the vertical is "bottom". |
|
* @private |
|
*/ |
|
dockInwardEnd: function (ownerContext, itemContext, item, axis) { |
|
var sizeProp = axis.sizeProp, |
|
size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp], |
|
pos = axis.end - size, |
|
frameEnd; |
|
|
|
if (!item.overlay) { |
|
axis.end = pos; |
|
} |
|
|
|
if (item.ignoreParentFrame) { |
|
axis.ignoreFrameEnd = itemContext; |
|
frameEnd = ownerContext.frameBorder[item.dock]; |
|
pos += frameEnd + ownerContext.framePadding[item.dock]; |
|
axis.end += frameEnd; |
|
} |
|
|
|
return pos; |
|
}, |
|
|
|
/** |
|
* Docks an item on a shrinkWrap axis at the "beginning". The "beginning" of the horizontal |
|
* axis is "left" and the vertical is "top". For a shrinkWrap axis, the size works from |
|
* the body outward to the outermost element (the panel). |
|
* |
|
* During the docking process, coordinates are allowed to be negative. We start with the |
|
* body at (0,0) so items docked "top" or "left" will simply be assigned negative x/y. In |
|
* the {@link #finishPositions} method these are corrected and framing is added. This way |
|
* the correction is applied as a simple translation of delta x/y on all coordinates to |
|
* bring the origin back to (0,0). |
|
* @private |
|
*/ |
|
dockOutwardBegin: function (ownerContext, itemContext, item, axis) { |
|
var pos = axis.begin, |
|
sizeProp = axis.sizeProp, |
|
size; |
|
|
|
if (axis.collapsed) { |
|
axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext; |
|
} else if (item.ignoreParentFrame) { |
|
axis.ignoreFrameBegin = itemContext; |
|
} |
|
// NOTE - When shrinkWrapping an ignoreParentFrame, this must be the last item |
|
// on the axis. Since that is so, we let finishAxis take this in to account. |
|
|
|
if (!item.overlay) { |
|
size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp]; |
|
pos -= size; |
|
axis.begin = pos; |
|
} |
|
|
|
return pos; |
|
}, |
|
|
|
/** |
|
* Docks an item on a shrinkWrap axis at the "end". The "end" of the horizontal axis is |
|
* "right" and the vertical is "bottom". |
|
* @private |
|
*/ |
|
dockOutwardEnd: function (ownerContext, itemContext, item, axis) { |
|
var pos = axis.end, |
|
sizeProp = axis.sizeProp, |
|
size; |
|
|
|
size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp]; |
|
|
|
if (axis.collapsed) { |
|
axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext; |
|
} else if (item.ignoreParentFrame) { |
|
axis.ignoreFrameEnd = itemContext; |
|
} |
|
// NOTE - When shrinkWrapping an ignoreParentFrame, this must be the last item |
|
// on the axis. Since that is so, we let finishAxis take this in to account. |
|
|
|
if (!item.overlay) { |
|
axis.end = pos + size; |
|
axis.dockedPixelsEnd += size; |
|
} |
|
|
|
return pos; |
|
}, |
|
|
|
/** |
|
* Docks an item that might stretch across an axis. This is done for dock "top" and |
|
* "bottom" items on the horizontal axis and dock "left" and "right" on the vertical. |
|
* @private |
|
*/ |
|
dockStretch: function (ownerContext, itemContext, item, axis) { |
|
var dock = item.dock, // left/top/right/bottom (also used to index padding/border) |
|
sizeProp = axis.sizeProp, // 'width' or 'height' |
|
horizontal = dock === 'top' || dock === 'bottom', |
|
border = ownerContext.frameBorder, |
|
offsets = itemContext.offsets, |
|
padding = ownerContext.framePadding, |
|
endProp = horizontal ? 'right' : 'bottom', |
|
startProp = horizontal ? 'left' : 'top', |
|
pos = axis.begin + offsets[startProp], |
|
margin, size; |
|
|
|
if (item.stretch !== false) { |
|
size = axis.end - pos - offsets[endProp]; |
|
|
|
if (item.ignoreParentFrame) { |
|
// In CSS3, the border and padding need to be ignored specifically. In |
|
// non-CSS3 / framing mode, the border and padding will be 0 **but** the |
|
// header is not rendered inside the framing elements and so we do not |
|
// want to do anything anyway! |
|
pos -= padding[startProp] + border[startProp]; |
|
size += padding[sizeProp] + border[sizeProp]; |
|
} |
|
|
|
margin = itemContext.marginInfo; |
|
size -= margin[sizeProp]; |
|
|
|
itemContext[axis.setSize](size); |
|
} |
|
|
|
return pos; |
|
}, |
|
|
|
/** |
|
* Finishes the calculation of an axis by determining its size. In non-shrink-wrap |
|
* cases, this is also where we set the body size. |
|
* @private |
|
*/ |
|
finishAxis: function (ownerContext, axis) { |
|
// If the maxChildSize is NaN it means at some point we tried to determine |
|
// The size of a docked item but we couldn't, so just jump out straight |
|
// away before doing any other processing |
|
if (isNaN(axis.maxChildSize)) { |
|
return false; |
|
} |
|
|
|
var axisBegin = axis.begin, |
|
size = axis.end - axisBegin, |
|
collapsed = axis.collapsed, |
|
setSizeMethod = axis.setSize, |
|
beginName = axis.dockBegin, // left or top |
|
endName = axis.dockEnd, // right or bottom |
|
padding = ownerContext.framePadding, |
|
border = ownerContext.frameBorder, |
|
borderBegin = border[beginName], |
|
framing = ownerContext.framing, |
|
framingBegin = framing && framing[beginName], |
|
// The padding is in play unless the axis is collapsed. |
|
paddingBegin = collapsed ? 0 : padding[beginName], |
|
sizeProp = axis.sizeProp, |
|
ignoreFrameBegin = axis.ignoreFrameBegin, |
|
ignoreFrameEnd = axis.ignoreFrameEnd, |
|
bodyContext = ownerContext.bodyContext, |
|
extraPaddingBegin = Math.max(borderBegin + paddingBegin - framingBegin, 0), |
|
bodyPos, bodySize, delta, dirty; |
|
|
|
if (axis.shrinkWrap) { |
|
// Since items docked left/top on a shrinkWrap axis go into negative coordinates, |
|
// we apply a delta to all coordinates to adjust their relative origin back to |
|
// a (0,0) inside the border. |
|
|
|
bodySize = axis.initialSize; |
|
|
|
if (framing) { |
|
// In CSS3 mode, things are compartively simple because "framing" is just |
|
// borders and padding. In non-CSS3 mode, however, the framing elements |
|
// are given a size equal to the max of the border-width and border-radius |
|
// and this pushes the body down accordingly. Further, the dockedItems are |
|
// all rendered outside the framing elements, so their origin equals the |
|
// ideal box origin. To translate this to match CSS3, we have to add on |
|
// the border-top. |
|
|
|
delta = -axisBegin + borderBegin + paddingBegin; |
|
bodyPos = delta - framingBegin - extraPaddingBegin; |
|
} else { |
|
bodyPos = -axisBegin; |
|
delta = bodyPos + paddingBegin; |
|
} |
|
|
|
if (!collapsed) { |
|
size += padding[sizeProp]; |
|
} |
|
|
|
if (ignoreFrameBegin) { |
|
// When some component ignores the begin framing, we move everything "up" |
|
// by that amount of framing. We also do not include that amount of the |
|
// framing in the shrinkWrap size. |
|
delta -= borderBegin; |
|
bodyPos -= borderBegin; |
|
|
|
// The item ignoring the framing must also escape the padding. Since the |
|
// axis.delta includes the padding and we want to apply this to only the |
|
// one item, we just poke its dockedAt.x/y property so that when we add |
|
// axis.begin the padding will cancel out. (Note: when we are collapsed |
|
// paddingBegin will be 0). |
|
|
|
ignoreFrameBegin.dockedAt[axis.posProp] -= paddingBegin; |
|
} else { |
|
size += borderBegin; |
|
} |
|
|
|
if (collapsed) { |
|
// in this case "ignoreFrameBegin === ignoreFrameEnd" so we can take the |
|
// special cases out of the mix here... |
|
} // jshint ignore:line |
|
else if (ignoreFrameEnd) { |
|
// When a component ignores the end framing, we simply move it further |
|
// "down" by the end padding and we do not add the end framing to the |
|
// shrinkWrap size. |
|
ignoreFrameEnd.dockedAt[axis.posProp] += padding[endName]; |
|
} else { |
|
size += border[endName]; |
|
} |
|
|
|
axis.size = size; // we have to wait for min/maxWidth/Height processing |
|
|
|
if (!axis.horizontal && !this.owner.manageHeight) { |
|
// the height of the bodyEl will give the proper height to the outerEl so |
|
// we don't need to set heights in the DOM |
|
dirty = false; |
|
} |
|
} else { |
|
// For a fixed-size axis, we started at the outer box and already have the |
|
// proper origin... almost... except for the owner's border. |
|
if (framing) { |
|
// since dockedItems are rendered outside the framing, they have the |
|
// proper origin already: |
|
delta = 0; |
|
bodyPos = axisBegin - framingBegin - extraPaddingBegin; |
|
} else { |
|
delta = -borderBegin; |
|
bodyPos = axisBegin - paddingBegin - borderBegin; |
|
} |
|
|
|
// Body size is remaining space between ends of Axis. |
|
bodySize = size; |
|
} |
|
|
|
axis.delta = delta; |
|
bodyContext[setSizeMethod](bodySize, dirty); |
|
bodyContext.setProp(axis.bodyPosProp, bodyPos); |
|
|
|
return !isNaN(size); |
|
}, |
|
|
|
beforeInvalidateShrinkWrapDock: function(itemContext, options){ |
|
var sizeModelName = options.axis.sizeModelName; |
|
if (!itemContext[sizeModelName].constrainedMin) { |
|
// if the child hit a min constraint, it needs to be at its configured size, so |
|
// we leave the sizeModel alone |
|
itemContext[sizeModelName] = Ext.layout.SizeModel.calculated; |
|
} |
|
}, |
|
|
|
afterInvalidateShrinkWrapDock: function(itemContext, options){ |
|
var axis = options.axis, |
|
me = options.layout, |
|
pos; |
|
|
|
if (itemContext[axis.sizeModelName].calculated) { |
|
pos = me.dockStretch(options.ownerContext, itemContext, itemContext.target, axis); |
|
itemContext.setProp(axis.posProp, axis.delta + pos); |
|
} |
|
}, |
|
|
|
/** |
|
* Finishes processing of each axis by applying the min/max size constraints. |
|
* @private |
|
*/ |
|
finishConstraints: function (ownerContext, horz, vert) { |
|
var me = this, |
|
sizeModels = me.sizeModels, |
|
publishWidth = horz.shrinkWrap, |
|
publishHeight = vert.shrinkWrap, |
|
owner = me.owner, |
|
dirty, height, width, heightModel, widthModel, size, |
|
minSize, maxSize, maxChildSize, desiredSize; |
|
|
|
// In these calculations, maxChildSize will only be > 0 in the scenario where |
|
// we are dock shrink wrapping in that direction, otherwise it is not measured. |
|
// As such, the additions are done to simplify the logic, even though in most |
|
// cases, it will have no impact on the overall result. |
|
|
|
if (publishWidth) { |
|
size = horz.size; |
|
minSize = horz.collapsed ? 0 : horz.minSize; |
|
maxSize = horz.maxSize; |
|
maxChildSize = horz.maxChildSize; |
|
desiredSize = Math.max(size, maxChildSize); |
|
|
|
if (desiredSize > maxSize) { |
|
widthModel = sizeModels.constrainedMax; |
|
width = maxSize; |
|
} else if (desiredSize < minSize) { |
|
widthModel = sizeModels.constrainedMin; |
|
width = minSize; |
|
} else if (size < maxChildSize) { |
|
widthModel = sizeModels.constrainedDock; |
|
owner.dockConstrainedWidth = width = maxChildSize; |
|
} else { |
|
width = size; |
|
} |
|
} |
|
|
|
if (publishHeight) { |
|
size = vert.size; |
|
minSize = vert.collapsed ? 0 : vert.minSize; |
|
maxSize = vert.maxSize; |
|
maxChildSize = vert.maxChildSize; |
|
// For vertical docks, their weighting means the height is affected by top/bottom |
|
// docked items, so we need to subtract them here |
|
desiredSize = Math.max(size, maxChildSize + size - vert.initialSize); |
|
|
|
if (desiredSize > maxSize) { |
|
heightModel = sizeModels.constrainedMax; |
|
height = maxSize; |
|
} else if (desiredSize < minSize) { |
|
heightModel = sizeModels.constrainedMin; |
|
height = minSize; |
|
} else if (size < maxChildSize) { |
|
heightModel = sizeModels.constrainedDock; |
|
owner.dockConstrainedHeight = height = maxChildSize; |
|
} else { |
|
if (!ownerContext.collapsedVert && !owner.manageHeight) { |
|
// height of the outerEl is provided by the height (including margins) |
|
// of the bodyEl, so this value does not need to be written to the DOM |
|
dirty = false; |
|
|
|
// so long as we set top and bottom margins on the bodyEl! |
|
ownerContext.bodyContext.setProp('margin-bottom', vert.dockedPixelsEnd); |
|
} |
|
|
|
height = size; |
|
} |
|
} |
|
|
|
// Handle the constraints... |
|
|
|
if (widthModel || heightModel) { |
|
// See ContextItem#init for an analysis of why this case is special. Basically, |
|
// in this case, we only know the width and the height could be anything. |
|
if (widthModel && heightModel && |
|
widthModel.constrainedMax && heightModel.constrainedByMin) { |
|
ownerContext.invalidate({ widthModel: widthModel }); |
|
return false; |
|
} |
|
|
|
// To process a width or height other than that to which we have shrinkWrapped, |
|
// we need to invalidate our component and carry forward w/these constrains... |
|
// unless the ownerLayout wants these results and will invalidate us anyway. |
|
if (!ownerContext.widthModel.calculatedFromShrinkWrap && |
|
!ownerContext.heightModel.calculatedFromShrinkWrap) { |
|
// nope, just us to handle the constraint... |
|
ownerContext.invalidate({ widthModel: widthModel, heightModel: heightModel }); |
|
return false; |
|
} |
|
|
|
// We have a constraint to deal with, so we just adjust the size models and |
|
// allow the ownerLayout to invalidate us with its contribution to our final |
|
// size... |
|
} else { |
|
// We're not invalidating, the ownerContext, so if we're shrink wrapping we'll need to |
|
// tell any docked items to invalidate themselves if necessary.' |
|
me.invalidateAxes(ownerContext, horz, vert); |
|
|
|
} |
|
|
|
// we only publish the sizes if we are not invalidating the result... |
|
|
|
if (publishWidth) { |
|
ownerContext.setWidth(width); |
|
if (widthModel) { |
|
ownerContext.widthModel = widthModel; // important to the ownerLayout |
|
} |
|
} |
|
if (publishHeight) { |
|
ownerContext.setHeight(height, dirty); |
|
if (heightModel) { |
|
ownerContext.heightModel = heightModel; // important to the ownerLayout |
|
} |
|
} |
|
|
|
return true; |
|
}, |
|
|
|
/** |
|
* |
|
* The default weighting of docked items produces this arrangement: |
|
* |
|
* +--------------------------------------------+ |
|
* | Top 1 | |
|
* +--------------------------------------------+ |
|
* | Top 2 | |
|
* +-----+-----+--------------------+-----+-----+ |
|
* | | | | | | |
|
* | | | | | | |
|
* | | | | R | R | |
|
* | L | L | | I | I | |
|
* | E | E | | G | G | |
|
* | F | F | | H | H | |
|
* | T | T | | T | T | |
|
* | | | | | | |
|
* | 2 | 1 | | 1 | 2 | |
|
* | | | | | | |
|
* | | | | | | |
|
* +-----+-----+--------------------+-----+-----+ |
|
* | Bottom 1 | |
|
* +--------------------------------------------+ |
|
* | Bottom 2 | |
|
* +--------------------------------------------+ |
|
* |
|
* So when we are shrinkWrapDock on the horizontal, the stretch size for top/bottom |
|
* docked items is the final axis size. For the vertical axis, however, the stretch |
|
* |
|
*/ |
|
invalidateAxes: function(ownerContext, horz, vert){ |
|
var before = this.beforeInvalidateShrinkWrapDock, |
|
after = this.afterInvalidateShrinkWrapDock, |
|
horzSize = horz.end - horz.begin, |
|
vertSize = vert.initialSize, |
|
invalidateHorz = horz.shrinkWrapDock && horz.maxChildSize <= horzSize, |
|
invalidateVert = vert.shrinkWrapDock && vert.maxChildSize <= vertSize, |
|
dockedItems, len, i, itemContext, itemSize, isHorz, axis, sizeProp; |
|
|
|
if (invalidateHorz || invalidateVert) { |
|
if (invalidateVert) { |
|
// For vertical, we need to reset the initial position because they are affected |
|
// by the horizontally docked items |
|
vert.begin = vert.initialBegin; |
|
vert.end = vert.begin + vert.initialSize; |
|
} |
|
dockedItems = ownerContext.dockedItems; |
|
for (i = 0, len = dockedItems.length; i < len; ++i) { |
|
itemContext = dockedItems[i]; |
|
isHorz = itemContext.horizontal; |
|
axis = null; |
|
if (invalidateHorz && isHorz) { |
|
sizeProp = horz.sizeProp; |
|
itemSize = horzSize; |
|
axis = horz; |
|
} else if (invalidateVert && !isHorz) { |
|
sizeProp = vert.sizeProp; |
|
itemSize = vertSize; |
|
axis = vert; |
|
} |
|
|
|
if (axis) { |
|
// subtract any margins |
|
itemSize -= itemContext.getMarginInfo()[sizeProp]; |
|
if (itemSize !== itemContext.props[sizeProp]) { |
|
itemContext.invalidate({ |
|
before: before, |
|
after: after, |
|
axis: axis, |
|
ownerContext: ownerContext, |
|
layout: this |
|
}); |
|
} |
|
} |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Finishes the calculation by setting positions on the body and all of the items. |
|
* @private |
|
*/ |
|
finishPositions: function (ownerContext, horz, vert) { |
|
var dockedItems = ownerContext.dockedItems, |
|
length = dockedItems.length, |
|
deltaX = horz.delta, |
|
deltaY = vert.delta, |
|
index, itemContext; |
|
|
|
for (index = 0; index < length; ++index) { |
|
itemContext = dockedItems[index]; |
|
|
|
itemContext.setProp('x', deltaX + itemContext.dockedAt.x); |
|
itemContext.setProp('y', deltaY + itemContext.dockedAt.y); |
|
} |
|
}, |
|
|
|
finishedLayout: function(ownerContext) { |
|
var me = this, |
|
target = ownerContext.target; |
|
|
|
me.callParent(arguments); |
|
|
|
if (!ownerContext.animatePolicy) { |
|
if (ownerContext.isCollapsingOrExpanding === 1) { |
|
target.afterCollapse(false); |
|
} else if (ownerContext.isCollapsingOrExpanding === 2) { |
|
target.afterExpand(false); |
|
} |
|
} |
|
}, |
|
|
|
getAnimatePolicy: function(ownerContext) { |
|
var me = this, |
|
lastCollapsedState, policy; |
|
|
|
if (ownerContext.isCollapsingOrExpanding === 1) { |
|
lastCollapsedState = me.lastCollapsedState; |
|
} else if (ownerContext.isCollapsingOrExpanding === 2) { |
|
lastCollapsedState = ownerContext.lastCollapsedState; |
|
} |
|
|
|
if (lastCollapsedState === 'left' || lastCollapsedState === 'right') { |
|
policy = me.horizontalCollapsePolicy; |
|
} else if (lastCollapsedState === 'top' || lastCollapsedState === 'bottom') { |
|
policy = me.verticalCollapsePolicy; |
|
} |
|
|
|
return policy; |
|
}, |
|
|
|
/** |
|
* Retrieve an ordered and/or filtered array of all docked Components. |
|
* @param {String} [order='render'] The desired ordering of the items ('render' or 'visual'). |
|
* @param {Boolean} [beforeBody] An optional flag to limit the set of items to only those |
|
* before the body (true) or after the body (false). All components are returned by |
|
* default. |
|
* @return {Ext.Component[]} An array of components. |
|
* @protected |
|
*/ |
|
getDockedItems: function(order, beforeBody) { |
|
var me = this, |
|
renderedOnly = (order === 'visual'), |
|
all = renderedOnly ? Ext.ComponentQuery.query('[rendered]', me.owner.dockedItems.items) : me.owner.dockedItems.items, |
|
sort = all && all.length && order !== false, |
|
renderOrder, |
|
dock, dockedItems, i, isBefore, length; |
|
|
|
if (beforeBody == null) { |
|
dockedItems = sort && !renderedOnly ? all.slice() : all; |
|
} else { |
|
dockedItems = []; |
|
|
|
for (i = 0, length = all.length; i < length; ++i) { |
|
dock = all[i].dock; |
|
isBefore = (dock === 'top' || dock === 'left'); |
|
if (beforeBody ? isBefore : !isBefore) { |
|
dockedItems.push(all[i]); |
|
} |
|
} |
|
|
|
sort = sort && dockedItems.length; |
|
} |
|
|
|
if (sort) { |
|
renderOrder = (order = order || 'render') === 'render'; |
|
Ext.Array.sort(dockedItems, function(a, b) { |
|
var aw, |
|
bw; |
|
|
|
// If the two items are on opposite sides of the body, they must not be sorted by any weight value: |
|
// For rendering purposes, left/top *always* sorts before right/bottom |
|
if (renderOrder && ((aw = me.owner.dockOrder[a.dock]) !== (bw = me.owner.dockOrder[b.dock]))) { |
|
|
|
// The two dockOrder values cancel out when two items are on opposite sides. |
|
if (!(aw + bw)) { // jshint ignore:line |
|
return aw - bw; |
|
} |
|
} |
|
|
|
aw = me.getItemWeight(a, order); |
|
bw = me.getItemWeight(b, order); |
|
if ((aw !== undefined) && (bw !== undefined)) { |
|
return aw - bw; |
|
} |
|
return 0; |
|
}); |
|
} |
|
|
|
return dockedItems || []; |
|
}, |
|
|
|
getItemWeight: function (item, order) { |
|
var weight = item.weight || this.owner.defaultDockWeights[item.dock]; |
|
return weight[order] || weight; |
|
}, |
|
|
|
/** |
|
* @protected |
|
* Returns an array containing all the **visible** docked items inside this layout's owner Panel |
|
* @return {Array} An array containing all the **visible** docked items of the Panel |
|
*/ |
|
getLayoutItems : function() { |
|
var me = this, |
|
items, |
|
itemCount, |
|
item, |
|
i, |
|
result; |
|
|
|
if (me.owner.collapsed) { |
|
result = me.owner.getCollapsedDockedItems(); |
|
} else { |
|
items = me.getDockedItems('visual'); |
|
itemCount = items.length; |
|
result = []; |
|
for (i = 0; i < itemCount; i++) { |
|
item = items[i]; |
|
if (!item.hidden) { |
|
result.push(item); |
|
} |
|
} |
|
} |
|
return result; |
|
}, |
|
|
|
// Content size includes padding but not borders, so subtract them off |
|
measureContentWidth: function (ownerContext) { |
|
var bodyContext = ownerContext.bodyContext; |
|
return bodyContext.el.getWidth() - bodyContext.getBorderInfo().width; |
|
}, |
|
|
|
measureContentHeight: function (ownerContext) { |
|
var bodyContext = ownerContext.bodyContext; |
|
return bodyContext.el.getHeight() - bodyContext.getBorderInfo().height; |
|
}, |
|
|
|
redoLayout: function(ownerContext) { |
|
var me = this, |
|
owner = me.owner; |
|
|
|
// If we are collapsing... |
|
if (ownerContext.isCollapsingOrExpanding === 1) { |
|
if (owner.reExpander) { |
|
owner.reExpander.el.show(); |
|
} |
|
// Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken by the layout. |
|
owner.addClsWithUI(owner.collapsedCls); |
|
ownerContext.redo(true); |
|
} else if (ownerContext.isCollapsingOrExpanding === 2) { |
|
// Remove the collapsed class now, before layout calculations are done. |
|
owner.removeClsWithUI(owner.collapsedCls); |
|
ownerContext.bodyContext.redo(); |
|
} |
|
}, |
|
|
|
// @private override inherited. |
|
// We need to render in the correct order, top/left before bottom/right |
|
renderChildren: function() { |
|
var me = this, |
|
items = me.getDockedItems(), |
|
target = me.getRenderTarget(); |
|
|
|
me.handleItemBorders(); |
|
|
|
me.renderItems(items, target); |
|
}, |
|
|
|
/** |
|
* @protected |
|
* Render the top and left docked items before any existing DOM nodes in our render target, |
|
* and then render the right and bottom docked items after. This is important, for such things |
|
* as tab stops and ARIA readers, that the DOM nodes are in a meaningful order. |
|
* Our collection of docked items will already be ordered via Panel.getDockedItems(). |
|
*/ |
|
renderItems: function(items, target) { |
|
var me = this, |
|
dockedItemCount = items.length, |
|
itemIndex = 0, |
|
correctPosition = 0, |
|
staticNodeCount = 0, |
|
targetNodes = me.getRenderTarget().dom.childNodes, |
|
targetChildCount = targetNodes.length, |
|
i, j, targetChildNode, item; |
|
|
|
// Calculate the number of DOM nodes in our target that are not our docked items |
|
for (i = 0, j = 0; i < targetChildCount; i++) { |
|
targetChildNode = targetNodes[i]; |
|
if (targetChildNode.nodeType === 1 && Ext.fly(targetChildNode).hasCls(Ext.baseCSSPrefix + 'resizable-handle')) { |
|
break; |
|
} |
|
for (j = 0; j < dockedItemCount; j++) { |
|
item = items[j]; |
|
if (item.rendered && item.el.dom === targetChildNode) { |
|
break; |
|
} |
|
} |
|
// Walked off the end of the docked items without matching the found child node; |
|
// Then it's a static node. |
|
if (j === dockedItemCount) { |
|
staticNodeCount++; |
|
} |
|
} |
|
|
|
// Now we go through our docked items and render/move them |
|
for (; itemIndex < dockedItemCount; itemIndex++, correctPosition++) { |
|
item = items[itemIndex]; |
|
|
|
// If we're now at the first right/bottom docked item, we jump over the body element. |
|
// |
|
// TODO: This is affected if users provide custom weight values to their |
|
// docked items, which puts it out of (t,l,r,b) order. Avoiding a second |
|
// sort operation here, for now, in the name of performance. getDockedItems() |
|
// needs the sort operation not just for this layout-time rendering, but |
|
// also for getRefItems() to return a logical ordering (FocusManager, CQ, et al). |
|
if (itemIndex === correctPosition && (item.dock === 'right' || item.dock === 'bottom')) { |
|
correctPosition += staticNodeCount; |
|
} |
|
|
|
// Same logic as Layout.renderItems() |
|
if (item && !item.rendered) { |
|
me.renderItem(item, target, correctPosition); |
|
} |
|
else if (!me.isValidParent(item, target, correctPosition)) { |
|
me.moveItem(item, target, correctPosition); |
|
} |
|
} |
|
}, |
|
|
|
undoLayout: function(ownerContext) { |
|
var me = this, |
|
owner = me.owner; |
|
|
|
// If we are collapsing... |
|
if (ownerContext.isCollapsingOrExpanding === 1) { |
|
|
|
// We do not want to see the re-expander header until the final collapse is complete |
|
if (owner.reExpander) { |
|
owner.reExpander.el.hide(); |
|
} |
|
// Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken by the layout. |
|
owner.removeClsWithUI(owner.collapsedCls); |
|
ownerContext.undo(true); |
|
} else if (ownerContext.isCollapsingOrExpanding === 2) { |
|
// Remove the collapsed class now, before layout calculations are done. |
|
owner.addClsWithUI(owner.collapsedCls); |
|
ownerContext.bodyContext.undo(); |
|
} |
|
}, |
|
|
|
sizePolicy: { |
|
nostretch: { |
|
setsWidth: 0, |
|
setsHeight: 0 |
|
}, |
|
|
|
horz: { // item goes horizontally (top or bottom docked) |
|
shrinkWrap: { |
|
// This is how we manage the width of a top/bottom docked item when its |
|
// shrinkWrapWidth and ours need to be maxed (calculatedFromShrinkWrap) |
|
setsWidth: 1, |
|
setsHeight: 0, |
|
readsWidth: 1 |
|
}, |
|
stretch: { |
|
setsWidth: 1, |
|
setsHeight: 0 |
|
} |
|
}, |
|
|
|
vert: { // item goes vertically (left or right docked) |
|
shrinkWrap: { |
|
setsWidth: 0, |
|
setsHeight: 1, |
|
readsHeight: 1 |
|
}, |
|
stretch: { |
|
setsWidth: 0, |
|
setsHeight: 1 |
|
} |
|
}, |
|
|
|
stretchV: { |
|
setsWidth: 0, |
|
setsHeight: 1 |
|
}, |
|
|
|
// Circular dependency with partial auto-sized panels: |
|
// |
|
// If we have an autoHeight docked item being stretched horizontally (top/bottom), |
|
// that stretching will determine its width and its width must be set before its |
|
// autoHeight can be determined. If that item is docked in an autoWidth panel, the |
|
// body will need its height set before it can determine its width, but the height |
|
// of the docked item is needed to subtract from the panel height in order to set |
|
// the body height. |
|
// |
|
// This same pattern occurs with autoHeight panels with autoWidth docked items on |
|
// left or right. If the panel is fully auto or fully fixed, these problems don't |
|
// come up because there is no dependency between the dimensions. |
|
// |
|
// Cutting the Gordian Knot: In these cases, we have to allow something to measure |
|
// itself without full context. This is OK as long as the managed dimension doesn't |
|
// effect the auto-dimension, which is often the case for things like toolbars. The |
|
// managed dimension only effects overflow handlers and such and does not change the |
|
// auto-dimension. To encourage the item to measure itself without waiting for the |
|
// managed dimension, we have to tell it that the layout will also be reading that |
|
// dimension. This is similar to how stretchmax works. |
|
|
|
autoStretchH: { |
|
readsWidth: 1, |
|
setsWidth: 1, |
|
setsHeight: 0 |
|
}, |
|
autoStretchV: { |
|
readsHeight: 1, |
|
setsWidth: 0, |
|
setsHeight: 1 |
|
} |
|
}, |
|
|
|
getItemSizePolicy: function (item, ownerSizeModel) { |
|
var me = this, |
|
policy = me.sizePolicy, |
|
shrinkWrapDock = me.owner.shrinkWrapDock, |
|
dock, vertical; |
|
|
|
if (item.stretch === false) { |
|
return policy.nostretch; |
|
} |
|
|
|
dock = item.dock; |
|
vertical = (dock === 'left' || dock === 'right'); |
|
|
|
shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0); |
|
if (vertical) { |
|
policy = policy.vert; |
|
shrinkWrapDock = shrinkWrapDock & 1; // jshint ignore:line |
|
} else { |
|
policy = policy.horz; |
|
shrinkWrapDock = shrinkWrapDock & 2; // jshint ignore:line |
|
} |
|
|
|
if (shrinkWrapDock) { |
|
// Getting the size model is expensive, so only do so if we really need it |
|
if (!ownerSizeModel) { |
|
ownerSizeModel = me.owner.getSizeModel(); |
|
} |
|
if (ownerSizeModel[vertical ? 'height' : 'width'].shrinkWrap) { |
|
return policy.shrinkWrap; |
|
} |
|
} |
|
|
|
return policy.stretch; |
|
}, |
|
|
|
/** |
|
* @protected |
|
* We are overriding the Ext.layout.Layout configureItem method to also add a class that |
|
* indicates the position of the docked item. We use the itemCls (x-docked) as a prefix. |
|
* An example of a class added to a dock: right item is x-docked-right |
|
* @param {Ext.Component} item The item we are configuring |
|
*/ |
|
configureItem : function(item, pos) { |
|
this.callParent(arguments); |
|
|
|
item.addCls(this._itemCls); |
|
if (!item.ignoreBorderManagement) { |
|
item.addClsWithUI(this.getDockCls(item.dock)); |
|
} |
|
}, |
|
|
|
/** |
|
* Get's the css class name for a given dock position. |
|
* @param {String} dock `top`, `right`, `bottom`, or `left` |
|
* @return {String} |
|
* @private |
|
*/ |
|
getDockCls: function(dock) { |
|
return 'docked-' + dock; |
|
}, |
|
|
|
afterRemove: function(item) { |
|
var dom; |
|
|
|
this.callParent(arguments); |
|
|
|
item.removeCls(this._itemCls); |
|
if (!item.ignoreBorderManagement) { |
|
item.removeClsWithUI(this.getDockCls(item.dock)); |
|
} |
|
|
|
dom = item.el.dom; |
|
|
|
if (!item.destroying && dom) { |
|
dom.parentNode.removeChild(dom); |
|
} |
|
this.childrenChanged = true; |
|
}, |
|
|
|
/** |
|
* This object is indexed by a component's `baseCls` to yield another object which |
|
* is then indexed by the component's `ui` to produce an array of CSS class names. |
|
* This array is indexed in the same manner as the `noBorderClassTable` and indicates |
|
* the a particular edge of a docked item or the body element is actually "collapsed" |
|
* with the component's outer border. |
|
* @private |
|
*/ |
|
borderCollapseMap: { |
|
/* |
|
'x-panel': { |
|
'default': [] |
|
} |
|
*/ |
|
}, |
|
|
|
/** |
|
* Returns the array of class names to add to a docked item or body element when for |
|
* the edges that should collapse with the outer component border. Basically, the |
|
* panel's outer border must look visually like a contiguous border but may need to |
|
* be realized by using the border of docked items and/or the body. This class name |
|
* allows the border color and width to be controlled accordingly and distinctly from |
|
* the border of the docked item or body element when it is not having its border |
|
* collapsed. |
|
* @private |
|
*/ |
|
getBorderCollapseTable: function () { |
|
var me = this, |
|
map = me.borderCollapseMap, |
|
owner = me.owner, |
|
baseCls = owner.baseCls, |
|
ui = owner.ui, |
|
table; |
|
|
|
map = map[baseCls] || (map[baseCls] = {}); |
|
table = map[ui]; |
|
|
|
if (!table) { |
|
baseCls += '-' + ui + '-outer-border-'; |
|
map[ui] = table = [ |
|
0, // TRBL |
|
baseCls + 'l', // 0001 = 1 |
|
baseCls + 'b', // 0010 = 2 |
|
baseCls + 'bl', // 0011 = 3 |
|
baseCls + 'r', // 0100 = 4 |
|
baseCls + 'rl', // 0101 = 5 |
|
baseCls + 'rb', // 0110 = 6 |
|
baseCls + 'rbl', // 0111 = 7 |
|
baseCls + 't', // 1000 = 8 |
|
baseCls + 'tl', // 1001 = 9 |
|
baseCls + 'tb', // 1010 = 10 |
|
baseCls + 'tbl', // 1011 = 11 |
|
baseCls + 'tr', // 1100 = 12 |
|
baseCls + 'trl', // 1101 = 13 |
|
baseCls + 'trb', // 1110 = 14 |
|
baseCls + 'trbl' // 1111 = 15 |
|
]; |
|
} |
|
|
|
return table; |
|
} |
|
});
|
|
|