2 * @class Ext.layout.component.AbstractDock
3 * @extends Ext.layout.component.Component
5 * This ComponentLayout handles docking for Panels. It takes care of panels that are
6 * part of a ContainerLayout that sets this Panel's size and Panels that are part of
7 * an AutoContainerLayout in which this panel get his height based of the CSS or
11 Ext.define('Ext.layout.component.AbstractDock', {
13 /* Begin Definitions */
15 extend: 'Ext.layout.component.Component',
23 * @property autoSizing
25 * This flag is set to indicate this layout may have an autoHeight/autoWidth.
29 beforeLayout: function() {
30 var returnValue = this.callParent(arguments);
31 if (returnValue !== false && (!this.initializedBorders || this.childrenChanged) && (!this.owner.border || this.owner.manageBodyBorders)) {
32 this.handleItemBorders();
33 this.initializedBorders = true;
38 handleItemBorders: function() {
39 var owner = this.owner,
41 docked = this.getLayoutItems(),
48 oldBorders = this.borders,
55 i, ln, item, dock, side;
57 for (i = 0, ln = docked.length; i < ln; i++) {
61 if (item.ignoreBorderManagement) {
65 if (!borders[dock].satisfied) {
66 borders[dock].push(item);
67 borders[dock].satisfied = true;
70 if (!borders.top.satisfied && opposites[dock] !== 'top') {
71 borders.top.push(item);
73 if (!borders.right.satisfied && opposites[dock] !== 'right') {
74 borders.right.push(item);
76 if (!borders.bottom.satisfied && opposites[dock] !== 'bottom') {
77 borders.bottom.push(item);
79 if (!borders.left.satisfied && opposites[dock] !== 'left') {
80 borders.left.push(item);
85 for (side in oldBorders) {
86 if (oldBorders.hasOwnProperty(side)) {
87 ln = oldBorders[side].length;
88 if (!owner.manageBodyBorders) {
89 for (i = 0; i < ln; i++) {
90 oldBorders[side][i].removeCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
92 if (!oldBorders[side].satisfied && !owner.bodyBorder) {
93 body.removeCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
96 else if (oldBorders[side].satisfied) {
97 body.setStyle('border-' + side + '-width', '');
103 for (side in borders) {
104 if (borders.hasOwnProperty(side)) {
105 ln = borders[side].length;
106 if (!owner.manageBodyBorders) {
107 for (i = 0; i < ln; i++) {
108 borders[side][i].addCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
110 if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) {
111 body.addCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
114 else if (borders[side].satisfied) {
115 body.setStyle('border-' + side + '-width', '1px');
120 this.borders = borders;
125 * @param {Ext.Component} owner The Panel that owns this DockLayout
126 * @param {Ext.core.Element} target The target in which we are going to render the docked items
127 * @param {Array} args The arguments passed to the ComponentLayout.layout method
129 onLayout: function(width, height) {
133 layout = owner.layout,
134 target = me.getTarget(),
137 padding, border, frameSize;
139 // We start of by resetting all the layouts info
140 var info = me.info = {
149 Ext.applyIf(info, me.getTargetInfo());
151 // We need to bind to the ownerCt whenever we do not have a user set height or width.
152 if (owner && owner.ownerCt && owner.ownerCt.layout && owner.ownerCt.layout.isLayout) {
153 if (!Ext.isNumber(owner.height) || !Ext.isNumber(owner.width)) {
154 owner.ownerCt.layout.bindToOwnerCtComponent = true;
157 owner.ownerCt.layout.bindToOwnerCtComponent = false;
161 // Determine if we have an autoHeight or autoWidth.
162 if (height === undefined || height === null || width === undefined || width === null) {
163 padding = info.padding;
164 border = info.border;
165 frameSize = me.frameSize;
167 // Auto-everything, clear out any style height/width and read from css
168 if ((height === undefined || height === null) && (width === undefined || width === null)) {
171 me.setTargetSize(null);
172 me.setBodyBox({width: null, height: null});
175 else if (height === undefined || height === null) {
177 // Clear any sizing that we already set in a previous layout
178 me.setTargetSize(width);
179 me.setBodyBox({width: width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right, height: null});
184 // Clear any sizing that we already set in a previous layout
185 me.setTargetSize(null, height);
186 me.setBodyBox({width: null, height: height - padding.top - padding.bottom - border.top - border.bottom - frameSize.top - frameSize.bottom});
190 if (layout && layout.isLayout) {
191 // Auto-Sized so have the container layout notify the component layout.
192 layout.bindToOwnerCtComponent = true;
195 // If this is an autosized container layout, then we must compensate for a
196 // body that is being autosized. We do not want to adjust the body's size
197 // to accommodate the dock items, but rather we will want to adjust the
200 // This is necessary because, particularly in a Box layout, all child items
201 // are set with absolute dimensions that are not flexible to the size of its
202 // innerCt/target. So once they are laid out, they are sized for good. By
203 // shrinking the body box to accommodate dock items, we're merely cutting off
204 // parts of the body. Not good. Instead, the target's size should expand
205 // to fit the dock items in. This is valid because the target container is
206 // suppose to be autosized to fit everything accordingly.
207 info.autoSizedCtLayout = layout.autoSize === true;
210 // The dockItems method will add all the top and bottom docked items height
211 // to the info.panelSize height. That's why we have to call setSize after
212 // we dock all the items to actually set the panel's width and height.
213 // We have to do this because the panel body and docked items will be position
214 // absolute which doesn't stretch the panel.
215 me.dockItems(autoWidth, autoHeight);
216 me.setTargetSize(info.size.width, info.size.height);
219 me.setTargetSize(width, height);
222 me.callParent(arguments);
227 * This method will first update all the information about the docked items,
228 * body dimensions and position, the panel's total size. It will then
229 * set all these values on the docked items and panel body.
230 * @param {Array} items Array containing all the docked items
231 * @param {Boolean} autoBoxes Set this to true if the Panel is part of an
232 * AutoContainerLayout
234 dockItems : function(autoWidth, autoHeight) {
235 this.calculateDockBoxes(autoWidth, autoHeight);
237 // Both calculateAutoBoxes and calculateSizedBoxes are changing the
238 // information about the body, panel size, and boxes for docked items
239 // inside a property called info.
240 var info = this.info,
245 // We are going to loop over all the boxes that were calculated
246 // and set the position of each item the box belongs to.
247 for (i = 0; i < ln; i++) {
249 dock.item.setPosition(dock.x, dock.y);
250 if ((autoWidth || autoHeight) && dock.layout && dock.layout.isLayout) {
251 // Auto-Sized so have the container layout notify the component layout.
252 dock.layout.bindToOwnerCtComponent = true;
256 // Don't adjust body width/height if the target is using an auto container layout.
257 // But, we do want to adjust the body size if the container layout is auto sized.
258 if (!info.autoSizedCtLayout) {
260 info.bodyBox.width = null;
263 info.bodyBox.height = null;
267 // If the bodyBox has been adjusted because of the docked items
268 // we will update the dimensions and position of the panel's body.
269 this.setBodyBox(info.bodyBox);
274 * This method will set up some initial information about the panel size and bodybox
275 * and then loop over all the items you pass it to take care of stretching, aligning,
276 * dock position and all calculations involved with adjusting the body box.
277 * @param {Array} items Array containing all the docked items we have to layout
279 calculateDockBoxes : function(autoWidth, autoHeight) {
280 // We want to use the Panel's el width, and the Panel's body height as the initial
281 // size we are going to use in calculateDockBoxes. We also want to account for
282 // the border of the panel.
284 target = me.getTarget(),
285 items = me.getLayoutItems(),
291 padding = info.padding,
292 border = info.border,
293 frameSize = me.frameSize,
296 // If this Panel is inside an AutoContainerLayout, we will base all the calculations
297 // around the height of the body and the width of the panel.
299 size.height = bodyEl.getHeight() + padding.top + border.top + padding.bottom + border.bottom + frameSize.top + frameSize.bottom;
302 size.height = target.getHeight();
305 size.width = bodyEl.getWidth() + padding.left + border.left + padding.right + border.right + frameSize.left + frameSize.right;
308 size.width = target.getWidth();
312 x: padding.left + frameSize.left,
313 y: padding.top + frameSize.top,
314 width: size.width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right,
315 height: size.height - border.top - padding.top - border.bottom - padding.bottom - frameSize.top - frameSize.bottom
318 // Loop over all the docked items
319 for (i = 0; i < ln; i++) {
321 // The initBox method will take care of stretching and alignment
322 // In some cases it will also layout the dock items to be able to
323 // get a width or height measurement
324 box = me.initBox(item);
326 if (autoHeight === true) {
327 box = me.adjustAutoBox(box, i);
330 box = me.adjustSizedBox(box, i);
333 // Save our box. This allows us to loop over all docked items and do all
334 // calculations first. Then in one loop we will actually size and position
335 // all the docked items that have changed.
336 info.boxes.push(box);
342 * This method will adjust the position of the docked item and adjust the body box
344 * @param {Object} box The box containing information about the width and height
345 * of this docked item
346 * @param {Number} index The index position of this docked item
347 * @return {Object} The adjusted box
349 adjustSizedBox : function(box, index) {
350 var bodyBox = this.info.bodyBox,
351 frameSize = this.frameSize,
353 padding = info.padding,
355 border = info.border;
367 box.y = (bodyBox.y + bodyBox.height) - box.height;
371 box.x = (bodyBox.x + bodyBox.width) - box.width;
375 if (box.ignoreFrame) {
376 if (pos == 'bottom') {
377 box.y += (frameSize.bottom + padding.bottom + border.bottom);
380 box.y -= (frameSize.top + padding.top + border.top);
382 if (pos == 'right') {
383 box.x += (frameSize.right + padding.right + border.right);
386 box.x -= (frameSize.left + padding.left + border.left);
390 // If this is not an overlaying docked item, we have to adjust the body box
394 bodyBox.y += box.height;
395 bodyBox.height -= box.height;
399 bodyBox.x += box.width;
400 bodyBox.width -= box.width;
404 bodyBox.height -= box.height;
408 bodyBox.width -= box.width;
417 * This method will adjust the position of the docked item inside an AutoContainerLayout
418 * and adjust the body box accordingly.
419 * @param {Object} box The box containing information about the width and height
420 * of this docked item
421 * @param {Number} index The index position of this docked item
422 * @return {Object} The adjusted box
424 adjustAutoBox : function (box, index) {
425 var info = this.info,
426 bodyBox = info.bodyBox,
429 boxesLn = boxes.length,
431 frameSize = this.frameSize,
432 padding = info.padding,
433 border = info.border,
434 autoSizedCtLayout = info.autoSizedCtLayout,
435 ln = (boxesLn < index) ? boxesLn : index,
438 if (pos == 'top' || pos == 'bottom') {
439 // This can affect the previously set left and right and bottom docked items
440 for (i = 0; i < ln; i++) {
441 adjustBox = boxes[i];
442 if (adjustBox.stretched && adjustBox.type == 'left' || adjustBox.type == 'right') {
443 adjustBox.height += box.height;
445 else if (adjustBox.type == 'bottom') {
446 adjustBox.y += box.height;
455 bodyBox.y += box.height;
457 size.height += box.height;
461 box.y = (bodyBox.y + bodyBox.height);
462 size.height += box.height;
468 bodyBox.x += box.width;
469 if (autoSizedCtLayout) {
470 size.width += box.width;
472 bodyBox.width -= box.width;
479 if (autoSizedCtLayout) {
480 size.width += box.width;
482 bodyBox.width -= box.width;
485 box.x = (bodyBox.x + bodyBox.width);
489 if (box.ignoreFrame) {
490 if (pos == 'bottom') {
491 box.y += (frameSize.bottom + padding.bottom + border.bottom);
494 box.y -= (frameSize.top + padding.top + border.top);
496 if (pos == 'right') {
497 box.x += (frameSize.right + padding.right + border.right);
500 box.x -= (frameSize.left + padding.left + border.left);
508 * This method will create a box object, with a reference to the item, the type of dock
509 * (top, left, bottom, right). It will also take care of stretching and aligning of the
511 * @param {Ext.Component} item The docked item we want to initialize the box for
512 * @return {Object} The initial box containing width and height and other useful information
514 initBox : function(item) {
516 bodyBox = me.info.bodyBox,
517 horizontal = (item.dock == 'top' || item.dock == 'bottom'),
519 frameSize = me.frameSize,
521 padding = info.padding,
522 border = info.border,
525 overlay: item.overlay,
527 offsets: Ext.core.Element.parseBox(item.offsets || {}),
528 ignoreFrame: item.ignoreParentFrame
530 // First we are going to take care of stretch and align properties for all four dock scenarios.
531 if (item.stretch !== false) {
532 box.stretched = true;
534 box.x = bodyBox.x + box.offsets.left;
535 box.width = bodyBox.width - (box.offsets.left + box.offsets.right);
536 if (box.ignoreFrame) {
537 box.width += (frameSize.left + frameSize.right + border.left + border.right + padding.left + padding.right);
539 item.setCalculatedSize(box.width - item.el.getMargin('lr'), undefined, owner);
542 box.y = bodyBox.y + box.offsets.top;
543 box.height = bodyBox.height - (box.offsets.bottom + box.offsets.top);
544 if (box.ignoreFrame) {
545 box.height += (frameSize.top + frameSize.bottom + border.top + border.bottom + padding.top + padding.bottom);
547 item.setCalculatedSize(undefined, box.height - item.el.getMargin('tb'), owner);
549 // At this point IE will report the left/right-docked toolbar as having a width equal to the
550 // container's full width. Forcing a repaint kicks it into shape so it reports the correct width.
551 if (!Ext.supports.ComputedStyle) {
557 item.doComponentLayout();
558 box.width = item.getWidth() - (box.offsets.left + box.offsets.right);
559 box.height = item.getHeight() - (box.offsets.bottom + box.offsets.top);
560 box.y += box.offsets.top;
562 box.x = (item.align == 'right') ? bodyBox.width - box.width : bodyBox.x;
563 box.x += box.offsets.left;
567 // If we haven't calculated the width or height of the docked item yet
568 // do so, since we need this for our upcoming calculations
569 if (box.width == undefined) {
570 box.width = item.getWidth() + item.el.getMargin('lr');
572 if (box.height == undefined) {
573 box.height = item.getHeight() + item.el.getMargin('tb');
581 * Returns an array containing all the <b>visible</b> docked items inside this layout's owner Panel
582 * @return {Array} An array containing all the <b>visible</b> docked items of the Panel
584 getLayoutItems : function() {
585 var it = this.owner.getDockedItems(),
589 for (; i < ln; i++) {
590 if (it[i].isVisible(true)) {
599 * Render the top and left docked items before any existing DOM nodes in our render target,
600 * and then render the right and bottom docked items after. This is important, for such things
601 * as tab stops and ARIA readers, that the DOM nodes are in a meaningful order.
602 * Our collection of docked items will already be ordered via Panel.getDockedItems().
604 renderItems: function(items, target) {
605 var cns = target.dom.childNodes,
611 // Calculate the number of DOM nodes in our target that are not our docked items
612 for (i = 0; i < cnsLn; i++) {
613 cn = Ext.get(cns[i]);
614 for (j = 0; j < ln; j++) {
616 if (item.rendered && (cn.id == item.el.id || cn.down('#' + item.el.id))) {
626 // Now we go through our docked items and render/move them
627 for (i = 0, j = 0; i < ln; i++, j++) {
630 // If we're now at the right/bottom docked item, we jump ahead in our
631 // DOM position, just past the existing DOM nodes.
633 // TODO: This is affected if users provide custom weight values to their
634 // docked items, which puts it out of (t,l,r,b) order. Avoiding a second
635 // sort operation here, for now, in the name of performance. getDockedItems()
636 // needs the sort operation not just for this layout-time rendering, but
637 // also for getRefItems() to return a logical ordering (FocusManager, CQ, et al).
638 if (i === j && (item.dock === 'right' || item.dock === 'bottom')) {
642 // Same logic as Layout.renderItems()
643 if (item && !item.rendered) {
644 this.renderItem(item, target, j);
646 else if (!this.isValidParent(item, target, j)) {
647 this.moveItem(item, target, j);
654 * This function will be called by the dockItems method. Since the body is positioned absolute,
655 * we need to give it dimensions and a position so that it is in the middle surrounded by
657 * @param {Object} box An object containing new x, y, width and height values for the
660 setBodyBox : function(box) {
665 bodyMargin = info.bodyMargin,
666 padding = info.padding,
667 border = info.border,
668 frameSize = me.frameSize;
670 // Panel collapse effectively hides the Panel's body, so this is a no-op.
671 if (owner.collapsed) {
675 if (Ext.isNumber(box.width)) {
676 box.width -= bodyMargin.left + bodyMargin.right;
679 if (Ext.isNumber(box.height)) {
680 box.height -= bodyMargin.top + bodyMargin.bottom;
683 me.setElementSize(body, box.width, box.height);
684 if (Ext.isNumber(box.x)) {
685 body.setLeft(box.x - padding.left - frameSize.left);
687 if (Ext.isNumber(box.y)) {
688 body.setTop(box.y - padding.top - frameSize.top);
694 * We are overriding the Ext.layout.Layout configureItem method to also add a class that
695 * indicates the position of the docked item. We use the itemCls (x-docked) as a prefix.
696 * An example of a class added to a dock: right item is x-docked-right
697 * @param {Ext.Component} item The item we are configuring
699 configureItem : function(item, pos) {
700 this.callParent(arguments);
702 item.addCls(Ext.baseCSSPrefix + 'docked');
703 item.addClsWithUI('docked-' + item.dock);
706 afterRemove : function(item) {
707 this.callParent(arguments);
709 item.el.removeCls(this.itemCls + '-' + item.dock);
711 var dom = item.el.dom;
713 if (!item.destroying && dom) {
714 dom.parentNode.removeChild(dom);
716 this.childrenChanged = true;