3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.layout.component.AbstractDock
17 * @extends Ext.layout.component.Component
19 * This ComponentLayout handles docking for Panels. It takes care of panels that are
20 * part of a ContainerLayout that sets this Panel's size and Panels that are part of
21 * an AutoContainerLayout in which this panel get his height based of the CSS or
25 Ext.define('Ext.layout.component.AbstractDock', {
27 /* Begin Definitions */
29 extend: 'Ext.layout.component.Component',
37 * @property autoSizing
39 * This flag is set to indicate this layout may have an autoHeight/autoWidth.
43 beforeLayout: function() {
44 var returnValue = this.callParent(arguments);
45 if (returnValue !== false && (!this.initializedBorders || this.childrenChanged) && (!this.owner.border || this.owner.manageBodyBorders)) {
46 this.handleItemBorders();
47 this.initializedBorders = true;
52 handleItemBorders: function() {
53 var owner = this.owner,
55 docked = this.getLayoutItems(),
62 oldBorders = this.borders,
69 i, ln, item, dock, side;
71 for (i = 0, ln = docked.length; i < ln; i++) {
75 if (item.ignoreBorderManagement) {
79 if (!borders[dock].satisfied) {
80 borders[dock].push(item);
81 borders[dock].satisfied = true;
84 if (!borders.top.satisfied && opposites[dock] !== 'top') {
85 borders.top.push(item);
87 if (!borders.right.satisfied && opposites[dock] !== 'right') {
88 borders.right.push(item);
90 if (!borders.bottom.satisfied && opposites[dock] !== 'bottom') {
91 borders.bottom.push(item);
93 if (!borders.left.satisfied && opposites[dock] !== 'left') {
94 borders.left.push(item);
99 for (side in oldBorders) {
100 if (oldBorders.hasOwnProperty(side)) {
101 ln = oldBorders[side].length;
102 if (!owner.manageBodyBorders) {
103 for (i = 0; i < ln; i++) {
104 oldBorders[side][i].removeCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
106 if (!oldBorders[side].satisfied && !owner.bodyBorder) {
107 body.removeCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
110 else if (oldBorders[side].satisfied) {
111 body.setStyle('border-' + side + '-width', '');
117 for (side in borders) {
118 if (borders.hasOwnProperty(side)) {
119 ln = borders[side].length;
120 if (!owner.manageBodyBorders) {
121 for (i = 0; i < ln; i++) {
122 borders[side][i].addCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
124 if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) {
125 body.addCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
128 else if (borders[side].satisfied) {
129 body.setStyle('border-' + side + '-width', '1px');
134 this.borders = borders;
139 * @param {Ext.Component} owner The Panel that owns this DockLayout
140 * @param {Ext.core.Element} target The target in which we are going to render the docked items
141 * @param {Array} args The arguments passed to the ComponentLayout.layout method
143 onLayout: function(width, height) {
147 layout = owner.layout,
148 target = me.getTarget(),
151 padding, border, frameSize;
153 // We start of by resetting all the layouts info
154 var info = me.info = {
162 // Clear isAutoDock flag
163 delete layout.isAutoDock;
165 Ext.applyIf(info, me.getTargetInfo());
167 // We need to bind to the ownerCt whenever we do not have a user set height or width.
168 if (owner && owner.ownerCt && owner.ownerCt.layout && owner.ownerCt.layout.isLayout) {
169 if (!Ext.isNumber(owner.height) || !Ext.isNumber(owner.width)) {
170 owner.ownerCt.layout.bindToOwnerCtComponent = true;
173 owner.ownerCt.layout.bindToOwnerCtComponent = false;
177 // Determine if we have an autoHeight or autoWidth.
178 if (height === undefined || height === null || width === undefined || width === null) {
179 padding = info.padding;
180 border = info.border;
181 frameSize = me.frameSize;
183 // Auto-everything, clear out any style height/width and read from css
184 if ((height === undefined || height === null) && (width === undefined || width === null)) {
187 me.setTargetSize(null);
188 me.setBodyBox({width: null, height: null});
191 else if (height === undefined || height === null) {
193 // Clear any sizing that we already set in a previous layout
194 me.setTargetSize(width);
195 me.setBodyBox({width: width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right, height: null});
200 // Clear any sizing that we already set in a previous layout
201 me.setTargetSize(null, height);
202 me.setBodyBox({width: null, height: height - padding.top - padding.bottom - border.top - border.bottom - frameSize.top - frameSize.bottom});
206 if (layout && layout.isLayout) {
207 // Auto-Sized so have the container layout notify the component layout.
208 layout.bindToOwnerCtComponent = true;
209 // Set flag so we don't do a redundant container layout
210 layout.isAutoDock = layout.autoSize !== true;
213 // If this is an autosized container layout, then we must compensate for a
214 // body that is being autosized. We do not want to adjust the body's size
215 // to accommodate the dock items, but rather we will want to adjust the
218 // This is necessary because, particularly in a Box layout, all child items
219 // are set with absolute dimensions that are not flexible to the size of its
220 // innerCt/target. So once they are laid out, they are sized for good. By
221 // shrinking the body box to accommodate dock items, we're merely cutting off
222 // parts of the body. Not good. Instead, the target's size should expand
223 // to fit the dock items in. This is valid because the target container is
224 // suppose to be autosized to fit everything accordingly.
225 info.autoSizedCtLayout = layout.autoSize === true;
228 // The dockItems method will add all the top and bottom docked items height
229 // to the info.panelSize height. That's why we have to call setSize after
230 // we dock all the items to actually set the panel's width and height.
231 // We have to do this because the panel body and docked items will be position
232 // absolute which doesn't stretch the panel.
233 me.dockItems(autoWidth, autoHeight);
234 me.setTargetSize(info.size.width, info.size.height);
237 me.setTargetSize(width, height);
240 me.callParent(arguments);
245 * This method will first update all the information about the docked items,
246 * body dimensions and position, the panel's total size. It will then
247 * set all these values on the docked items and panel body.
248 * @param {Array} items Array containing all the docked items
249 * @param {Boolean} autoBoxes Set this to true if the Panel is part of an
250 * AutoContainerLayout
252 dockItems : function(autoWidth, autoHeight) {
253 this.calculateDockBoxes(autoWidth, autoHeight);
255 // Both calculateAutoBoxes and calculateSizedBoxes are changing the
256 // information about the body, panel size, and boxes for docked items
257 // inside a property called info.
258 var info = this.info,
263 // We are going to loop over all the boxes that were calculated
264 // and set the position of each item the box belongs to.
265 for (i = 0; i < ln; i++) {
267 dock.item.setPosition(dock.x, dock.y);
268 if ((autoWidth || autoHeight) && dock.layout && dock.layout.isLayout) {
269 // Auto-Sized so have the container layout notify the component layout.
270 dock.layout.bindToOwnerCtComponent = true;
274 // Don't adjust body width/height if the target is using an auto container layout.
275 // But, we do want to adjust the body size if the container layout is auto sized.
276 if (!info.autoSizedCtLayout) {
278 info.bodyBox.width = null;
281 info.bodyBox.height = null;
285 // If the bodyBox has been adjusted because of the docked items
286 // we will update the dimensions and position of the panel's body.
287 this.setBodyBox(info.bodyBox);
292 * This method will set up some initial information about the panel size and bodybox
293 * and then loop over all the items you pass it to take care of stretching, aligning,
294 * dock position and all calculations involved with adjusting the body box.
295 * @param {Array} items Array containing all the docked items we have to layout
297 calculateDockBoxes : function(autoWidth, autoHeight) {
298 // We want to use the Panel's el width, and the Panel's body height as the initial
299 // size we are going to use in calculateDockBoxes. We also want to account for
300 // the border of the panel.
302 target = me.getTarget(),
303 items = me.getLayoutItems(),
309 padding = info.padding,
310 border = info.border,
311 frameSize = me.frameSize,
314 // If this Panel is inside an AutoContainerLayout, we will base all the calculations
315 // around the height of the body and the width of the panel.
317 size.height = bodyEl.getHeight() + padding.top + border.top + padding.bottom + border.bottom + frameSize.top + frameSize.bottom;
320 size.height = target.getHeight();
323 size.width = bodyEl.getWidth() + padding.left + border.left + padding.right + border.right + frameSize.left + frameSize.right;
326 size.width = target.getWidth();
330 x: padding.left + frameSize.left,
331 y: padding.top + frameSize.top,
332 width: size.width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right,
333 height: size.height - border.top - padding.top - border.bottom - padding.bottom - frameSize.top - frameSize.bottom
336 // Loop over all the docked items
337 for (i = 0; i < ln; i++) {
339 // The initBox method will take care of stretching and alignment
340 // In some cases it will also layout the dock items to be able to
341 // get a width or height measurement
342 box = me.initBox(item);
344 if (autoHeight === true) {
345 box = me.adjustAutoBox(box, i);
348 box = me.adjustSizedBox(box, i);
351 // Save our box. This allows us to loop over all docked items and do all
352 // calculations first. Then in one loop we will actually size and position
353 // all the docked items that have changed.
354 info.boxes.push(box);
360 * This method will adjust the position of the docked item and adjust the body box
362 * @param {Object} box The box containing information about the width and height
363 * of this docked item
364 * @param {Number} index The index position of this docked item
365 * @return {Object} The adjusted box
367 adjustSizedBox : function(box, index) {
368 var bodyBox = this.info.bodyBox,
369 frameSize = this.frameSize,
371 padding = info.padding,
373 border = info.border;
385 box.y = (bodyBox.y + bodyBox.height) - box.height;
389 box.x = (bodyBox.x + bodyBox.width) - box.width;
393 if (box.ignoreFrame) {
394 if (pos == 'bottom') {
395 box.y += (frameSize.bottom + padding.bottom + border.bottom);
398 box.y -= (frameSize.top + padding.top + border.top);
400 if (pos == 'right') {
401 box.x += (frameSize.right + padding.right + border.right);
404 box.x -= (frameSize.left + padding.left + border.left);
408 // If this is not an overlaying docked item, we have to adjust the body box
412 bodyBox.y += box.height;
413 bodyBox.height -= box.height;
417 bodyBox.x += box.width;
418 bodyBox.width -= box.width;
422 bodyBox.height -= box.height;
426 bodyBox.width -= box.width;
435 * This method will adjust the position of the docked item inside an AutoContainerLayout
436 * and adjust the body box accordingly.
437 * @param {Object} box The box containing information about the width and height
438 * of this docked item
439 * @param {Number} index The index position of this docked item
440 * @return {Object} The adjusted box
442 adjustAutoBox : function (box, index) {
443 var info = this.info,
445 bodyBox = info.bodyBox,
448 boxesLn = boxes.length,
450 frameSize = this.frameSize,
451 padding = info.padding,
452 border = info.border,
453 autoSizedCtLayout = info.autoSizedCtLayout,
454 ln = (boxesLn < index) ? boxesLn : index,
457 if (pos == 'top' || pos == 'bottom') {
458 // This can affect the previously set left and right and bottom docked items
459 for (i = 0; i < ln; i++) {
460 adjustBox = boxes[i];
461 if (adjustBox.stretched && adjustBox.type == 'left' || adjustBox.type == 'right') {
462 adjustBox.height += box.height;
464 else if (adjustBox.type == 'bottom') {
465 adjustBox.y += box.height;
474 bodyBox.y += box.height;
475 if (owner.isFixedHeight()) {
476 bodyBox.height -= box.height;
478 size.height += box.height;
485 if (owner.isFixedHeight()) {
486 bodyBox.height -= box.height;
488 size.height += box.height;
491 box.y = (bodyBox.y + bodyBox.height);
497 bodyBox.x += box.width;
498 if (owner.isFixedWidth()) {
499 bodyBox.width -= box.width;
501 size.width += box.width;
508 if (owner.isFixedWidth()) {
509 bodyBox.width -= box.width;
511 size.width += box.width;
514 box.x = (bodyBox.x + bodyBox.width);
518 if (box.ignoreFrame) {
519 if (pos == 'bottom') {
520 box.y += (frameSize.bottom + padding.bottom + border.bottom);
523 box.y -= (frameSize.top + padding.top + border.top);
525 if (pos == 'right') {
526 box.x += (frameSize.right + padding.right + border.right);
529 box.x -= (frameSize.left + padding.left + border.left);
537 * This method will create a box object, with a reference to the item, the type of dock
538 * (top, left, bottom, right). It will also take care of stretching and aligning of the
540 * @param {Ext.Component} item The docked item we want to initialize the box for
541 * @return {Object} The initial box containing width and height and other useful information
543 initBox : function(item) {
545 bodyBox = me.info.bodyBox,
546 horizontal = (item.dock == 'top' || item.dock == 'bottom'),
548 frameSize = me.frameSize,
550 padding = info.padding,
551 border = info.border,
554 overlay: item.overlay,
556 offsets: Ext.core.Element.parseBox(item.offsets || {}),
557 ignoreFrame: item.ignoreParentFrame
559 // First we are going to take care of stretch and align properties for all four dock scenarios.
560 if (item.stretch !== false) {
561 box.stretched = true;
563 box.x = bodyBox.x + box.offsets.left;
564 box.width = bodyBox.width - (box.offsets.left + box.offsets.right);
565 if (box.ignoreFrame) {
566 box.width += (frameSize.left + frameSize.right + border.left + border.right + padding.left + padding.right);
568 item.setCalculatedSize(box.width - item.el.getMargin('lr'), undefined, owner);
571 box.y = bodyBox.y + box.offsets.top;
572 box.height = bodyBox.height - (box.offsets.bottom + box.offsets.top);
573 if (box.ignoreFrame) {
574 box.height += (frameSize.top + frameSize.bottom + border.top + border.bottom + padding.top + padding.bottom);
576 item.setCalculatedSize(undefined, box.height - item.el.getMargin('tb'), owner);
578 // At this point IE will report the left/right-docked toolbar as having a width equal to the
579 // container's full width. Forcing a repaint kicks it into shape so it reports the correct width.
580 if (!Ext.supports.ComputedStyle) {
586 item.doComponentLayout();
587 box.width = item.getWidth() - (box.offsets.left + box.offsets.right);
588 box.height = item.getHeight() - (box.offsets.bottom + box.offsets.top);
589 box.y += box.offsets.top;
591 box.x = (item.align == 'right') ? bodyBox.width - box.width : bodyBox.x;
592 box.x += box.offsets.left;
596 // If we haven't calculated the width or height of the docked item yet
597 // do so, since we need this for our upcoming calculations
598 if (box.width == undefined) {
599 box.width = item.getWidth() + item.el.getMargin('lr');
601 if (box.height == undefined) {
602 box.height = item.getHeight() + item.el.getMargin('tb');
610 * Returns an array containing all the <b>visible</b> docked items inside this layout's owner Panel
611 * @return {Array} An array containing all the <b>visible</b> docked items of the Panel
613 getLayoutItems : function() {
614 var it = this.owner.getDockedItems(),
618 for (; i < ln; i++) {
619 if (it[i].isVisible(true)) {
628 * Render the top and left docked items before any existing DOM nodes in our render target,
629 * and then render the right and bottom docked items after. This is important, for such things
630 * as tab stops and ARIA readers, that the DOM nodes are in a meaningful order.
631 * Our collection of docked items will already be ordered via Panel.getDockedItems().
633 renderItems: function(items, target) {
634 var cns = target.dom.childNodes,
640 // Calculate the number of DOM nodes in our target that are not our docked items
641 for (i = 0; i < cnsLn; i++) {
642 cn = Ext.get(cns[i]);
643 for (j = 0; j < ln; j++) {
645 if (item.rendered && (cn.id == item.el.id || cn.down('#' + item.el.id))) {
655 // Now we go through our docked items and render/move them
656 for (i = 0, j = 0; i < ln; i++, j++) {
659 // If we're now at the right/bottom docked item, we jump ahead in our
660 // DOM position, just past the existing DOM nodes.
662 // TODO: This is affected if users provide custom weight values to their
663 // docked items, which puts it out of (t,l,r,b) order. Avoiding a second
664 // sort operation here, for now, in the name of performance. getDockedItems()
665 // needs the sort operation not just for this layout-time rendering, but
666 // also for getRefItems() to return a logical ordering (FocusManager, CQ, et al).
667 if (i === j && (item.dock === 'right' || item.dock === 'bottom')) {
671 // Same logic as Layout.renderItems()
672 if (item && !item.rendered) {
673 this.renderItem(item, target, j);
675 else if (!this.isValidParent(item, target, j)) {
676 this.moveItem(item, target, j);
683 * This function will be called by the dockItems method. Since the body is positioned absolute,
684 * we need to give it dimensions and a position so that it is in the middle surrounded by
686 * @param {Object} box An object containing new x, y, width and height values for the
689 setBodyBox : function(box) {
694 bodyMargin = info.bodyMargin,
695 padding = info.padding,
696 border = info.border,
697 frameSize = me.frameSize;
699 // Panel collapse effectively hides the Panel's body, so this is a no-op.
700 if (owner.collapsed) {
704 if (Ext.isNumber(box.width)) {
705 box.width -= bodyMargin.left + bodyMargin.right;
708 if (Ext.isNumber(box.height)) {
709 box.height -= bodyMargin.top + bodyMargin.bottom;
712 me.setElementSize(body, box.width, box.height);
713 if (Ext.isNumber(box.x)) {
714 body.setLeft(box.x - padding.left - frameSize.left);
716 if (Ext.isNumber(box.y)) {
717 body.setTop(box.y - padding.top - frameSize.top);
723 * We are overriding the Ext.layout.Layout configureItem method to also add a class that
724 * indicates the position of the docked item. We use the itemCls (x-docked) as a prefix.
725 * An example of a class added to a dock: right item is x-docked-right
726 * @param {Ext.Component} item The item we are configuring
728 configureItem : function(item, pos) {
729 this.callParent(arguments);
730 if (item.dock == 'top' || item.dock == 'bottom') {
731 item.layoutManagedWidth = 1;
732 item.layoutManagedHeight = 2;
734 item.layoutManagedWidth = 2;
735 item.layoutManagedHeight = 1;
738 item.addCls(Ext.baseCSSPrefix + 'docked');
739 item.addClsWithUI('docked-' + item.dock);
742 afterRemove : function(item) {
743 this.callParent(arguments);
745 item.el.removeCls(this.itemCls + '-' + item.dock);
747 var dom = item.el.dom;
749 if (!item.destroying && dom) {
750 dom.parentNode.removeChild(dom);
752 this.childrenChanged = true;