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.
619 lines
20 KiB
619 lines
20 KiB
/** |
|
* This class is the base for all layout types: component and container. |
|
* @protected |
|
*/ |
|
Ext.define('Ext.layout.Layout', { |
|
mixins: [ |
|
'Ext.mixin.Factoryable' |
|
], |
|
|
|
requires: [ |
|
'Ext.XTemplate', |
|
'Ext.layout.SizeModel' |
|
], |
|
|
|
uses: [ 'Ext.layout.Context' ], |
|
|
|
factoryConfig: { |
|
type: 'layout' |
|
}, |
|
|
|
/** |
|
* @property {Boolean} isLayout |
|
* `true` in this class to identify an object as an instantiated Layout, or subclass thereof. |
|
* @readonly |
|
*/ |
|
isLayout: true, |
|
initialized: false, |
|
running: false, |
|
|
|
/** |
|
* @private |
|
* `true` if this layout may need to incorporate the dimensions of individual child |
|
* items into its layout calculations. Layouts that handle the size of their children |
|
* as a group (autocontainer, form) can set this to false for an additional performance |
|
* optimization. When `false` the layout system will not recurse into the child |
|
* items if {@link Ext.layout.container.Container#activeItemCount} is `0`, which will be the case if all child items |
|
* use "liquid" CSS layout, e.g. form fields. (See Ext.Component#liquidLayout) |
|
*/ |
|
needsItemSize: true, |
|
|
|
/** |
|
* @private |
|
* `true` if this layout may set the size of its child items. Layouts that do not |
|
* set the size of their child items (autocontainer, form) can set this to false |
|
* for an additional performance optimization. When `true` the layout system will |
|
* not create a context item for children that use liquid layout, because there is |
|
* no need for a context item if item size is neither read nor set by the owning layout. |
|
*/ |
|
setsItemSize: true, |
|
|
|
autoSizePolicy: { |
|
readsWidth: 1, |
|
readsHeight: 1, |
|
setsWidth: 0, |
|
setsHeight: 0 |
|
}, |
|
|
|
$configPrefixed: false, |
|
$configStrict: false, |
|
|
|
constructor : function(config) { |
|
var me = this; |
|
|
|
me.id = Ext.id(null, me.type + '-'); |
|
|
|
me.initConfig(config); |
|
|
|
// prevent any "type" that was passed as the alias from overriding the "type" |
|
// property from the prototype |
|
delete me.type; |
|
me.layoutCount = 0; |
|
}, |
|
|
|
/** |
|
* @property {Boolean} done Used only during a layout run, this value indicates that a |
|
* layout has finished its calculations. This flag is set to true prior to the call to |
|
* {@link #calculate} and should be set to false if this layout has more work to do. |
|
*/ |
|
|
|
/** |
|
* Called before any calculation cycles to prepare for layout. |
|
* |
|
* This is a write phase and DOM reads should be strictly avoided when overridding |
|
* this method. |
|
* |
|
* @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner |
|
* component. |
|
* @method beginLayout |
|
*/ |
|
beginLayout: Ext.emptyFn, |
|
|
|
/** |
|
* Called before any calculation cycles to reset DOM values and prepare for calculation. |
|
* |
|
* This is a write phase and DOM reads should be strictly avoided when overridding |
|
* this method. |
|
* |
|
* @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner |
|
* component. |
|
* @method beginLayoutCycle |
|
*/ |
|
beginLayoutCycle: function (ownerContext) { |
|
var me = this, |
|
context = me.context, |
|
changed; |
|
|
|
if (me.lastWidthModel !== ownerContext.widthModel) { |
|
if (me.lastWidthModel) { |
|
changed = true; |
|
} |
|
me.lastWidthModel = ownerContext.widthModel; |
|
} |
|
|
|
if (me.lastHeightModel !== ownerContext.heightModel) { |
|
if (me.lastWidthModel) { |
|
changed = true; |
|
} |
|
me.lastHeightModel = ownerContext.heightModel; |
|
} |
|
|
|
if (changed) { |
|
(context = ownerContext.context).clearTriggers(me, false); |
|
context.clearTriggers(me, true); |
|
me.triggerCount = 0; |
|
} |
|
}, |
|
|
|
/** |
|
* Called to perform the calculations for this layout. This method will be called at |
|
* least once and may be called repeatedly if the {@link #done} property is cleared |
|
* before return to indicate that this layout is not yet done. The {@link #done} property |
|
* is always set to `true` before entering this method. |
|
* |
|
* This is a read phase and DOM writes should be strictly avoided in derived classes. |
|
* Instead, DOM writes need to be written to {@link Ext.layout.ContextItem} objects to |
|
* be flushed at the next opportunity. |
|
* |
|
* @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner |
|
* component. |
|
* @method calculate |
|
* @abstract |
|
*/ |
|
|
|
/** |
|
* This method (if implemented) is called at the end of the cycle in which this layout |
|
* completes (by not setting {@link #done} to `false` in {@link #calculate}). It is |
|
* possible for the layout to complete and yet become invalid before the end of the cycle, |
|
* in which case, this method will not be called. It is also possible for this method to |
|
* be called and then later the layout becomes invalidated. This will result in |
|
* {@link #calculate} being called again, followed by another call to this method. |
|
* |
|
* This is a read phase and DOM writes should be strictly avoided in derived classes. |
|
* Instead, DOM writes need to be written to {@link Ext.layout.ContextItem} objects to |
|
* be flushed at the next opportunity. |
|
* |
|
* This method need not be implemented by derived classes and, in fact, should only be |
|
* implemented when needed. |
|
* |
|
* @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner |
|
* component. |
|
* @method completeLayout |
|
*/ |
|
|
|
/** |
|
* This method (if implemented) is called after all layouts have completed. In most |
|
* ways this is similar to {@link #completeLayout}. This call can cause this (or any |
|
* layout) to be become invalid (see {@link Ext.layout.Context#invalidate}), but this |
|
* is best avoided. This method is intended to be where final reads are made and so it |
|
* is best to avoid invalidating layouts at this point whenever possible. Even so, this |
|
* method can be used to perform final checks that may require all other layouts to be |
|
* complete and then invalidate some results. |
|
* |
|
* This is a read phase and DOM writes should be strictly avoided in derived classes. |
|
* Instead, DOM writes need to be written to {@link Ext.layout.ContextItem} objects to |
|
* be flushed at the next opportunity. |
|
* |
|
* This method need not be implemented by derived classes and, in fact, should only be |
|
* implemented when needed. |
|
* |
|
* @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner |
|
* component. |
|
* @method finalizeLayout |
|
*/ |
|
|
|
/** |
|
* This method is called after all layouts are complete and their calculations flushed |
|
* to the DOM. No further layouts will be run and this method is only called once per |
|
* layout run. The base component layout caches `lastComponentSize`. |
|
* |
|
* This is a write phase and DOM reads should be avoided if possible when overridding |
|
* this method. |
|
* |
|
* This method need not be implemented by derived classes and, in fact, should only be |
|
* implemented when needed. |
|
* |
|
* @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner |
|
* component. |
|
*/ |
|
finishedLayout: function (ownerContext) { |
|
this.lastWidthModel = ownerContext.widthModel; |
|
this.lastHeightModel = ownerContext.heightModel; |
|
this.ownerContext = null; |
|
}, |
|
|
|
/** |
|
* This method (if implemented) is called after all layouts are finished, and all have |
|
* a `lastComponentSize` cached. No further layouts will be run and this method is only |
|
* called once per layout run. It is the bookend to {@link #beginLayout}. |
|
* |
|
* This is a write phase and DOM reads should be avoided if possible when overridding |
|
* this method. This is the catch-all tail method to a layout and so the rules are more |
|
* relaxed. Even so, for performance reasons, it is best to avoid reading the DOM. If |
|
* a read is necessary, consider implementing a {@link #finalizeLayout} method to do the |
|
* required reads. |
|
* |
|
* This method need not be implemented by derived classes and, in fact, should only be |
|
* implemented when needed. |
|
* |
|
* @param {Ext.layout.ContextItem} ownerContext The context item for the layout's owner |
|
* component. |
|
* @method notifyOwner |
|
*/ |
|
|
|
redoLayout: Ext.emptyFn, |
|
undoLayout: Ext.emptyFn, |
|
|
|
getAnimatePolicy: function() { |
|
return this.animatePolicy; |
|
}, |
|
|
|
/** |
|
* Returns an object describing how this layout manages the size of the given component. |
|
* This method must be implemented by any layout that manages components. |
|
* |
|
* @param {Ext.Component} item |
|
* @return {Ext.layout.SizePolicy} An object describing the sizing done by the layout |
|
* for this item. |
|
* @protected |
|
*/ |
|
getItemSizePolicy: function (item) { |
|
return this.autoSizePolicy; |
|
}, |
|
|
|
/** |
|
* Returns the element that wraps the contents for the purposes of touch scrolling. |
|
* Only applicable when the layout adds the scroller element as part of its renderTpl |
|
* (e.g. autocontainer and box) |
|
* @private |
|
*/ |
|
getScrollerEl: Ext.emptyFn, |
|
|
|
isItemBoxParent: function (itemContext) { |
|
return false; |
|
}, |
|
|
|
isItemLayoutRoot: function (item) { |
|
var sizeModel = item.getSizeModel(), |
|
width = sizeModel.width, |
|
height = sizeModel.height; |
|
|
|
// If this component has never had a layout and some of its dimensions are set by |
|
// its ownerLayout, we cannot be the layoutRoot... |
|
if (!item.componentLayout.lastComponentSize && (width.calculated || height.calculated)) { |
|
return false; |
|
} |
|
|
|
// otherwise an ownerCt whose size is not effected by its content is a root |
|
return !width.shrinkWrap && !height.shrinkWrap; |
|
}, |
|
|
|
isItemShrinkWrap: function (item) { |
|
return item.shrinkWrap; |
|
}, |
|
|
|
isRunning: function () { |
|
return !!this.ownerContext; |
|
}, |
|
|
|
//----------------------------------------------------- |
|
/* |
|
* Clears any styles which must be cleared before layout can take place. |
|
* Only DOM WRITES must be performed at this stage. |
|
* |
|
* An entry for the owner's element ID must be created in the layoutContext containing |
|
* a reference to the target which must be sized/positioned/styled by the layout at |
|
* the flush stage: |
|
* |
|
* { |
|
* target: me.owner |
|
* } |
|
* |
|
* Component layouts should iterate through managed Elements, |
|
* pushing an entry for each element: |
|
* |
|
* { |
|
* target: childElement |
|
* } |
|
*/ |
|
//----------------------------------------------------- |
|
|
|
getItemsRenderTree: function (items, renderCfgs) { |
|
var length = items.length, |
|
i, item, itemConfig, result; |
|
|
|
if (length) { |
|
result = []; |
|
for (i = 0; i < length; ++i) { |
|
item = items[i]; |
|
|
|
// If we are being asked to move an already rendered Component, we must not recalculate its renderTree |
|
// and rerun its render process. The Layout's isValidParent check will ensure that the DOM is moved into place. |
|
if (!item.rendered) { |
|
|
|
// If we've already calculated the item's element config, don't calculate it again. |
|
// This may happen if the rendering process mutates the owning Container's items |
|
// collection, and Ext.layout.Container#getRenderTree runs through the collection again. |
|
// Note that the config may be null if a beforerender listener vetoed the operation, so |
|
// we must compare to undefined. |
|
if (renderCfgs && (renderCfgs[item.id] !== undefined)) { |
|
itemConfig = renderCfgs[item.id]; |
|
} else { |
|
// Perform layout preprocessing in the bulk render path |
|
this.configureItem(item); |
|
itemConfig = item.getRenderTree(); |
|
if (renderCfgs) { |
|
renderCfgs[item.id] = itemConfig; |
|
} |
|
} |
|
|
|
// itemConfig mey be null if a beforerender listener vetoed the operation. |
|
if (itemConfig) { |
|
result.push(itemConfig); |
|
} |
|
} |
|
} |
|
} |
|
|
|
return result; |
|
}, |
|
|
|
finishRender: Ext.emptyFn, |
|
|
|
finishRenderItems: function (target, items) { |
|
var length = items.length, |
|
i, item; |
|
|
|
for (i = 0; i < length; i++) { |
|
item = items[i]; |
|
|
|
// Only postprocess items which are being rendered. deferredRender may mean that only one has been rendered. |
|
if (item.rendering) { |
|
|
|
// Tell the item at which index in the Container it is |
|
item.finishRender(i); |
|
} |
|
} |
|
}, |
|
|
|
renderChildren: function () { |
|
var me = this, |
|
items = me.getLayoutItems(), |
|
target = me.getRenderTarget(); |
|
|
|
me.renderItems(items, target); |
|
}, |
|
|
|
/** |
|
* Iterates over all passed items, ensuring they are rendered. If the items are already rendered, |
|
* also determines if the items are in the proper place in the dom. |
|
* @protected |
|
*/ |
|
renderItems : function(items, target) { |
|
var me = this, |
|
ln = items.length, |
|
i = 0, |
|
item; |
|
|
|
if (ln) { |
|
Ext.suspendLayouts(); |
|
for (; i < ln; i++) { |
|
item = items[i]; |
|
if (item && !item.rendered) { |
|
me.renderItem(item, target, i); |
|
} else if (!me.isValidParent(item, target, i)) { |
|
me.moveItem(item, target, i); |
|
} else { |
|
// still need to configure the item, it may have moved in the container. |
|
me.configureItem(item); |
|
} |
|
} |
|
Ext.resumeLayouts(true); |
|
} |
|
}, |
|
|
|
/** |
|
* Validates item is in the proper place in the dom. |
|
* @protected |
|
*/ |
|
isValidParent : function(item, target, position) { |
|
var targetDom = (target && target.dom) || target, |
|
itemDom = this.getItemLayoutEl(item); |
|
|
|
// Test DOM nodes for equality using "===" : http://jsperf.com/dom-equality-test |
|
if (itemDom && targetDom) { |
|
if (typeof position === 'number') { |
|
position = this.getPositionOffset(position); |
|
return itemDom === targetDom.childNodes[position]; |
|
} |
|
return itemDom.parentNode === targetDom; |
|
} |
|
|
|
return false; |
|
}, |
|
|
|
/** |
|
* For a given item, returns the element that participates in the childNodes array |
|
* of the layout's target element. This is usually the component's "el", but can |
|
* also be a wrapper |
|
* @private |
|
* @param {Ext.Component} item |
|
* @return {HTMLElement} |
|
*/ |
|
getItemLayoutEl: function(item) { |
|
var dom = item.el ? item.el.dom : Ext.getDom(item), |
|
parentNode = dom.parentNode, |
|
className; |
|
|
|
if (parentNode) { |
|
className = parentNode.className; |
|
if (className && className.indexOf(Ext.baseCSSPrefix + 'resizable-wrap') !== -1) { |
|
dom = dom.parentNode; |
|
} |
|
} |
|
|
|
return dom; |
|
}, |
|
|
|
getPositionOffset: function(position){ |
|
return position; |
|
}, |
|
|
|
/** |
|
* Called before an item is rendered to allow the layout to configure the item. |
|
* @param {Ext.Component} item The item to be configured |
|
* @protected |
|
*/ |
|
configureItem: function(item) { |
|
item.ownerLayout = this; |
|
}, |
|
|
|
/** |
|
* Renders the given Component into the target Element. |
|
* @param {Ext.Component} item The Component to render |
|
* @param {Ext.dom.Element} target The target Element |
|
* @param {Number} position The position within the target to render the item to |
|
* @private |
|
*/ |
|
renderItem : function(item, target, position) { |
|
var me = this; |
|
|
|
if (!item.rendered) { |
|
me.configureItem(item); |
|
|
|
item.render(target, position); |
|
} |
|
}, |
|
|
|
/** |
|
* Moves Component to the provided target instead. |
|
* @private |
|
*/ |
|
moveItem : function(item, target, position) { |
|
target = target.dom || target; |
|
if (typeof position === 'number') { |
|
position = target.childNodes[position]; |
|
} |
|
target.insertBefore(item.el.dom, position || null); |
|
item.container = Ext.get(target); |
|
this.configureItem(item); |
|
}, |
|
|
|
/** |
|
* This method is called when a child item changes in some way. By default this calls |
|
* {@link Ext.Component#updateLayout} on this layout's owner. |
|
* |
|
* @param {Ext.Component} child The child item that has changed. |
|
* @return {Boolean} True if this layout has handled the content change. |
|
*/ |
|
onContentChange: function () { |
|
this.owner.updateLayout(); |
|
return true; |
|
}, |
|
|
|
/** |
|
* A one-time initialization method called just before rendering. |
|
* @protected |
|
*/ |
|
initLayout : function() { |
|
this.initialized = true; |
|
}, |
|
|
|
// @private Sets the layout owner |
|
setOwner : function(owner) { |
|
this.owner = owner; |
|
}, |
|
|
|
/** |
|
* Returns the set of items to layout (empty by default). |
|
* @protected |
|
*/ |
|
getLayoutItems : function() { |
|
return []; |
|
}, |
|
|
|
onAdd: function (item) { |
|
item.ownerLayout = this; |
|
}, |
|
|
|
onRemove : Ext.emptyFn, |
|
onDestroy : Ext.emptyFn, |
|
|
|
/** |
|
* Removes layout's itemCls and owning Container's itemCls. |
|
* Clears the managed dimensions flags |
|
* @protected |
|
*/ |
|
afterRemove: function(item) { |
|
var me = this, |
|
el = item.el, |
|
owner = me.owner, |
|
removeClasses; |
|
|
|
if (item.rendered) { |
|
removeClasses = [].concat(me.itemCls || []); |
|
if (owner.itemCls) { |
|
removeClasses = Ext.Array.push(removeClasses, owner.itemCls); |
|
} |
|
if (removeClasses.length) { |
|
el.removeCls(removeClasses); |
|
} |
|
} |
|
|
|
delete item.ownerLayout; |
|
}, |
|
|
|
/** |
|
* @private |
|
* Called by an owning Panel after the Panel finishes its collapse process. |
|
*/ |
|
afterCollapse: function(owner, animated) { |
|
if (animated) { |
|
this.onContentChange(owner); |
|
} |
|
}, |
|
|
|
/** |
|
* @private |
|
* Called by an owning Panel after the Panel finishes its expand process. |
|
*/ |
|
afterExpand: function(owner, animated) { |
|
if (animated) { |
|
this.onContentChange(owner); |
|
} |
|
}, |
|
|
|
/** |
|
* Destroys this layout. This method removes a `targetCls` from the `target` |
|
* element and calls `onDestroy`. |
|
* |
|
* A derived class can override either this method or `onDestroy` but in all |
|
* cases must call the base class versions of these methods to allow the base class to |
|
* perform its cleanup. |
|
* |
|
* This method (or `onDestroy`) are overridden by subclasses most often to purge |
|
* event handlers or remove unmanged DOM nodes. |
|
* |
|
* @protected |
|
*/ |
|
destroy : function() { |
|
var me = this, |
|
target; |
|
|
|
if (me.targetCls) { |
|
target = me.getTarget(); |
|
if (target) { |
|
target.removeCls(me.targetCls); |
|
} |
|
} |
|
|
|
me.onDestroy(); |
|
}, |
|
|
|
sortWeightedItems: function (items, reverseProp) { |
|
for (var i = 0, length = items.length; i < length; ++i) { |
|
items[i].$i = i; |
|
} |
|
|
|
Ext.Array.sort(items, function (item1, item2) { |
|
var ret = item2.weight - item1.weight; |
|
|
|
if (!ret) { |
|
ret = item1.$i - item2.$i; |
|
if (item1[reverseProp]) { |
|
ret = -ret; |
|
} |
|
} |
|
|
|
return ret; |
|
}); |
|
|
|
for (i = 0; i < length; ++i) { |
|
delete items[i].$i; |
|
} |
|
} |
|
}, function () { |
|
var Layout = this; |
|
|
|
Layout.prototype.sizeModels = Layout.sizeModels = Ext.layout.SizeModel.sizeModels; |
|
});
|
|
|