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.container.Border
17 * @extends Ext.layout.container.Container
18 * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
19 * nested panels, automatic bars between regions and built-in
20 * {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions.</p>
21 * <p>This class is intended to be extended or created via the <code>layout:'border'</code>
22 * {@link Ext.container.Container#layout} config, and should generally not need to be created directly
23 * via the new keyword.</p>
24 * {@img Ext.layout.container.Border/Ext.layout.container.Border.png Ext.layout.container.Border container layout}
25 * <p>Example usage:</p>
27 Ext.create('Ext.panel.Panel', {
30 title: 'Border Layout',
33 title: 'South Region is resizable',
34 region: 'south', // position for region
37 split: true, // enable resizing
40 // xtype: 'panel' implied by default
41 title: 'West Region is collapsible',
46 collapsible: true, // make collapsible
47 id: 'west-region-container',
50 title: 'Center Region',
51 region: 'center', // center region is required, no width/height specified
56 renderTo: Ext.getBody()
59 * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
60 * <li>Any Container using the Border layout <b>must</b> have a child item with <code>region:'center'</code>.
61 * The child item in the center region will always be resized to fill the remaining space not used by
62 * the other regions in the layout.</li>
63 * <li>Any child items with a region of <code>west</code> or <code>east</code> may be configured with either
64 * an initial <code>width</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
65 * <li>Any child items with a region of <code>north</code> or <code>south</code> may be configured with either
66 * an initial <code>height</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
67 * <li>The regions of a BorderLayout are <b>fixed at render time</b> and thereafter, its child Components may not be removed or added</b>.To add/remove
68 * Components within a BorderLayout, have them wrapped by an additional Container which is directly
69 * managed by the BorderLayout. If the region is to be collapsible, the Container used directly
70 * by the BorderLayout manager should be a Panel. In the following example a Container (an Ext.panel.Panel)
71 * is added to the west region:<pre><code>
72 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
73 wrc.{@link Ext.container.Container#removeAll removeAll}();
74 wrc.{@link Ext.container.Container#add add}({
80 * <li><b>There is no BorderLayout.Region class in ExtJS 4.0+</b></li>
83 Ext.define('Ext.layout.container.Border', {
85 alias: ['layout.border'],
86 extend: 'Ext.layout.container.Container',
87 requires: ['Ext.resizer.Splitter', 'Ext.container.Container', 'Ext.fx.Anim'],
88 alternateClassName: 'Ext.layout.BorderLayout',
90 targetCls: Ext.baseCSSPrefix + 'border-layout-ct',
92 itemCls: Ext.baseCSSPrefix + 'border-item',
94 bindToOwnerCtContainer: true,
96 percentageRe: /(\d+)%/,
105 constructor: function(config) {
106 this.initialConfig = config;
107 this.callParent(arguments);
110 onLayout: function() {
112 if (!me.borderLayoutInitialized) {
113 me.initializeBorderLayout();
116 // Delegate this operation to the shadow "V" or "H" box layout, and then down to any embedded layout.
117 me.fixHeightConstraints();
118 me.shadowLayout.onLayout();
119 if (me.embeddedContainer) {
120 me.embeddedContainer.layout.onLayout();
123 // If the panel was originally configured with collapsed: true, it will have
124 // been initialized with a "borderCollapse" flag: Collapse it now before the first layout.
125 if (!me.initialCollapsedComplete) {
126 Ext.iterate(me.regions, function(name, region){
127 if (region.borderCollapse) {
128 me.onBeforeRegionCollapse(region, region.collapseDirection, false, 0);
131 me.initialCollapsedComplete = true;
135 isValidParent : function(item, target, position) {
136 if (!this.borderLayoutInitialized) {
137 this.initializeBorderLayout();
140 // Delegate this operation to the shadow "V" or "H" box layout.
141 return this.shadowLayout.isValidParent(item, target, position);
144 beforeLayout: function() {
145 if (!this.borderLayoutInitialized) {
146 this.initializeBorderLayout();
149 // Delegate this operation to the shadow "V" or "H" box layout.
150 this.shadowLayout.beforeLayout();
153 renderItems: function(items, target) {
155 Ext.Error.raise('This should not be called');
159 renderItem: function(item) {
161 Ext.Error.raise('This should not be called');
166 * Gathers items for a layout operation. Injected into child Box layouts through configuration.
167 * We must not include child items which are floated over the layout (are primed with a slide out animation)
169 getVisibleItems: function() {
170 return Ext.ComponentQuery.query(':not([slideOutAnim])', this.callParent(arguments));
173 initializeBorderLayout: function() {
176 items = me.getLayoutItems(),
178 regions = (me.regions = {}),
185 // Map of Splitters for each region
189 for (; i < ln; i++) {
191 regions[comp.region] = comp;
193 // Intercept collapsing to implement showing an alternate Component as a collapsed placeholder
194 if (comp.region != 'center' && comp.collapsible && comp.collapseMode != 'header') {
196 // This layout intercepts any initial collapsed state. Panel must not do this itself.
197 comp.borderCollapse = comp.collapsed;
198 delete comp.collapsed;
201 beforecollapse: me.onBeforeRegionCollapse,
202 beforeexpand: me.onBeforeRegionExpand,
203 destroy: me.onRegionDestroy,
210 if (!regions.center) {
211 Ext.Error.raise("You must specify a center region when defining a BorderLayout.");
214 comp = regions.center;
219 comp.maintainFlex = true;
221 // Begin the VBox and HBox item list.
224 comp.collapseDirection = Ext.Component.DIRECTION_LEFT;
225 hBoxItems.push(comp);
227 hBoxItems.push(me.splitters.west = me.createSplitter(comp));
229 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
231 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
235 comp = regions.north;
237 comp.collapseDirection = Ext.Component.DIRECTION_TOP;
238 vBoxItems.push(comp);
240 vBoxItems.push(me.splitters.north = me.createSplitter(comp));
242 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
244 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
249 // Decide into which Collection the center region goes.
250 if (regions.north || regions.south) {
251 if (regions.east || regions.west) {
253 // Create the embedded center. Mark it with the region: 'center' property so that it can be identified as the center.
254 vBoxItems.push(me.embeddedContainer = Ext.create('Ext.container.Container', {
257 id: me.owner.id + '-embedded-center',
258 cls: Ext.baseCSSPrefix + 'border-item',
259 flex: regions.center.flex,
264 getVisibleItems: me.getVisibleItems
267 hBoxItems.push(regions.center);
269 // No east or west: the original center goes straight into the vbox
271 vBoxItems.push(regions.center);
274 // If we have no north or south, then the center is part of the HBox items
276 hBoxItems.push(regions.center);
279 // Finish off the VBox and HBox item list.
280 comp = regions.south;
282 comp.collapseDirection = Ext.Component.DIRECTION_BOTTOM;
284 vBoxItems.push(me.splitters.south = me.createSplitter(comp));
286 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
288 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
291 vBoxItems.push(comp);
295 comp.collapseDirection = Ext.Component.DIRECTION_RIGHT;
297 hBoxItems.push(me.splitters.east = me.createSplitter(comp));
299 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
301 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
304 hBoxItems.push(comp);
307 // Create the injected "items" collections for the Containers.
308 // If we have north or south, then the shadow Container will be a VBox.
309 // If there are also east or west regions, its center will be a shadow HBox.
310 // If there are *only* east or west regions, then the shadow layout will be an HBox (or Fit).
311 if (regions.north || regions.south) {
313 me.shadowContainer = Ext.create('Ext.container.Container', {
316 layout: Ext.applyIf({
319 getVisibleItems: me.getVisibleItems
322 me.createItems(me.shadowContainer, vBoxItems);
324 // Allow the Splitters to orientate themselves
325 if (me.splitters.north) {
326 me.splitters.north.ownerCt = me.shadowContainer;
328 if (me.splitters.south) {
329 me.splitters.south.ownerCt = me.shadowContainer;
332 // Inject items into the HBox Container if there is one - if there was an east or west.
333 if (me.embeddedContainer) {
334 me.embeddedContainer.ownerCt = me.shadowContainer;
335 me.createItems(me.embeddedContainer, hBoxItems);
337 // Allow the Splitters to orientate themselves
338 if (me.splitters.east) {
339 me.splitters.east.ownerCt = me.embeddedContainer;
341 if (me.splitters.west) {
342 me.splitters.west.ownerCt = me.embeddedContainer;
345 // These spliiters need to be constrained by components one-level below
346 // the component in their vobx. We update the min/maxHeight on the helper
347 // (embeddedContainer) prior to starting the split/drag. This has to be
348 // done on-the-fly to allow min/maxHeight of the E/C/W regions to be set
350 Ext.each([me.splitters.north, me.splitters.south], function (splitter) {
352 splitter.on('beforedragstart', me.fixHeightConstraints, me);
356 // The east or west region wanted a percentage
357 if (horizontalFlex) {
358 regions.center.flex -= horizontalFlex;
360 // The north or south region wanted a percentage
362 me.embeddedContainer.flex -= verticalFlex;
365 // The north or south region wanted a percentage
367 regions.center.flex -= verticalFlex;
371 // If we have no north or south, then there's only one Container, and it's
372 // an HBox, or, if only a center region was specified, a Fit.
374 me.shadowContainer = Ext.create('Ext.container.Container', {
377 layout: Ext.applyIf({
378 type: (hBoxItems.length == 1) ? 'fit' : 'hbox',
382 me.createItems(me.shadowContainer, hBoxItems);
384 // Allow the Splitters to orientate themselves
385 if (me.splitters.east) {
386 me.splitters.east.ownerCt = me.shadowContainer;
388 if (me.splitters.west) {
389 me.splitters.west.ownerCt = me.shadowContainer;
392 // The east or west region wanted a percentage
393 if (horizontalFlex) {
394 regions.center.flex -= verticalFlex;
398 // Create upward links from the region Components to their shadow ownerCts
399 for (i = 0, items = me.shadowContainer.items.items, ln = items.length; i < ln; i++) {
400 items[i].shadowOwnerCt = me.shadowContainer;
402 if (me.embeddedContainer) {
403 for (i = 0, items = me.embeddedContainer.items.items, ln = items.length; i < ln; i++) {
404 items[i].shadowOwnerCt = me.embeddedContainer;
408 // This is the layout that we delegate all operations to
409 me.shadowLayout = me.shadowContainer.getLayout();
411 me.borderLayoutInitialized = true;
414 setupState: function(comp){
415 var getState = comp.getState;
416 comp.getState = function(){
417 // call the original getState
418 var state = getState.call(comp) || {},
419 region = comp.region;
421 state.collapsed = !!comp.collapsed;
422 if (region == 'west' || region == 'east') {
423 state.width = comp.getWidth();
425 state.height = comp.getHeight();
429 comp.addStateEvents(['collapse', 'expand', 'resize']);
433 * Create the items collection for our shadow/embedded containers
436 createItems: function(container, items){
437 // Have to inject an items Collection *after* construction.
438 // The child items of the shadow layout must retain their original, user-defined ownerCt
439 delete container.items;
440 container.initItems();
441 container.items.addAll(items);
445 // Create a splitter for a child of the layout.
446 createSplitter: function(comp) {
448 interceptCollapse = (comp.collapseMode != 'header'),
451 resizer = Ext.create('Ext.resizer.Splitter', {
452 hidden: !!comp.hidden,
453 collapseTarget: comp,
454 performCollapse: !interceptCollapse,
455 listeners: interceptCollapse ? {
457 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
458 element: 'collapseEl'
463 // Mini collapse means that the splitter is the placeholder Component
464 if (comp.collapseMode == 'mini') {
465 comp.placeholder = resizer;
466 resizer.collapsedCls = comp.collapsedCls;
469 // Arrange to hide/show a region's associated splitter when the region is hidden/shown
471 hide: me.onRegionVisibilityChange,
472 show: me.onRegionVisibilityChange,
479 // Propagates the min/maxHeight values from the inner hbox items to its container.
480 fixHeightConstraints: function () {
482 ct = me.embeddedContainer,
483 maxHeight = 1e99, minHeight = -1;
489 ct.items.each(function (item) {
490 if (Ext.isNumber(item.maxHeight)) {
491 maxHeight = Math.max(maxHeight, item.maxHeight);
493 if (Ext.isNumber(item.minHeight)) {
494 minHeight = Math.max(minHeight, item.minHeight);
498 ct.maxHeight = maxHeight;
499 ct.minHeight = minHeight;
502 // Hide/show a region's associated splitter when the region is hidden/shown
503 onRegionVisibilityChange: function(comp){
504 this.splitters[comp.region][comp.hidden ? 'hide' : 'show']();
508 // Called when a splitter mini-collapse tool is clicked on.
509 // The listener is only added if this layout is controlling collapsing,
510 // not if the component's collapseMode is 'mini' or 'header'.
511 onSplitterCollapseClick: function(comp) {
512 if (comp.collapsed) {
513 this.onPlaceHolderToolClick(null, null, null, {client: comp});
520 * <p>Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the layout will collapse.
521 * By default, this will be a {@link Ext.panel.Header Header} component (Docked to the appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}.
522 * config to customize this.</p>
523 * <p><b>Note that this will be a fully instantiated Component, but will only be <i>rendered</i> when the Panel is first collapsed.</b></p>
524 * @param {Panel} panel The child Panel of the layout for which to return the {@link Ext.panel.Panel#placeholder placeholder}.
525 * @returns {Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link Ext.panel.Panel#collapseMode collapseMode} is
526 * <code>'header'</code>, in which case <i>undefined</i> is returned.
528 getPlaceholder: function(comp) {
530 placeholder = comp.placeholder,
531 shadowContainer = comp.shadowOwnerCt,
532 shadowLayout = shadowContainer.layout,
533 oppositeDirection = Ext.panel.Panel.prototype.getOppositeDirection(comp.collapseDirection),
534 horiz = (comp.region == 'north' || comp.region == 'south');
536 // No placeholder if the collapse mode is not the Border layout default
537 if (comp.collapseMode == 'header') {
541 // Provide a replacement Container with an expand tool
543 if (comp.collapseMode == 'mini') {
544 placeholder = Ext.create('Ext.resizer.Splitter', {
545 id: 'collapse-placeholder-' + comp.id,
546 collapseTarget: comp,
547 performCollapse: false,
550 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
551 element: 'collapseEl'
555 placeholder.addCls(placeholder.collapsedCls);
558 id: 'collapse-placeholder-' + comp.id,
559 margins: comp.initialConfig.margins || Ext.getClass(comp).prototype.margins,
561 orientation: horiz ? 'horizontal' : 'vertical',
563 textCls: comp.headerTextCls,
564 iconCls: comp.iconCls,
565 baseCls: comp.baseCls + '-header',
567 indicateDrag: comp.draggable,
568 cls: Ext.baseCSSPrefix + 'region-collapsed-placeholder ' + Ext.baseCSSPrefix + 'region-collapsed-' + comp.collapseDirection + '-placeholder ' + comp.collapsedCls,
569 listeners: comp.floatable ? {
572 me.floatCollapsedPanel(e, comp);
578 // Hack for IE6/7/IEQuirks's inability to display an inline-block
579 if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) && !horiz) {
580 placeholder.width = 25;
582 if (!comp.hideCollapseTool) {
583 placeholder[horiz ? 'tools' : 'items'] = [{
586 type: 'expand-' + oppositeDirection,
587 handler: me.onPlaceHolderToolClick,
592 placeholder = me.owner.createComponent(placeholder);
593 if (comp.isXType('panel')) {
595 titlechange: me.onRegionTitleChange,
596 iconchange: me.onRegionIconChange,
602 // The collapsed Component holds a reference to its placeholder and vice versa
603 comp.placeholder = placeholder;
604 placeholder.comp = comp;
611 * Update the placeholder title when panel title has been set or changed.
613 onRegionTitleChange: function(comp, newTitle) {
614 comp.placeholder.setTitle(newTitle);
619 * Update the placeholder iconCls when panel iconCls has been set or changed.
621 onRegionIconChange: function(comp, newIconCls) {
622 comp.placeholder.setIconCls(newIconCls);
627 * Calculates the size and positioning of the passed child item. Must be present because Panel's expand,
628 * when configured with a flex, calls this method on its ownerCt's layout.
629 * @param {Component} child The child Component to calculate the box for
630 * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
632 calculateChildBox: function(comp) {
634 if (me.shadowContainer.items.contains(comp)) {
635 return me.shadowContainer.layout.calculateChildBox(comp);
637 else if (me.embeddedContainer && me.embeddedContainer.items.contains(comp)) {
638 return me.embeddedContainer.layout.calculateChildBox(comp);
644 * Intercepts the Panel's own collapse event and perform's substitution of the Panel
645 * with a placeholder Header orientated in the appropriate dimension.
646 * @param comp The Panel being collapsed.
649 * @returns {Boolean} false to inhibit the Panel from performing its own collapse.
651 onBeforeRegionCollapse: function(comp, direction, animate) {
655 miniCollapse = comp.collapseMode == 'mini',
656 shadowContainer = comp.shadowOwnerCt,
657 shadowLayout = shadowContainer.layout,
658 placeholder = comp.placeholder,
659 sl = me.owner.suspendLayout,
660 scsl = shadowContainer.suspendLayout,
661 isNorthOrWest = (comp.region == 'north' || comp.region == 'west'); // Flag to keep the placeholder non-adjacent to any Splitter
663 // Do not trigger a layout during transition to collapsed Component
664 me.owner.suspendLayout = true;
665 shadowContainer.suspendLayout = true;
667 // Prevent upward notifications from downstream layouts
668 shadowLayout.layoutBusy = true;
669 if (shadowContainer.componentLayout) {
670 shadowContainer.componentLayout.layoutBusy = true;
672 me.shadowContainer.layout.layoutBusy = true;
673 me.layoutBusy = true;
674 me.owner.componentLayout.layoutBusy = true;
676 // Provide a replacement Container with an expand tool
678 placeholder = me.getPlaceholder(comp);
681 // placeholder already in place; show it.
682 if (placeholder.shadowOwnerCt === shadowContainer) {
685 // Insert the collapsed placeholder Component into the appropriate Box layout shadow Container
686 // It must go next to its client Component, but non-adjacent to the splitter so splitter can find its collapse client.
687 // Inject an ownerCt value pointing to the owner, border layout Container as the user will expect.
689 shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
690 placeholder.shadowOwnerCt = shadowContainer;
691 placeholder.ownerCt = me.owner;
694 // Flag the collapsing Component as hidden and show the placeholder.
695 // This causes the shadow Box layout's calculateChildBoxes to calculate the correct new arrangement.
696 // We hide or slideOut the Component's element
699 if (!placeholder.rendered) {
700 shadowLayout.renderItem(placeholder, shadowLayout.innerCt);
702 // The inserted placeholder does not have the proper size, so copy the width
703 // for N/S or the height for E/W from the component. This fixes EXTJSIV-1562
704 // without recursive layouts. This is only an issue initially. After this time,
705 // placeholder will have the correct width/height set by the layout (which has
706 // already happened when we get here initially).
707 if (comp.region == 'north' || comp.region == 'south') {
708 placeholder.setCalculatedSize(comp.getWidth());
710 placeholder.setCalculatedSize(undefined, comp.getHeight());
714 // Jobs to be done after the collapse has been done
715 function afterCollapse() {
716 // Reinstate automatic laying out.
717 me.owner.suspendLayout = sl;
718 shadowContainer.suspendLayout = scsl;
719 delete shadowLayout.layoutBusy;
720 if (shadowContainer.componentLayout) {
721 delete shadowContainer.componentLayout.layoutBusy;
723 delete me.shadowContainer.layout.layoutBusy;
724 delete me.layoutBusy;
725 delete me.owner.componentLayout.layoutBusy;
727 // Fire the collapse event: The Panel has in fact been collapsed, but by substitution of an alternative Component
728 comp.collapsed = true;
729 comp.fireEvent('collapse', comp);
733 * Set everything to the new positions. Note that we
734 * only want to animate the collapse if it wasn't configured
735 * initially with collapsed: true
737 if (comp.animCollapse && me.initialCollapsedComplete) {
738 shadowLayout.layout();
739 compEl.dom.style.zIndex = 100;
741 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
743 placeholder.el.hide();
745 compEl.slideOut(me.slideDirection[comp.region], {
746 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
748 afteranimate: function() {
749 compEl.show().setLeftTop(-10000, -10000);
750 compEl.dom.style.zIndex = '';
752 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
754 placeholder.el.slideIn(me.slideDirection[comp.region], {
764 compEl.setLeftTop(-10000, -10000);
765 shadowLayout.layout();
772 // Hijack the expand operation to remove the placeholder and slide the region back in.
773 onBeforeRegionExpand: function(comp, animate) {
774 this.onPlaceHolderToolClick(null, null, null, {client: comp});
778 // Called when the collapsed placeholder is clicked to reinstate a "collapsed" (in reality hidden) Panel.
779 onPlaceHolderToolClick: function(e, target, owner, tool) {
783 // Hide the placeholder unless it was the Component's preexisting splitter
784 hidePlaceholder = (comp.collapseMode != 'mini') || !comp.split,
787 placeholder = comp.placeholder,
788 placeholderEl = placeholder.el,
789 shadowContainer = comp.shadowOwnerCt,
790 shadowLayout = shadowContainer.layout,
792 sl = me.owner.suspendLayout,
793 scsl = shadowContainer.suspendLayout,
796 // If the slide in is still going, stop it.
797 // This will either leave the Component in its fully floated state (which is processed below)
798 // or in its collapsed state. Either way, we expand it..
799 if (comp.getActiveAnimation()) {
800 comp.stopAnimation();
803 // If the Component is fully floated when they click the placeholder Tool,
804 // it will be primed with a slide out animation object... so delete that
805 // and remove the mouseout listeners
806 if (comp.slideOutAnim) {
807 // Remove mouse leave monitors
808 compEl.un(comp.panelMouseMon);
809 placeholderEl.un(comp.placeholderMouseMon);
811 delete comp.slideOutAnim;
812 delete comp.panelMouseMon;
813 delete comp.placeholderMouseMon;
815 // If the Panel was floated and primed with a slideOut animation, we don't want to animate its layout operation.
819 // Do not trigger a layout during transition to expanded Component
820 me.owner.suspendLayout = true;
821 shadowContainer.suspendLayout = true;
823 // Prevent upward notifications from downstream layouts
824 shadowLayout.layoutBusy = true;
825 if (shadowContainer.componentLayout) {
826 shadowContainer.componentLayout.layoutBusy = true;
828 me.shadowContainer.layout.layoutBusy = true;
829 me.layoutBusy = true;
830 me.owner.componentLayout.layoutBusy = true;
832 // Unset the hidden and collapsed flags set in onBeforeRegionCollapse. The shadowLayout will now take it into account
833 // Find where the shadow Box layout plans to put the expanding Component.
835 comp.collapsed = false;
836 if (hidePlaceholder) {
837 placeholder.hidden = true;
839 toCompBox = shadowLayout.calculateChildBox(comp);
841 // Show the collapse tool in case it was hidden by the slide-in
842 if (comp.collapseTool) {
843 comp.collapseTool.show();
846 // If we're going to animate, we need to hide the component before moving it back into position
847 if (comp.animCollapse && !isFloating) {
848 compEl.setStyle('visibility', 'hidden');
850 compEl.setLeftTop(toCompBox.left, toCompBox.top);
852 // Equalize the size of the expanding Component prior to animation
853 // in case the layout area has changed size during the time it was collapsed.
854 curSize = comp.getSize();
855 if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
856 me.setItemSize(comp, toCompBox.width, toCompBox.height);
859 // Jobs to be done after the expand has been done
860 function afterExpand() {
861 // Reinstate automatic laying out.
862 me.owner.suspendLayout = sl;
863 shadowContainer.suspendLayout = scsl;
864 delete shadowLayout.layoutBusy;
865 if (shadowContainer.componentLayout) {
866 delete shadowContainer.componentLayout.layoutBusy;
868 delete me.shadowContainer.layout.layoutBusy;
869 delete me.layoutBusy;
870 delete me.owner.componentLayout.layoutBusy;
872 // In case it was floated out and they clicked the re-expand tool
873 comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
875 // Fire the expand event: The Panel has in fact been expanded, but by removal of an alternative Component
876 comp.fireEvent('expand', comp);
879 // Hide the placeholder
880 if (hidePlaceholder) {
881 placeholder.el.hide();
884 // Slide the expanding Component to its new position.
885 // When that is done, layout the layout.
886 if (comp.animCollapse && !isFloating) {
887 compEl.dom.style.zIndex = 100;
888 compEl.slideIn(me.slideDirection[comp.region], {
889 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
891 afteranimate: function() {
892 compEl.dom.style.zIndex = '';
894 shadowLayout.onLayout();
900 shadowLayout.onLayout();
905 floatCollapsedPanel: function(e, comp) {
907 if (comp.floatable === false) {
913 placeholder = comp.placeholder,
914 placeholderEl = placeholder.el,
915 shadowContainer = comp.shadowOwnerCt,
916 shadowLayout = shadowContainer.layout,
917 placeholderBox = shadowLayout.getChildBox(placeholder),
918 scsl = shadowContainer.suspendLayout,
919 curSize, toCompBox, compAnim;
921 // Ignore clicks on tools.
922 if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
926 // It's *being* animated, ignore the click.
927 // Possible future enhancement: Stop and *reverse* the current active Fx.
928 if (compEl.getActiveAnimation()) {
932 // If the Component is already fully floated when they click the placeholder,
933 // it will be primed with a slide out animation object... so slide it out.
934 if (comp.slideOutAnim) {
935 me.slideOutFloatedComponent(comp);
939 // Function to be called when the mouse leaves the floated Panel
940 // Slide out when the mouse leaves the region bounded by the slid Component and its placeholder.
941 function onMouseLeaveFloated(e) {
942 var slideRegion = compEl.getRegion().union(placeholderEl.getRegion()).adjust(1, -1, -1, 1);
944 // If mouse is not within slide Region, slide it out
945 if (!slideRegion.contains(e.getPoint())) {
946 me.slideOutFloatedComponent(comp);
950 // Monitor for mouseouting of the placeholder. Hide it if they exit for half a second or more
951 comp.placeholderMouseMon = placeholderEl.monitorMouseLeave(500, onMouseLeaveFloated);
953 // Do not trigger a layout during slide out of the Component
954 shadowContainer.suspendLayout = true;
956 // Prevent upward notifications from downstream layouts
957 me.layoutBusy = true;
958 me.owner.componentLayout.layoutBusy = true;
960 // The collapse tool is hidden while slid.
961 // It is re-shown on expand.
962 if (comp.collapseTool) {
963 comp.collapseTool.hide();
966 // Set flags so that the layout will calculate the boxes for what we want
968 comp.collapsed = false;
969 placeholder.hidden = true;
971 // Recalculate new arrangement of the Component being floated.
972 toCompBox = shadowLayout.calculateChildBox(comp);
973 placeholder.hidden = false;
975 // Component to appear just after the placeholder, whatever "after" means in the context of the shadow Box layout.
976 if (comp.region == 'north' || comp.region == 'west') {
977 toCompBox[shadowLayout.parallelBefore] += placeholderBox[shadowLayout.parallelPrefix] - 1;
979 toCompBox[shadowLayout.parallelBefore] -= (placeholderBox[shadowLayout.parallelPrefix] - 1);
981 compEl.setStyle('visibility', 'hidden');
982 compEl.setLeftTop(toCompBox.left, toCompBox.top);
984 // Equalize the size of the expanding Component prior to animation
985 // in case the layout area has changed size during the time it was collapsed.
986 curSize = comp.getSize();
987 if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
988 me.setItemSize(comp, toCompBox.width, toCompBox.height);
991 // This animation slides the collapsed Component's el out to just beyond its placeholder
994 afteranimate: function() {
995 shadowContainer.suspendLayout = scsl;
996 delete me.layoutBusy;
997 delete me.owner.componentLayout.layoutBusy;
999 // Prime the Component with an Anim config object to slide it back out
1000 compAnim.listeners = {
1001 afterAnimate: function() {
1002 compEl.show().removeCls(Ext.baseCSSPrefix + 'border-region-slide-in').setLeftTop(-10000, -10000);
1004 // Reinstate the correct, current state after slide out animation finishes
1006 comp.collapsed = true;
1007 delete comp.slideOutAnim;
1008 delete comp.panelMouseMon;
1009 delete comp.placeholderMouseMon;
1012 comp.slideOutAnim = compAnim;
1018 // Give the element the correct class which places it at a high z-index
1019 compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');
1021 // Begin the slide in
1022 compEl.slideIn(me.slideDirection[comp.region], compAnim);
1024 // Monitor for mouseouting of the slid area. Hide it if they exit for half a second or more
1025 comp.panelMouseMon = compEl.monitorMouseLeave(500, onMouseLeaveFloated);
1029 slideOutFloatedComponent: function(comp) {
1030 var compEl = comp.el,
1033 // Remove mouse leave monitors
1034 compEl.un(comp.panelMouseMon);
1035 comp.placeholder.el.un(comp.placeholderMouseMon);
1037 // Slide the Component out
1038 compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);
1040 delete comp.slideOutAnim;
1041 delete comp.panelMouseMon;
1042 delete comp.placeholderMouseMon;
1047 * Ensure any collapsed placeholder Component is destroyed along with its region.
1048 * Can't do this in onDestroy because they may remove a Component and use it elsewhere.
1050 onRegionDestroy: function(comp) {
1051 var placeholder = comp.placeholder;
1053 delete placeholder.ownerCt;
1054 placeholder.destroy();
1060 * Ensure any shadow Containers are destroyed.
1061 * Ensure we don't keep references to Components.
1063 onDestroy: function() {
1065 shadowContainer = me.shadowContainer,
1066 embeddedContainer = me.embeddedContainer;
1068 if (shadowContainer) {
1069 delete shadowContainer.ownerCt;
1070 Ext.destroy(shadowContainer);
1073 if (embeddedContainer) {
1074 delete embeddedContainer.ownerCt;
1075 Ext.destroy(embeddedContainer);
1078 delete me.splitters;
1079 delete me.shadowContainer;
1080 delete me.embeddedContainer;
1081 me.callParent(arguments);