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 * This is a multi-pane, application-oriented UI layout style that supports multiple nested panels, automatic bars
17 * between regions and built-in {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions.
19 * This class is intended to be extended or created via the `layout:'border'` {@link Ext.container.Container#layout}
20 * config, and should generally not need to be created directly via the new keyword.
23 * Ext.create('Ext.panel.Panel', {
26 * title: 'Border Layout',
29 * title: 'South Region is resizable',
30 * region: 'south', // position for region
33 * split: true, // enable resizing
36 * // xtype: 'panel' implied by default
37 * title: 'West Region is collapsible',
42 * collapsible: true, // make collapsible
43 * id: 'west-region-container',
46 * title: 'Center Region',
47 * region: 'center', // center region is required, no width/height specified
52 * renderTo: Ext.getBody()
57 * - Any Container using the Border layout **must** have a child item with `region:'center'`.
58 * The child item in the center region will always be resized to fill the remaining space
59 * not used by the other regions in the layout.
61 * - Any child items with a region of `west` or `east` may be configured with either an initial
62 * `width`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width
63 * **string** (Which is simply divided by 100 and used as a flex value).
64 * The 'center' region has a flex value of `1`.
66 * - Any child items with a region of `north` or `south` may be configured with either an initial
67 * `height`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height
68 * **string** (Which is simply divided by 100 and used as a flex value).
69 * The 'center' region has a flex value of `1`.
71 * - The regions of a BorderLayout are **fixed at render time** and thereafter, its child
72 * Components may not be removed or added**. To add/remove Components within a BorderLayout,
73 * have them wrapped by an additional Container which is directly managed by the BorderLayout.
74 * If the region is to be collapsible, the Container used directly by the BorderLayout manager
75 * should be a Panel. In the following example a Container (an Ext.panel.Panel) is added to
78 * wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
79 * wrc.{@link Ext.container.Container#removeAll removeAll}();
80 * wrc.{@link Ext.container.Container#add add}({
81 * title: 'Added Panel',
82 * html: 'Some content'
85 * - **There is no BorderLayout.Region class in ExtJS 4.0+**
87 Ext.define('Ext.layout.container.Border', {
89 alias: ['layout.border'],
90 extend: 'Ext.layout.container.Container',
91 requires: ['Ext.resizer.Splitter', 'Ext.container.Container', 'Ext.fx.Anim'],
92 alternateClassName: 'Ext.layout.BorderLayout',
94 targetCls: Ext.baseCSSPrefix + 'border-layout-ct',
96 itemCls: Ext.baseCSSPrefix + 'border-item',
98 bindToOwnerCtContainer: true,
100 percentageRe: /(\d+)%/,
109 constructor: function(config) {
110 this.initialConfig = config;
111 this.callParent(arguments);
114 onLayout: function() {
116 if (!me.borderLayoutInitialized) {
117 me.initializeBorderLayout();
120 // Delegate this operation to the shadow "V" or "H" box layout, and then down to any embedded layout.
121 me.fixHeightConstraints();
122 me.shadowLayout.onLayout();
123 if (me.embeddedContainer) {
124 me.embeddedContainer.layout.onLayout();
127 // If the panel was originally configured with collapsed: true, it will have
128 // been initialized with a "borderCollapse" flag: Collapse it now before the first layout.
129 if (!me.initialCollapsedComplete) {
130 Ext.iterate(me.regions, function(name, region){
131 if (region.borderCollapse) {
132 me.onBeforeRegionCollapse(region, region.collapseDirection, false, 0);
135 me.initialCollapsedComplete = true;
139 isValidParent : function(item, target, position) {
140 if (!this.borderLayoutInitialized) {
141 this.initializeBorderLayout();
144 // Delegate this operation to the shadow "V" or "H" box layout.
145 return this.shadowLayout.isValidParent(item, target, position);
148 beforeLayout: function() {
149 if (!this.borderLayoutInitialized) {
150 this.initializeBorderLayout();
153 // Delegate this operation to the shadow "V" or "H" box layout.
154 this.shadowLayout.beforeLayout();
156 // note: don't call base because that does a renderItems again
159 renderItems: function(items, target) {
161 Ext.Error.raise('This should not be called');
165 renderItem: function(item) {
167 Ext.Error.raise('This should not be called');
171 renderChildren: function() {
172 if (!this.borderLayoutInitialized) {
173 this.initializeBorderLayout();
176 this.shadowLayout.renderChildren();
180 * Gathers items for a layout operation. Injected into child Box layouts through configuration.
181 * We must not include child items which are floated over the layout (are primed with a slide out animation)
183 getVisibleItems: function() {
184 return Ext.ComponentQuery.query(':not([slideOutAnim])', this.callParent(arguments));
187 initializeBorderLayout: function() {
190 items = me.getLayoutItems(),
192 regions = (me.regions = {}),
199 // Map of Splitters for each region
203 for (; i < ln; i++) {
205 regions[comp.region] = comp;
207 // Intercept collapsing to implement showing an alternate Component as a collapsed placeholder
208 if (comp.region != 'center' && comp.collapsible && comp.collapseMode != 'header') {
210 // This layout intercepts any initial collapsed state. Panel must not do this itself.
211 comp.borderCollapse = comp.collapsed;
212 comp.collapsed = false;
215 beforecollapse: me.onBeforeRegionCollapse,
216 beforeexpand: me.onBeforeRegionExpand,
217 destroy: me.onRegionDestroy,
224 if (!regions.center) {
225 Ext.Error.raise("You must specify a center region when defining a BorderLayout.");
228 comp = regions.center;
233 comp.maintainFlex = true;
235 // Begin the VBox and HBox item list.
238 comp.collapseDirection = Ext.Component.DIRECTION_LEFT;
239 hBoxItems.push(comp);
241 hBoxItems.push(me.splitters.west = me.createSplitter(comp));
243 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
245 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
249 comp = regions.north;
251 comp.collapseDirection = Ext.Component.DIRECTION_TOP;
252 vBoxItems.push(comp);
254 vBoxItems.push(me.splitters.north = me.createSplitter(comp));
256 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
258 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
263 // Decide into which Collection the center region goes.
264 if (regions.north || regions.south) {
265 if (regions.east || regions.west) {
267 // Create the embedded center. Mark it with the region: 'center' property so that it can be identified as the center.
268 vBoxItems.push(me.embeddedContainer = Ext.create('Ext.container.Container', {
271 id: me.owner.id + '-embedded-center',
272 cls: Ext.baseCSSPrefix + 'border-item',
273 flex: regions.center.flex,
278 getVisibleItems: me.getVisibleItems
281 hBoxItems.push(regions.center);
283 // No east or west: the original center goes straight into the vbox
285 vBoxItems.push(regions.center);
288 // If we have no north or south, then the center is part of the HBox items
290 hBoxItems.push(regions.center);
293 // Finish off the VBox and HBox item list.
294 comp = regions.south;
296 comp.collapseDirection = Ext.Component.DIRECTION_BOTTOM;
298 vBoxItems.push(me.splitters.south = me.createSplitter(comp));
300 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
302 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
305 vBoxItems.push(comp);
309 comp.collapseDirection = Ext.Component.DIRECTION_RIGHT;
311 hBoxItems.push(me.splitters.east = me.createSplitter(comp));
313 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
315 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
318 hBoxItems.push(comp);
321 // Create the injected "items" collections for the Containers.
322 // If we have north or south, then the shadow Container will be a VBox.
323 // If there are also east or west regions, its center will be a shadow HBox.
324 // If there are *only* east or west regions, then the shadow layout will be an HBox (or Fit).
325 if (regions.north || regions.south) {
327 me.shadowContainer = Ext.create('Ext.container.Container', {
330 layout: Ext.applyIf({
333 getVisibleItems: me.getVisibleItems
336 me.createItems(me.shadowContainer, vBoxItems);
338 // Allow the Splitters to orientate themselves
339 if (me.splitters.north) {
340 me.splitters.north.ownerCt = me.shadowContainer;
342 if (me.splitters.south) {
343 me.splitters.south.ownerCt = me.shadowContainer;
346 // Inject items into the HBox Container if there is one - if there was an east or west.
347 if (me.embeddedContainer) {
348 me.embeddedContainer.ownerCt = me.shadowContainer;
349 me.createItems(me.embeddedContainer, hBoxItems);
351 // Allow the Splitters to orientate themselves
352 if (me.splitters.east) {
353 me.splitters.east.ownerCt = me.embeddedContainer;
355 if (me.splitters.west) {
356 me.splitters.west.ownerCt = me.embeddedContainer;
359 // These spliiters need to be constrained by components one-level below
360 // the component in their vobx. We update the min/maxHeight on the helper
361 // (embeddedContainer) prior to starting the split/drag. This has to be
362 // done on-the-fly to allow min/maxHeight of the E/C/W regions to be set
364 Ext.each([me.splitters.north, me.splitters.south], function (splitter) {
366 splitter.on('beforedragstart', me.fixHeightConstraints, me);
370 // The east or west region wanted a percentage
371 if (horizontalFlex) {
372 regions.center.flex -= horizontalFlex;
374 // The north or south region wanted a percentage
376 me.embeddedContainer.flex -= verticalFlex;
379 // The north or south region wanted a percentage
381 regions.center.flex -= verticalFlex;
385 // If we have no north or south, then there's only one Container, and it's
386 // an HBox, or, if only a center region was specified, a Fit.
388 me.shadowContainer = Ext.create('Ext.container.Container', {
391 layout: Ext.applyIf({
392 type: (hBoxItems.length == 1) ? 'fit' : 'hbox',
396 me.createItems(me.shadowContainer, hBoxItems);
398 // Allow the Splitters to orientate themselves
399 if (me.splitters.east) {
400 me.splitters.east.ownerCt = me.shadowContainer;
402 if (me.splitters.west) {
403 me.splitters.west.ownerCt = me.shadowContainer;
406 // The east or west region wanted a percentage
407 if (horizontalFlex) {
408 regions.center.flex -= verticalFlex;
412 // Create upward links from the region Components to their shadow ownerCts
413 for (i = 0, items = me.shadowContainer.items.items, ln = items.length; i < ln; i++) {
414 items[i].shadowOwnerCt = me.shadowContainer;
416 if (me.embeddedContainer) {
417 for (i = 0, items = me.embeddedContainer.items.items, ln = items.length; i < ln; i++) {
418 items[i].shadowOwnerCt = me.embeddedContainer;
422 // This is the layout that we delegate all operations to
423 me.shadowLayout = me.shadowContainer.getLayout();
425 me.borderLayoutInitialized = true;
428 setupState: function(comp){
429 var getState = comp.getState;
430 comp.getState = function(){
431 // call the original getState
432 var state = getState.call(comp) || {},
433 region = comp.region;
435 state.collapsed = !!comp.collapsed;
436 if (region == 'west' || region == 'east') {
437 state.width = comp.getWidth();
439 state.height = comp.getHeight();
443 comp.addStateEvents(['collapse', 'expand', 'resize']);
447 * Create the items collection for our shadow/embedded containers
450 createItems: function(container, items){
451 // Have to inject an items Collection *after* construction.
452 // The child items of the shadow layout must retain their original, user-defined ownerCt
453 delete container.items;
454 container.initItems();
455 container.items.addAll(items);
459 // Create a splitter for a child of the layout.
460 createSplitter: function(comp) {
462 interceptCollapse = (comp.collapseMode != 'header'),
465 resizer = Ext.create('Ext.resizer.Splitter', {
466 hidden: !!comp.hidden,
467 collapseTarget: comp,
468 performCollapse: !interceptCollapse,
469 listeners: interceptCollapse ? {
471 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
472 element: 'collapseEl'
477 // Mini collapse means that the splitter is the placeholder Component
478 if (comp.collapseMode == 'mini') {
479 comp.placeholder = resizer;
480 resizer.collapsedCls = comp.collapsedCls;
483 // Arrange to hide/show a region's associated splitter when the region is hidden/shown
485 hide: me.onRegionVisibilityChange,
486 show: me.onRegionVisibilityChange,
493 // Propagates the min/maxHeight values from the inner hbox items to its container.
494 fixHeightConstraints: function () {
496 ct = me.embeddedContainer,
497 maxHeight = 1e99, minHeight = -1;
503 ct.items.each(function (item) {
504 if (Ext.isNumber(item.maxHeight)) {
505 maxHeight = Math.max(maxHeight, item.maxHeight);
507 if (Ext.isNumber(item.minHeight)) {
508 minHeight = Math.max(minHeight, item.minHeight);
512 ct.maxHeight = maxHeight;
513 ct.minHeight = minHeight;
516 // Hide/show a region's associated splitter when the region is hidden/shown
517 onRegionVisibilityChange: function(comp){
518 this.splitters[comp.region][comp.hidden ? 'hide' : 'show']();
522 // Called when a splitter mini-collapse tool is clicked on.
523 // The listener is only added if this layout is controlling collapsing,
524 // not if the component's collapseMode is 'mini' or 'header'.
525 onSplitterCollapseClick: function(comp) {
526 if (comp.collapsed) {
527 this.onPlaceHolderToolClick(null, null, null, {client: comp});
534 * Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the
535 * layout will collapse. By default, this will be a {@link Ext.panel.Header Header} component (Docked to the
536 * appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}. config to customize this.
538 * **Note that this will be a fully instantiated Component, but will only be _rendered_ when the Panel is first
540 * @param {Ext.panel.Panel} panel The child Panel of the layout for which to return the {@link
541 * Ext.panel.Panel#placeholder placeholder}.
542 * @return {Ext.Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link
543 * Ext.panel.Panel#collapseMode collapseMode} is `'header'`, in which case _undefined_ is returned.
545 getPlaceholder: function(comp) {
547 placeholder = comp.placeholder,
548 shadowContainer = comp.shadowOwnerCt,
549 shadowLayout = shadowContainer.layout,
550 oppositeDirection = Ext.panel.Panel.prototype.getOppositeDirection(comp.collapseDirection),
551 horiz = (comp.region == 'north' || comp.region == 'south');
553 // No placeholder if the collapse mode is not the Border layout default
554 if (comp.collapseMode == 'header') {
558 // Provide a replacement Container with an expand tool
560 if (comp.collapseMode == 'mini') {
561 placeholder = Ext.create('Ext.resizer.Splitter', {
562 id: 'collapse-placeholder-' + comp.id,
563 collapseTarget: comp,
564 performCollapse: false,
567 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
568 element: 'collapseEl'
572 placeholder.addCls(placeholder.collapsedCls);
575 id: 'collapse-placeholder-' + comp.id,
576 margins: comp.initialConfig.margins || Ext.getClass(comp).prototype.margins,
578 orientation: horiz ? 'horizontal' : 'vertical',
580 textCls: comp.headerTextCls,
581 iconCls: comp.iconCls,
582 baseCls: comp.baseCls + '-header',
584 indicateDrag: comp.draggable,
585 cls: Ext.baseCSSPrefix + 'region-collapsed-placeholder ' + Ext.baseCSSPrefix + 'region-collapsed-' + comp.collapseDirection + '-placeholder ' + comp.collapsedCls,
586 listeners: comp.floatable ? {
589 me.floatCollapsedPanel(e, comp);
595 // Hack for IE6/7/IEQuirks's inability to display an inline-block
596 if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) && !horiz) {
597 placeholder.width = 25;
599 if (!comp.hideCollapseTool) {
600 placeholder[horiz ? 'tools' : 'items'] = [{
603 type: 'expand-' + oppositeDirection,
604 handler: me.onPlaceHolderToolClick,
609 placeholder = me.owner.createComponent(placeholder);
610 if (comp.isXType('panel')) {
612 titlechange: me.onRegionTitleChange,
613 iconchange: me.onRegionIconChange,
619 // The collapsed Component holds a reference to its placeholder and vice versa
620 comp.placeholder = placeholder;
621 placeholder.comp = comp;
628 * Update the placeholder title when panel title has been set or changed.
630 onRegionTitleChange: function(comp, newTitle) {
631 comp.placeholder.setTitle(newTitle);
636 * Update the placeholder iconCls when panel iconCls has been set or changed.
638 onRegionIconChange: function(comp, newIconCls) {
639 comp.placeholder.setIconCls(newIconCls);
644 * Calculates the size and positioning of the passed child item. Must be present because Panel's expand,
645 * when configured with a flex, calls this method on its ownerCt's layout.
646 * @param {Ext.Component} child The child Component to calculate the box for
647 * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
649 calculateChildBox: function(comp) {
651 if (me.shadowContainer.items.contains(comp)) {
652 return me.shadowContainer.layout.calculateChildBox(comp);
654 else if (me.embeddedContainer && me.embeddedContainer.items.contains(comp)) {
655 return me.embeddedContainer.layout.calculateChildBox(comp);
661 * Intercepts the Panel's own collapse event and perform's substitution of the Panel
662 * with a placeholder Header orientated in the appropriate dimension.
663 * @param comp The Panel being collapsed.
666 * @returns {Boolean} false to inhibit the Panel from performing its own collapse.
668 onBeforeRegionCollapse: function(comp, direction, animate) {
669 if (comp.collapsedChangingLayout) {
671 if (Ext.global.console && Ext.global.console.warn) {
672 Ext.global.console.warn(Ext.getDisplayName(arguments.callee), 'aborted because the collapsed state is in the middle of changing');
677 comp.collapsedChangingLayout = true;
681 miniCollapse = comp.collapseMode == 'mini',
682 shadowContainer = comp.shadowOwnerCt,
683 shadowLayout = shadowContainer.layout,
684 placeholder = comp.placeholder,
685 sl = me.owner.suspendLayout,
686 scsl = shadowContainer.suspendLayout,
687 isNorthOrWest = (comp.region == 'north' || comp.region == 'west'); // Flag to keep the placeholder non-adjacent to any Splitter
689 // Do not trigger a layout during transition to collapsed Component
690 me.owner.suspendLayout = true;
691 shadowContainer.suspendLayout = true;
693 // Prevent upward notifications from downstream layouts
694 shadowLayout.layoutBusy = true;
695 if (shadowContainer.componentLayout) {
696 shadowContainer.componentLayout.layoutBusy = true;
698 me.shadowContainer.layout.layoutBusy = true;
699 me.layoutBusy = true;
700 me.owner.componentLayout.layoutBusy = true;
702 // Provide a replacement Container with an expand tool
704 placeholder = me.getPlaceholder(comp);
707 // placeholder already in place; show it.
708 if (placeholder.shadowOwnerCt === shadowContainer) {
711 // Insert the collapsed placeholder Component into the appropriate Box layout shadow Container
712 // It must go next to its client Component, but non-adjacent to the splitter so splitter can find its collapse client.
713 // Inject an ownerCt value pointing to the owner, border layout Container as the user will expect.
715 shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
716 placeholder.shadowOwnerCt = shadowContainer;
717 placeholder.ownerCt = me.owner;
720 // Flag the collapsing Component as hidden and show the placeholder.
721 // This causes the shadow Box layout's calculateChildBoxes to calculate the correct new arrangement.
722 // We hide or slideOut the Component's element
725 if (!placeholder.rendered) {
726 shadowLayout.renderItem(placeholder, shadowLayout.innerCt);
728 // The inserted placeholder does not have the proper size, so copy the width
729 // for N/S or the height for E/W from the component. This fixes EXTJSIV-1562
730 // without recursive layouts. This is only an issue initially. After this time,
731 // placeholder will have the correct width/height set by the layout (which has
732 // already happened when we get here initially).
733 if (comp.region == 'north' || comp.region == 'south') {
734 placeholder.setCalculatedSize(comp.getWidth());
736 placeholder.setCalculatedSize(undefined, comp.getHeight());
740 // Jobs to be done after the collapse has been done
741 function afterCollapse() {
742 // Reinstate automatic laying out.
743 me.owner.suspendLayout = sl;
744 shadowContainer.suspendLayout = scsl;
745 delete shadowLayout.layoutBusy;
746 if (shadowContainer.componentLayout) {
747 delete shadowContainer.componentLayout.layoutBusy;
749 delete me.shadowContainer.layout.layoutBusy;
750 delete me.layoutBusy;
751 delete me.owner.componentLayout.layoutBusy;
752 delete comp.collapsedChangingLayout;
754 // Fire the collapse event: The Panel has in fact been collapsed, but by substitution of an alternative Component
755 comp.collapsed = true;
756 comp.fireEvent('collapse', comp);
760 * Set everything to the new positions. Note that we
761 * only want to animate the collapse if it wasn't configured
762 * initially with collapsed: true
764 if (comp.animCollapse && me.initialCollapsedComplete) {
765 shadowLayout.layout();
766 compEl.dom.style.zIndex = 100;
768 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
770 placeholder.el.hide();
772 compEl.slideOut(me.slideDirection[comp.region], {
773 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
775 afteranimate: function() {
776 compEl.show().setLeftTop(-10000, -10000);
777 compEl.dom.style.zIndex = '';
779 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
781 placeholder.el.slideIn(me.slideDirection[comp.region], {
791 compEl.setLeftTop(-10000, -10000);
792 shadowLayout.layout();
799 // Hijack the expand operation to remove the placeholder and slide the region back in.
800 onBeforeRegionExpand: function(comp, animate) {
801 // We don't check for comp.collapsedChangingLayout here because onPlaceHolderToolClick does it
802 this.onPlaceHolderToolClick(null, null, null, {client: comp, shouldFireBeforeexpand: false});
806 // Called when the collapsed placeholder is clicked to reinstate a "collapsed" (in reality hidden) Panel.
807 onPlaceHolderToolClick: function(e, target, owner, tool) {
811 // Hide the placeholder unless it was the Component's preexisting splitter
812 hidePlaceholder = (comp.collapseMode != 'mini') || !comp.split,
815 placeholder = comp.placeholder,
816 placeholderEl = placeholder.el,
817 shadowContainer = comp.shadowOwnerCt,
818 shadowLayout = shadowContainer.layout,
820 sl = me.owner.suspendLayout,
821 scsl = shadowContainer.suspendLayout,
824 if (comp.collapsedChangingLayout) {
826 if (Ext.global.console && Ext.global.console.warn) {
827 Ext.global.console.warn(Ext.getDisplayName(arguments.callee), 'aborted because the collapsed state is in the middle of changing');
832 if (tool.shouldFireBeforeexpand !== false && comp.fireEvent('beforeexpand', comp, true) === false) {
835 comp.collapsedChangingLayout = true;
836 // If the slide in is still going, stop it.
837 // This will either leave the Component in its fully floated state (which is processed below)
838 // or in its collapsed state. Either way, we expand it..
839 if (comp.getActiveAnimation()) {
840 comp.stopAnimation();
843 // If the Component is fully floated when they click the placeholder Tool,
844 // it will be primed with a slide out animation object... so delete that
845 // and remove the mouseout listeners
846 if (comp.slideOutAnim) {
847 // Remove mouse leave monitors
848 compEl.un(comp.panelMouseMon);
849 placeholderEl.un(comp.placeholderMouseMon);
851 delete comp.slideOutAnim;
852 delete comp.panelMouseMon;
853 delete comp.placeholderMouseMon;
855 // If the Panel was floated and primed with a slideOut animation, we don't want to animate its layout operation.
859 // Do not trigger a layout during transition to expanded Component
860 me.owner.suspendLayout = true;
861 shadowContainer.suspendLayout = true;
863 // Prevent upward notifications from downstream layouts
864 shadowLayout.layoutBusy = true;
865 if (shadowContainer.componentLayout) {
866 shadowContainer.componentLayout.layoutBusy = true;
868 me.shadowContainer.layout.layoutBusy = true;
869 me.layoutBusy = true;
870 me.owner.componentLayout.layoutBusy = true;
872 // Unset the hidden and collapsed flags set in onBeforeRegionCollapse. The shadowLayout will now take it into account
873 // Find where the shadow Box layout plans to put the expanding Component.
875 comp.collapsed = false;
876 if (hidePlaceholder) {
877 placeholder.hidden = true;
879 toCompBox = shadowLayout.calculateChildBox(comp);
881 // Show the collapse tool in case it was hidden by the slide-in
882 if (comp.collapseTool) {
883 comp.collapseTool.show();
886 // If we're going to animate, we need to hide the component before moving it back into position
887 if (comp.animCollapse && !isFloating) {
888 compEl.setStyle('visibility', 'hidden');
890 compEl.setLeftTop(toCompBox.left, toCompBox.top);
892 // Equalize the size of the expanding Component prior to animation
893 // in case the layout area has changed size during the time it was collapsed.
894 curSize = comp.getSize();
895 if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
896 me.setItemSize(comp, toCompBox.width, toCompBox.height);
899 // Jobs to be done after the expand has been done
900 function afterExpand() {
901 // Reinstate automatic laying out.
902 me.owner.suspendLayout = sl;
903 shadowContainer.suspendLayout = scsl;
904 delete shadowLayout.layoutBusy;
905 if (shadowContainer.componentLayout) {
906 delete shadowContainer.componentLayout.layoutBusy;
908 delete me.shadowContainer.layout.layoutBusy;
909 delete me.layoutBusy;
910 delete me.owner.componentLayout.layoutBusy;
911 delete comp.collapsedChangingLayout;
913 // In case it was floated out and they clicked the re-expand tool
914 comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
916 // Fire the expand event: The Panel has in fact been expanded, but by removal of an alternative Component
917 comp.fireEvent('expand', comp);
920 // Hide the placeholder
921 if (hidePlaceholder) {
922 placeholder.el.hide();
925 // Slide the expanding Component to its new position.
926 // When that is done, layout the layout.
927 if (comp.animCollapse && !isFloating) {
928 compEl.dom.style.zIndex = 100;
929 compEl.slideIn(me.slideDirection[comp.region], {
930 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
932 afteranimate: function() {
933 compEl.dom.style.zIndex = '';
935 shadowLayout.onLayout();
941 shadowLayout.onLayout();
946 floatCollapsedPanel: function(e, comp) {
948 if (comp.floatable === false) {
954 placeholder = comp.placeholder,
955 placeholderEl = placeholder.el,
956 shadowContainer = comp.shadowOwnerCt,
957 shadowLayout = shadowContainer.layout,
958 placeholderBox = shadowLayout.getChildBox(placeholder),
959 scsl = shadowContainer.suspendLayout,
960 curSize, toCompBox, compAnim;
962 // Ignore clicks on tools.
963 if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
967 // It's *being* animated, ignore the click.
968 // Possible future enhancement: Stop and *reverse* the current active Fx.
969 if (compEl.getActiveAnimation()) {
973 // If the Component is already fully floated when they click the placeholder,
974 // it will be primed with a slide out animation object... so slide it out.
975 if (comp.slideOutAnim) {
976 me.slideOutFloatedComponent(comp);
980 // Function to be called when the mouse leaves the floated Panel
981 // Slide out when the mouse leaves the region bounded by the slid Component and its placeholder.
982 function onMouseLeaveFloated(e) {
983 var slideRegion = compEl.getRegion().union(placeholderEl.getRegion()).adjust(1, -1, -1, 1);
985 // If mouse is not within slide Region, slide it out
986 if (!slideRegion.contains(e.getPoint())) {
987 me.slideOutFloatedComponent(comp);
991 // Monitor for mouseouting of the placeholder. Hide it if they exit for half a second or more
992 comp.placeholderMouseMon = placeholderEl.monitorMouseLeave(500, onMouseLeaveFloated);
994 // Do not trigger a layout during slide out of the Component
995 shadowContainer.suspendLayout = true;
997 // Prevent upward notifications from downstream layouts
998 me.layoutBusy = true;
999 me.owner.componentLayout.layoutBusy = true;
1001 // The collapse tool is hidden while slid.
1002 // It is re-shown on expand.
1003 if (comp.collapseTool) {
1004 comp.collapseTool.hide();
1007 // Set flags so that the layout will calculate the boxes for what we want
1008 comp.hidden = false;
1009 comp.collapsed = false;
1010 placeholder.hidden = true;
1012 // Recalculate new arrangement of the Component being floated.
1013 toCompBox = shadowLayout.calculateChildBox(comp);
1014 placeholder.hidden = false;
1016 // Component to appear just after the placeholder, whatever "after" means in the context of the shadow Box layout.
1017 if (comp.region == 'north' || comp.region == 'west') {
1018 toCompBox[shadowLayout.parallelBefore] += placeholderBox[shadowLayout.parallelPrefix] - 1;
1020 toCompBox[shadowLayout.parallelBefore] -= (placeholderBox[shadowLayout.parallelPrefix] - 1);
1022 compEl.setStyle('visibility', 'hidden');
1023 compEl.setLeftTop(toCompBox.left, toCompBox.top);
1025 // Equalize the size of the expanding Component prior to animation
1026 // in case the layout area has changed size during the time it was collapsed.
1027 curSize = comp.getSize();
1028 if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
1029 me.setItemSize(comp, toCompBox.width, toCompBox.height);
1032 // This animation slides the collapsed Component's el out to just beyond its placeholder
1035 afteranimate: function() {
1036 shadowContainer.suspendLayout = scsl;
1037 delete me.layoutBusy;
1038 delete me.owner.componentLayout.layoutBusy;
1040 // Prime the Component with an Anim config object to slide it back out
1041 compAnim.listeners = {
1042 afterAnimate: function() {
1043 compEl.show().removeCls(Ext.baseCSSPrefix + 'border-region-slide-in').setLeftTop(-10000, -10000);
1045 // Reinstate the correct, current state after slide out animation finishes
1047 comp.collapsed = true;
1048 delete comp.slideOutAnim;
1049 delete comp.panelMouseMon;
1050 delete comp.placeholderMouseMon;
1053 comp.slideOutAnim = compAnim;
1059 // Give the element the correct class which places it at a high z-index
1060 compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');
1062 // Begin the slide in
1063 compEl.slideIn(me.slideDirection[comp.region], compAnim);
1065 // Monitor for mouseouting of the slid area. Hide it if they exit for half a second or more
1066 comp.panelMouseMon = compEl.monitorMouseLeave(500, onMouseLeaveFloated);
1070 slideOutFloatedComponent: function(comp) {
1071 var compEl = comp.el,
1074 // Remove mouse leave monitors
1075 compEl.un(comp.panelMouseMon);
1076 comp.placeholder.el.un(comp.placeholderMouseMon);
1078 // Slide the Component out
1079 compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);
1081 delete comp.slideOutAnim;
1082 delete comp.panelMouseMon;
1083 delete comp.placeholderMouseMon;
1088 * Ensure any collapsed placeholder Component is destroyed along with its region.
1089 * Can't do this in onDestroy because they may remove a Component and use it elsewhere.
1091 onRegionDestroy: function(comp) {
1092 var placeholder = comp.placeholder;
1094 delete placeholder.ownerCt;
1095 placeholder.destroy();
1101 * Ensure any shadow Containers are destroyed.
1102 * Ensure we don't keep references to Components.
1104 onDestroy: function() {
1106 shadowContainer = me.shadowContainer,
1107 embeddedContainer = me.embeddedContainer;
1109 if (shadowContainer) {
1110 delete shadowContainer.ownerCt;
1111 Ext.destroy(shadowContainer);
1114 if (embeddedContainer) {
1115 delete embeddedContainer.ownerCt;
1116 Ext.destroy(embeddedContainer);
1119 delete me.splitters;
1120 delete me.shadowContainer;
1121 delete me.embeddedContainer;
1122 me.callParent(arguments);