/** * @class Ext.layout.DockLayout * @extends Ext.layout.ComponentLayout * 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 * or its content. */ Ext.layout.DockLayout = Ext.extend(Ext.layout.ComponentLayout, { type: 'dock', /** * @property itemCls * @type String * This class is automatically added to each docked item within this layout. * We also use this as a prefix for the position class e.g. x-docked-bottom */ itemCls: 'x-docked', /** * @protected * @param {Ext.Component} owner The Panel that owns this DockLayout * @param {Ext.Element} target The target in which we are going to render the docked items * @param {Array} args The arguments passed to the ComponentLayout.layout method */ onLayout: function(width, height) { var me = this, owner = me.owner, body = owner.body, ownerCt = owner.ownerCt, layout = owner.layout, collapsed = owner.collapsed, contracted = owner.contracted, expanded = owner.expanded, headerItem = me.headerItem, target = me.getTarget(), autoWidth = false, autoHeight = false, animTo; // We start of by resetting all the layouts info var info = me.info = { boxes: [], size: { width: width, height: height }, padding: { top: target.getPadding('t'), right: target.getPadding('r'), bottom: target.getPadding('b'), left: target.getPadding('l') }, border: { top: target.getBorderWidth('t'), right: target.getBorderWidth('r'), bottom: target.getBorderWidth('b'), left: target.getBorderWidth('l') }, bodyMargin: { top: body.getMargin('t'), right: body.getMargin('r'), bottom: body.getMargin('b'), left: body.getMargin('l') }, bodyBox: {} }; // Determine if we have an autoHeight or autoWidth. if (height === undefined || height === null || width === undefined || width === null || contracted) { // Auto-everything, clear out any style height/width and read from css if ((height === undefined || height === null) && (width === undefined || width === null)) { autoHeight = true; autoWidth = true; if (!owner.animCollapse || (!expanded && !contracted)) { me.setTargetSize(null, null); } me.setBodyBox({width: null, height: null}); } // Auto-height else if (height === undefined || height === null) { autoHeight = true; // Clear any sizing that we already set in a previous layout if (!owner.animCollapse || (!expanded && !contracted)) { me.setTargetSize(width, null); } me.setBodyBox({width: width, height: null}); // Auto-width } else { autoWidth = true; // Clear any sizing that we already set in a previous layout if (!owner.animCollapse || (!expanded && !contracted)) { me.setTargetSize(null, height); } me.setBodyBox({width: null, height: height}); } // Run the container if (!collapsed && layout && layout.isLayout) { layout.layout(); } // The dockItems method will add all the top and bottom docked items height // to the info.panelSize height. Thats why we have to call setSize after // we dock all the items to actually set the panel's width and height. // We have to do this because the panel body and docked items will be position // absolute which doesnt stretch the panel. me.dockItems(autoWidth, autoHeight); if (collapsed) { if (headerItem) { if (headerItem.dock == 'top' || headerItem.dock == 'bottom') { info.size.height = headerItem.getHeight(); } else { info.size.width = headerItem.getWidths(); } } else { info.size.height = 0; } } if (expanded || contracted) { if (owner.animCollapse) { Ext.createDelegate(owner.animCollapseFn, owner, [info.size.width, info.size.height])(); } else { Ext.createDelegate(owner['after' + (expanded ? 'Expand' : 'Collapse')], owner)(); me.setTargetSize(info.size.width, info.size.height); } } else { me.setTargetSize(info.size.width, info.size.height); } } else { // If we get inside this else statement, it means that the Panel has been // given a size by its parents container layout. In this case we want to // actualy set the Panel's dimensions and dock all the items. if (expanded || contracted) { if (owner.animCollapse) { Ext.createDelegate(owner.animCollapseFn, owner, [width, height])(); } else { Ext.createDelegate(owner['after' + (expanded ? 'Expand' : 'Collapse')], owner)(); me.setTargetSize(width, height); } } else { me.setTargetSize(width, height); me.dockItems(); } } Ext.layout.DockLayout.superclass.onLayout.call(me, width, height); }, afterLayout : function() { Ext.layout.DockLayout.superclass.afterLayout.call(this); }, /** * @protected * This method will first update all the information about the docked items, * body dimensions and position, the panel's total size. It will then * set all these values on the docked items and panel body. * @param {Array} items Array containing all the docked items * @param {Boolean} autoBoxes Set this to true if the Panel is part of an * AutoContainerLayout */ dockItems : function(autoWidth, autoHeight) { this.calculateDockBoxes(autoWidth, autoHeight); // Both calculateAutoBoxes and calculateSizedBoxes are changing the // information about the body, panel size, and boxes for docked items // inside a property called info. var info = this.info, collapsed = this.owner.collapsed, boxes = info.boxes, ln = boxes.length, dock, i; // We are going to loop over all the boxes that were calculated // and set the position of each item the box belongs to. for (i = 0; i < ln; i++) { dock = boxes[i]; if (collapsed === true && !dock.isHeader) { continue; } dock.item.setPosition(dock.x, dock.y); } // If the bodyBox has been adjusted because of the docked items // we will update the dimensions and position of the panel's body. if (autoWidth) { info.bodyBox.width = null; } if (autoHeight) { info.bodyBox.height = null; } this.setBodyBox(info.bodyBox); }, /** * @protected * This method will set up some initial information about the panel size and bodybox * and then loop over all the items you pass it to take care of stretching, aligning, * dock position and all calculations involved with adjusting the body box. * @param {Array} items Array containing all the docked items we have to layout */ calculateDockBoxes : function(autoWidth, autoHeight) { // We want to use the Panel's el width, and the Panel's body height as the initial // size we are going to use in calculateDockBoxes. We also want to account for // the border of the panel. var me = this, target = me.getTarget(), items = me.getLayoutItems(), owner = me.owner, contracted = owner.contracted, expanded = owner.expanded, bodyEl = owner.body, info = me.info, size = info.size, ln = items.length, padding = info.padding, border = info.border, item, i, box, w, h, itemEl, vis; // If this Panel is inside an AutoContainerLayout, we will base all the calculations // around the height of the body and the width of the panel. if (autoHeight) { size.height = bodyEl.getHeight() + padding.top + border.top + padding.bottom + border.bottom; } else { size.height = target.getHeight() - target.getMargin('tb'); } if (autoWidth) { size.width = bodyEl.getWidth() + padding.left + border.left + padding.right + border.right; } else { size.width = target.getWidth() - target.getMargin('lr'); } info.bodyBox = { x: border.left + padding.left, y: border.top + padding.top, width: size.width - padding.left - border.left - padding.right - border.right, height: size.height - border.top - padding.top - border.bottom - padding.bottom }; // Loop over all the docked items for (i = 0; i < ln; i++) { item = items[i]; if (item.isHeader) { me.headerItem = item; } // The initBox method will take care of stretching and alignment // In some cases it will also layout the dock items to be able to // get a width or height measurement box = me.initBox(item); if (autoHeight === true) { box = me.adjustAutoBox(box, i); } else { box = me.adjustSizedBox(box, i); } // Save our box. This allows us to loop over all docked items and do all // calculations first. Then in one loop we will actually size and position // all the docked items that have changed. info.boxes.push(box); } }, /** * @protected * This method will adjust the position of the docked item and adjust the body box * accordingly. * @param {Object} box The box containing information about the width and height * of this docked item * @param {Number} index The index position of this docked item * @return {Object} The adjusted box */ adjustSizedBox : function(box, index) { var bodyBox = this.info.bodyBox; switch (box.type) { case 'top': box.y = bodyBox.y; break; case 'left': box.x = bodyBox.x; break; case 'bottom': box.y = (bodyBox.y + bodyBox.height) - box.height; break; case 'right': box.x = (bodyBox.x + bodyBox.width) - box.width; break; } // If this is not an overlaying docked item, we have to adjust the body box if (!box.overlay) { switch (box.type) { case 'top': bodyBox.y += box.height; bodyBox.height -= box.height; break; case 'left': bodyBox.x += box.width; bodyBox.width -= box.width; break; case 'bottom': bodyBox.height -= box.height; break; case 'right': bodyBox.width -= box.width; break; } } return box; }, /** * @protected * This method will adjust the position of the docked item inside an AutoContainerLayout * and adjust the body box accordingly. * @param {Object} box The box containing information about the width and height * of this docked item * @param {Number} index The index position of this docked item * @return {Object} The adjusted box */ adjustAutoBox : function (box, index) { var info = this.info, bodyBox = info.bodyBox, size = info.size, boxes = info.boxes, pos = box.type, i, adjustBox; if (pos == 'top' || pos == 'bottom') { // This can affect the previously set left and right and bottom docked items for (i = 0; i < index; i++) { adjustBox = boxes[i]; if (adjustBox.stretched && adjustBox.type == 'left' || adjustBox.type == 'right') { adjustBox.height += box.height; } else if (adjustBox.type == 'bottom') { adjustBox.y += box.height; } } } switch (pos) { case 'top': box.y = bodyBox.y; if (!box.overlay) { bodyBox.y += box.height; } size.height += box.height; break; case 'bottom': box.y = (bodyBox.y + bodyBox.height); size.height += box.height; break; case 'left': box.x = bodyBox.x; if (!box.overlay) { bodyBox.x += box.width; bodyBox.width -= box.width; } break; case 'right': if (!box.overlay) { bodyBox.width -= box.width; } box.x = (bodyBox.x + bodyBox.width); break; } return box; }, /** * @protected * This method will create a box object, with a reference to the item, the type of dock * (top, left, bottom, right). It will also take care of stretching and aligning of the * docked items. * @param {Ext.Component} item The docked item we want to initialize the box for * @return {Object} The initial box containing width and height and other useful information */ initBox : function(item) { var bodyBox = this.info.bodyBox, horizontal = (item.dock == 'top' || item.dock == 'bottom'), box = { item: item, overlay: item.overlay, type: item.dock }; // First we are going to take care of stretch and align properties for all four dock scenarios. if (item.stretch !== false) { box.stretched = true; if (horizontal) { box.x = bodyBox.x; box.width = bodyBox.width; item.doComponentLayout(box.width - item.el.getMargin('lr')); } else { box.y = bodyBox.y; box.height = bodyBox.height; item.doComponentLayout(undefined, box.height - item.el.getMargin('tb')); } } else { item.doComponentLayout(); box.width = item.getWidth(); box.height = item.getHeight(); if (horizontal) { box.x = (item.align == 'right') ? bodyBox.width - box.width : bodyBox.x; } } // If we havent calculated the width or height of the docked item yet // do so, since we need this for our upcoming calculations if (box.width == undefined) { box.width = item.getWidth() + item.el.getMargin('lr'); } if (box.height == undefined) { box.height = item.getHeight() + item.el.getMargin('tb'); } return box; }, /** * @protected * Returns an array containing all the docked items inside this layout's owner panel * @return {Array} An array containing all the docked items of the Panel */ getLayoutItems : function() { return this.owner.getDockedItems(); }, /** * @protected * This function will be called by the dockItems method. Since the body is positioned absolute, * we need to give it dimensions and a position so that it is in the middle surrounded by * docked items * @param {Object} box An object containing new x, y, width and height values for the * Panel's body */ setBodyBox : function(box) { var me = this, owner = me.owner, body = owner.body, contracted = owner.contracted, expanded = owner.expanded, info = me.info, bodyMargin = info.bodyMargin, padding = info.padding, border = info.border; if (Ext.isNumber(box.width)) { box.width -= bodyMargin.left + bodyMargin.right; } if (Ext.isNumber(box.height)) { box.height -= bodyMargin.top + bodyMargin.bottom; } me.setElementSize(body, box.width, box.height); body.setLeft(box.x - padding.left - border.left); body.setTop(box.y - padding.top - border.top); }, /** * @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) { Ext.layout.DockLayout.superclass.configureItem.call(this, item, pos); var el = item.el || Ext.get(item); if (this.itemCls) { el.addCls(this.itemCls + '-' + item.dock); } }, afterRemove : function(item) { Ext.layout.DockLayout.superclass.afterRemove.call(this, item); if (this.itemCls) { item.el.removeCls(this.itemCls + '-' + item.dock); } var dom = item.el.dom; if (dom) { dom.parentNode.removeChild(dom); } this.childrenChanged = true; } }); Ext.regLayout('dock', Ext.layout.DockLayout);