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.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) {
144 if (this.onLayout_running) {
147 this.onLayout_running = true;
151 layout = owner.layout,
152 target = me.getTarget(),
155 padding, border, frameSize;
157 // We start of by resetting all the layouts info
158 var info = me.info = {
166 // Clear isAutoDock flag
167 delete layout.isAutoDock;
169 Ext.applyIf(info, me.getTargetInfo());
171 // We need to bind to the ownerCt whenever we do not have a user set height or width.
172 if (owner && owner.ownerCt && owner.ownerCt.layout && owner.ownerCt.layout.isLayout) {
173 if (!Ext.isNumber(owner.height) || !Ext.isNumber(owner.width)) {
174 owner.ownerCt.layout.bindToOwnerCtComponent = true;
177 owner.ownerCt.layout.bindToOwnerCtComponent = false;
181 // Determine if we have an autoHeight or autoWidth.
182 if (height == null || width == null) {
183 padding = info.padding;
184 border = info.border;
185 frameSize = me.frameSize;
187 // Auto-everything, clear out any style height/width and read from css
188 if ((height == null) && (width == null)) {
191 me.setTargetSize(null);
192 me.setBodyBox({width: null, height: null});
195 else if (height == null) {
197 // Clear any sizing that we already set in a previous layout
198 me.setTargetSize(width);
199 me.setBodyBox({width: width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right, height: null});
204 // Clear any sizing that we already set in a previous layout
205 me.setTargetSize(null, height);
206 me.setBodyBox({width: null, height: height - padding.top - padding.bottom - border.top - border.bottom - frameSize.top - frameSize.bottom});
210 if (layout && layout.isLayout) {
211 // Auto-Sized so have the container layout notify the component layout.
212 layout.bindToOwnerCtComponent = true;
213 // Set flag so we don't do a redundant container layout
214 layout.isAutoDock = layout.autoSize !== true;
217 // If this is an autosized container layout, then we must compensate for a
218 // body that is being autosized. We do not want to adjust the body's size
219 // to accommodate the dock items, but rather we will want to adjust the
222 // This is necessary because, particularly in a Box layout, all child items
223 // are set with absolute dimensions that are not flexible to the size of its
224 // innerCt/target. So once they are laid out, they are sized for good. By
225 // shrinking the body box to accommodate dock items, we're merely cutting off
226 // parts of the body. Not good. Instead, the target's size should expand
227 // to fit the dock items in. This is valid because the target container is
228 // suppose to be autosized to fit everything accordingly.
229 info.autoSizedCtLayout = layout.autoSize === true;
230 info.autoHeight = autoHeight;
231 info.autoWidth = autoWidth;
234 // The dockItems method will add all the top and bottom docked items height
235 // to the info.panelSize height. That's why we have to call setSize after
236 // we dock all the items to actually set the panel's width and height.
237 // We have to do this because the panel body and docked items will be position
238 // absolute which doesn't stretch the panel.
240 me.setTargetSize(info.size.width, info.size.height);
243 me.setTargetSize(width, height);
246 me.callParent(arguments);
247 this.onLayout_running = false;
252 * This method will first update all the information about the docked items,
253 * body dimensions and position, the panel's total size. It will then
254 * set all these values on the docked items and panel body.
255 * @param {Array} items Array containing all the docked items
256 * @param {Boolean} autoBoxes Set this to true if the Panel is part of an
257 * AutoContainerLayout
259 dockItems : function() {
260 this.calculateDockBoxes();
262 // Both calculateAutoBoxes and calculateSizedBoxes are changing the
263 // information about the body, panel size, and boxes for docked items
264 // inside a property called info.
265 var info = this.info,
266 autoWidth = info.autoWidth,
267 autoHeight = info.autoHeight,
272 // We are going to loop over all the boxes that were calculated
273 // and set the position of each item the box belongs to.
274 for (i = 0; i < ln; i++) {
277 item.setPosition(dock.x, dock.y);
278 if ((autoWidth || autoHeight) && item.layout && item.layout.isLayout) {
279 // Auto-Sized so have the container layout notify the component layout.
280 item.layout.bindToOwnerCtComponent = true;
284 // Don't adjust body width/height if the target is using an auto container layout.
285 // But, we do want to adjust the body size if the container layout is auto sized.
286 if (!info.autoSizedCtLayout) {
288 info.bodyBox.width = null;
291 info.bodyBox.height = null;
295 // If the bodyBox has been adjusted because of the docked items
296 // we will update the dimensions and position of the panel's body.
297 this.setBodyBox(info.bodyBox);
302 * This method will set up some initial information about the panel size and bodybox
303 * and then loop over all the items you pass it to take care of stretching, aligning,
304 * dock position and all calculations involved with adjusting the body box.
305 * @param {Array} items Array containing all the docked items we have to layout
307 calculateDockBoxes : function() {
308 if (this.calculateDockBoxes_running) {
309 // [AbstractDock#calculateDockBoxes] attempted to run again while it was already running
312 this.calculateDockBoxes_running = true;
313 // We want to use the Panel's el width, and the Panel's body height as the initial
314 // size we are going to use in calculateDockBoxes. We also want to account for
315 // the border of the panel.
317 target = me.getTarget(),
318 items = me.getLayoutItems(),
322 autoWidth = info.autoWidth,
323 autoHeight = info.autoHeight,
326 padding = info.padding,
327 border = info.border,
328 frameSize = me.frameSize,
331 // If this Panel is inside an AutoContainerLayout, we will base all the calculations
332 // around the height of the body and the width of the panel.
334 size.height = bodyEl.getHeight() + padding.top + border.top + padding.bottom + border.bottom + frameSize.top + frameSize.bottom;
337 size.height = target.getHeight();
340 size.width = bodyEl.getWidth() + padding.left + border.left + padding.right + border.right + frameSize.left + frameSize.right;
343 size.width = target.getWidth();
347 x: padding.left + frameSize.left,
348 y: padding.top + frameSize.top,
349 width: size.width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right,
350 height: size.height - border.top - padding.top - border.bottom - padding.bottom - frameSize.top - frameSize.bottom
353 // Loop over all the docked items
354 for (i = 0; i < ln; i++) {
356 // The initBox method will take care of stretching and alignment
357 // In some cases it will also layout the dock items to be able to
358 // get a width or height measurement
359 box = me.initBox(item);
361 if (autoHeight === true) {
362 box = me.adjustAutoBox(box, i);
365 box = me.adjustSizedBox(box, i);
368 // Save our box. This allows us to loop over all docked items and do all
369 // calculations first. Then in one loop we will actually size and position
370 // all the docked items that have changed.
371 info.boxes.push(box);
373 this.calculateDockBoxes_running = false;
378 * This method will adjust the position of the docked item and adjust the body box
380 * @param {Object} box The box containing information about the width and height
381 * of this docked item
382 * @param {Number} index The index position of this docked item
383 * @return {Object} The adjusted box
385 adjustSizedBox : function(box, index) {
386 var bodyBox = this.info.bodyBox,
387 frameSize = this.frameSize,
389 padding = info.padding,
391 border = info.border;
403 box.y = (bodyBox.y + bodyBox.height) - box.height;
407 box.x = (bodyBox.x + bodyBox.width) - box.width;
411 if (box.ignoreFrame) {
412 if (pos == 'bottom') {
413 box.y += (frameSize.bottom + padding.bottom + border.bottom);
416 box.y -= (frameSize.top + padding.top + border.top);
418 if (pos == 'right') {
419 box.x += (frameSize.right + padding.right + border.right);
422 box.x -= (frameSize.left + padding.left + border.left);
426 // If this is not an overlaying docked item, we have to adjust the body box
430 bodyBox.y += box.height;
431 bodyBox.height -= box.height;
435 bodyBox.x += box.width;
436 bodyBox.width -= box.width;
440 bodyBox.height -= box.height;
444 bodyBox.width -= box.width;
453 * This method will adjust the position of the docked item inside an AutoContainerLayout
454 * and adjust the body box accordingly.
455 * @param {Object} box The box containing information about the width and height
456 * of this docked item
457 * @param {Number} index The index position of this docked item
458 * @return {Object} The adjusted box
460 adjustAutoBox : function (box, index) {
461 var info = this.info,
463 bodyBox = info.bodyBox,
466 boxesLn = boxes.length,
468 frameSize = this.frameSize,
469 padding = info.padding,
470 border = info.border,
471 autoSizedCtLayout = info.autoSizedCtLayout,
472 ln = (boxesLn < index) ? boxesLn : index,
475 if (pos == 'top' || pos == 'bottom') {
476 // This can affect the previously set left and right and bottom docked items
477 for (i = 0; i < ln; i++) {
478 adjustBox = boxes[i];
479 if (adjustBox.stretched && adjustBox.type == 'left' || adjustBox.type == 'right') {
480 adjustBox.height += box.height;
482 else if (adjustBox.type == 'bottom') {
483 adjustBox.y += box.height;
492 bodyBox.y += box.height;
493 if (info.autoHeight) {
494 size.height += box.height;
496 bodyBox.height -= box.height;
503 if (info.autoHeight) {
504 size.height += box.height;
506 bodyBox.height -= box.height;
509 box.y = (bodyBox.y + bodyBox.height);
515 bodyBox.x += box.width;
516 if (info.autoWidth) {
517 size.width += box.width;
519 bodyBox.width -= box.width;
526 if (info.autoWidth) {
527 size.width += box.width;
529 bodyBox.width -= box.width;
532 box.x = (bodyBox.x + bodyBox.width);
536 if (box.ignoreFrame) {
537 if (pos == 'bottom') {
538 box.y += (frameSize.bottom + padding.bottom + border.bottom);
541 box.y -= (frameSize.top + padding.top + border.top);
543 if (pos == 'right') {
544 box.x += (frameSize.right + padding.right + border.right);
547 box.x -= (frameSize.left + padding.left + border.left);
555 * This method will create a box object, with a reference to the item, the type of dock
556 * (top, left, bottom, right). It will also take care of stretching and aligning of the
558 * @param {Ext.Component} item The docked item we want to initialize the box for
559 * @return {Object} The initial box containing width and height and other useful information
561 initBox : function(item) {
563 bodyBox = me.info.bodyBox,
564 horizontal = (item.dock == 'top' || item.dock == 'bottom'),
566 frameSize = me.frameSize,
568 padding = info.padding,
569 border = info.border,
572 overlay: item.overlay,
574 offsets: Ext.Element.parseBox(item.offsets || {}),
575 ignoreFrame: item.ignoreParentFrame
577 // First we are going to take care of stretch and align properties for all four dock scenarios.
578 if (item.stretch !== false) {
579 box.stretched = true;
581 box.x = bodyBox.x + box.offsets.left;
582 box.width = bodyBox.width - (box.offsets.left + box.offsets.right);
583 if (box.ignoreFrame) {
584 box.width += (frameSize.left + frameSize.right + border.left + border.right + padding.left + padding.right);
586 item.setCalculatedSize(box.width - item.el.getMargin('lr'), undefined, owner);
589 box.y = bodyBox.y + box.offsets.top;
590 box.height = bodyBox.height - (box.offsets.bottom + box.offsets.top);
591 if (box.ignoreFrame) {
592 box.height += (frameSize.top + frameSize.bottom + border.top + border.bottom + padding.top + padding.bottom);
594 item.setCalculatedSize(undefined, box.height - item.el.getMargin('tb'), owner);
596 // At this point IE will report the left/right-docked toolbar as having a width equal to the
597 // container's full width. Forcing a repaint kicks it into shape so it reports the correct width.
598 if (!Ext.supports.ComputedStyle) {
604 item.doComponentLayout();
605 box.width = item.getWidth() - (box.offsets.left + box.offsets.right);
606 box.height = item.getHeight() - (box.offsets.bottom + box.offsets.top);
607 box.y += box.offsets.top;
609 box.x = (item.align == 'right') ? bodyBox.width - box.width : bodyBox.x;
610 box.x += box.offsets.left;
614 // If we haven't calculated the width or height of the docked item yet
615 // do so, since we need this for our upcoming calculations
616 if (box.width === undefined) {
617 box.width = item.getWidth() + item.el.getMargin('lr');
619 if (box.height === undefined) {
620 box.height = item.getHeight() + item.el.getMargin('tb');
628 * Returns an array containing all the <b>visible</b> docked items inside this layout's owner Panel
629 * @return {Array} An array containing all the <b>visible</b> docked items of the Panel
631 getLayoutItems : function() {
632 var it = this.owner.getDockedItems(),
636 for (; i < ln; i++) {
637 if (it[i].isVisible(true)) {
646 * Render the top and left docked items before any existing DOM nodes in our render target,
647 * and then render the right and bottom docked items after. This is important, for such things
648 * as tab stops and ARIA readers, that the DOM nodes are in a meaningful order.
649 * Our collection of docked items will already be ordered via Panel.getDockedItems().
651 renderItems: function(items, target) {
652 var cns = target.dom.childNodes,
658 // Calculate the number of DOM nodes in our target that are not our docked items
659 for (i = 0; i < cnsLn; i++) {
660 cn = Ext.get(cns[i]);
661 for (j = 0; j < ln; j++) {
663 if (item.rendered && (cn.id == item.el.id || cn.contains(item.el.id))) {
673 // Now we go through our docked items and render/move them
674 for (i = 0, j = 0; i < ln; i++, j++) {
677 // If we're now at the right/bottom docked item, we jump ahead in our
678 // DOM position, just past the existing DOM nodes.
680 // TODO: This is affected if users provide custom weight values to their
681 // docked items, which puts it out of (t,l,r,b) order. Avoiding a second
682 // sort operation here, for now, in the name of performance. getDockedItems()
683 // needs the sort operation not just for this layout-time rendering, but
684 // also for getRefItems() to return a logical ordering (FocusManager, CQ, et al).
685 if (i === j && (item.dock === 'right' || item.dock === 'bottom')) {
689 // Same logic as Layout.renderItems()
690 if (item && !item.rendered) {
691 this.renderItem(item, target, j);
693 else if (!this.isValidParent(item, target, j)) {
694 this.moveItem(item, target, j);
701 * This function will be called by the dockItems method. Since the body is positioned absolute,
702 * we need to give it dimensions and a position so that it is in the middle surrounded by
704 * @param {Object} box An object containing new x, y, width and height values for the
707 setBodyBox : function(box) {
712 bodyMargin = info.bodyMargin,
713 padding = info.padding,
714 border = info.border,
715 frameSize = me.frameSize;
717 // Panel collapse effectively hides the Panel's body, so this is a no-op.
718 if (owner.collapsed) {
722 if (Ext.isNumber(box.width)) {
723 box.width -= bodyMargin.left + bodyMargin.right;
726 if (Ext.isNumber(box.height)) {
727 box.height -= bodyMargin.top + bodyMargin.bottom;
730 me.setElementSize(body, box.width, box.height);
731 if (Ext.isNumber(box.x)) {
732 body.setLeft(box.x - padding.left - frameSize.left);
734 if (Ext.isNumber(box.y)) {
735 body.setTop(box.y - padding.top - frameSize.top);
741 * We are overriding the Ext.layout.Layout configureItem method to also add a class that
742 * indicates the position of the docked item. We use the itemCls (x-docked) as a prefix.
743 * An example of a class added to a dock: right item is x-docked-right
744 * @param {Ext.Component} item The item we are configuring
746 configureItem : function(item, pos) {
747 this.callParent(arguments);
748 if (item.dock == 'top' || item.dock == 'bottom') {
749 item.layoutManagedWidth = 1;
750 item.layoutManagedHeight = 2;
752 item.layoutManagedWidth = 2;
753 item.layoutManagedHeight = 1;
756 item.addCls(Ext.baseCSSPrefix + 'docked');
757 item.addClsWithUI('docked-' + item.dock);
760 afterRemove : function(item) {
761 this.callParent(arguments);
763 item.el.removeCls(this.itemCls + '-' + item.dock);
765 var dom = item.el.dom;
767 if (!item.destroying && dom) {
768 dom.parentNode.removeChild(dom);
770 this.childrenChanged = true;