/ext-4.0.7/docs/source/Border.html
HTML | 1130 lines | 1004 code | 126 blank | 0 comment | 0 complexity | 5d4366a9c99954a646446b4ac23f7480 MD5 | raw file
1<!DOCTYPE html>
2<html>
3<head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
10 </style>
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14 }
15 </script>
16</head>
17<body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-layout-container-Border'>/**
19</span> * This is a multi-pane, application-oriented UI layout style that supports multiple nested panels, automatic bars
20 * between regions and built-in {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions.
21 *
22 * This class is intended to be extended or created via the `layout:'border'` {@link Ext.container.Container#layout}
23 * config, and should generally not need to be created directly via the new keyword.
24 *
25 * @example
26 * Ext.create('Ext.panel.Panel', {
27 * width: 500,
28 * height: 400,
29 * title: 'Border Layout',
30 * layout: 'border',
31 * items: [{
32 * title: 'South Region is resizable',
33 * region: 'south', // position for region
34 * xtype: 'panel',
35 * height: 100,
36 * split: true, // enable resizing
37 * margins: '0 5 5 5'
38 * },{
39 * // xtype: 'panel' implied by default
40 * title: 'West Region is collapsible',
41 * region:'west',
42 * xtype: 'panel',
43 * margins: '5 0 0 5',
44 * width: 200,
45 * collapsible: true, // make collapsible
46 * id: 'west-region-container',
47 * layout: 'fit'
48 * },{
49 * title: 'Center Region',
50 * region: 'center', // center region is required, no width/height specified
51 * xtype: 'panel',
52 * layout: 'fit',
53 * margins: '5 5 0 0'
54 * }],
55 * renderTo: Ext.getBody()
56 * });
57 *
58 * # Notes
59 *
60 * - Any Container using the Border layout **must** have a child item with `region:'center'`.
61 * The child item in the center region will always be resized to fill the remaining space
62 * not used by the other regions in the layout.
63 *
64 * - Any child items with a region of `west` or `east` may be configured with either an initial
65 * `width`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width
66 * **string** (Which is simply divided by 100 and used as a flex value).
67 * The 'center' region has a flex value of `1`.
68 *
69 * - Any child items with a region of `north` or `south` may be configured with either an initial
70 * `height`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height
71 * **string** (Which is simply divided by 100 and used as a flex value).
72 * The 'center' region has a flex value of `1`.
73 *
74 * - The regions of a BorderLayout are **fixed at render time** and thereafter, its child
75 * Components may not be removed or added**. To add/remove Components within a BorderLayout,
76 * have them wrapped by an additional Container which is directly managed by the BorderLayout.
77 * If the region is to be collapsible, the Container used directly by the BorderLayout manager
78 * should be a Panel. In the following example a Container (an Ext.panel.Panel) is added to
79 * the west region:
80 *
81 * wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
82 * wrc.{@link Ext.container.Container#removeAll removeAll}();
83 * wrc.{@link Ext.container.Container#add add}({
84 * title: 'Added Panel',
85 * html: 'Some content'
86 * });
87 *
88 * - **There is no BorderLayout.Region class in ExtJS 4.0+**
89 */
90Ext.define('Ext.layout.container.Border', {
91
92 alias: ['layout.border'],
93 extend: 'Ext.layout.container.Container',
94 requires: ['Ext.resizer.Splitter', 'Ext.container.Container', 'Ext.fx.Anim'],
95 alternateClassName: 'Ext.layout.BorderLayout',
96
97 targetCls: Ext.baseCSSPrefix + 'border-layout-ct',
98
99 itemCls: Ext.baseCSSPrefix + 'border-item',
100
101 bindToOwnerCtContainer: true,
102
103 percentageRe: /(\d+)%/,
104
105 slideDirection: {
106 north: 't',
107 south: 'b',
108 west: 'l',
109 east: 'r'
110 },
111
112 constructor: function(config) {
113 this.initialConfig = config;
114 this.callParent(arguments);
115 },
116
117 onLayout: function() {
118 var me = this;
119 if (!me.borderLayoutInitialized) {
120 me.initializeBorderLayout();
121 }
122
123 // Delegate this operation to the shadow "V" or "H" box layout, and then down to any embedded layout.
124 me.fixHeightConstraints();
125 me.shadowLayout.onLayout();
126 if (me.embeddedContainer) {
127 me.embeddedContainer.layout.onLayout();
128 }
129
130 // If the panel was originally configured with collapsed: true, it will have
131 // been initialized with a "borderCollapse" flag: Collapse it now before the first layout.
132 if (!me.initialCollapsedComplete) {
133 Ext.iterate(me.regions, function(name, region){
134 if (region.borderCollapse) {
135 me.onBeforeRegionCollapse(region, region.collapseDirection, false, 0);
136 }
137 });
138 me.initialCollapsedComplete = true;
139 }
140 },
141
142 isValidParent : function(item, target, position) {
143 if (!this.borderLayoutInitialized) {
144 this.initializeBorderLayout();
145 }
146
147 // Delegate this operation to the shadow "V" or "H" box layout.
148 return this.shadowLayout.isValidParent(item, target, position);
149 },
150
151 beforeLayout: function() {
152 if (!this.borderLayoutInitialized) {
153 this.initializeBorderLayout();
154 }
155
156 // Delegate this operation to the shadow "V" or "H" box layout.
157 this.shadowLayout.beforeLayout();
158
159 // note: don't call base because that does a renderItems again
160 },
161
162 renderItems: function(items, target) {
163 //<debug>
164 Ext.Error.raise('This should not be called');
165 //</debug>
166 },
167
168 renderItem: function(item) {
169 //<debug>
170 Ext.Error.raise('This should not be called');
171 //</debug>
172 },
173
174 renderChildren: function() {
175 if (!this.borderLayoutInitialized) {
176 this.initializeBorderLayout();
177 }
178
179 this.shadowLayout.renderChildren();
180 },
181
182 /*
183 * Gathers items for a layout operation. Injected into child Box layouts through configuration.
184 * We must not include child items which are floated over the layout (are primed with a slide out animation)
185 */
186 getVisibleItems: function() {
187 return Ext.ComponentQuery.query(':not([slideOutAnim])', this.callParent(arguments));
188 },
189
190 initializeBorderLayout: function() {
191 var me = this,
192 i = 0,
193 items = me.getLayoutItems(),
194 ln = items.length,
195 regions = (me.regions = {}),
196 vBoxItems = [],
197 hBoxItems = [],
198 horizontalFlex = 0,
199 verticalFlex = 0,
200 comp, percentage;
201
202 // Map of Splitters for each region
203 me.splitters = {};
204
205 // Map of regions
206 for (; i < ln; i++) {
207 comp = items[i];
208 regions[comp.region] = comp;
209
210 // Intercept collapsing to implement showing an alternate Component as a collapsed placeholder
211 if (comp.region != 'center' && comp.collapsible && comp.collapseMode != 'header') {
212
213 // This layout intercepts any initial collapsed state. Panel must not do this itself.
214 comp.borderCollapse = comp.collapsed;
215 comp.collapsed = false;
216
217 comp.on({
218 beforecollapse: me.onBeforeRegionCollapse,
219 beforeexpand: me.onBeforeRegionExpand,
220 destroy: me.onRegionDestroy,
221 scope: me
222 });
223 me.setupState(comp);
224 }
225 }
226 //<debug>
227 if (!regions.center) {
228 Ext.Error.raise("You must specify a center region when defining a BorderLayout.");
229 }
230 //</debug>
231 comp = regions.center;
232 if (!comp.flex) {
233 comp.flex = 1;
234 }
235 delete comp.width;
236 comp.maintainFlex = true;
237
238 // Begin the VBox and HBox item list.
239 comp = regions.west;
240 if (comp) {
241 comp.collapseDirection = Ext.Component.DIRECTION_LEFT;
242 hBoxItems.push(comp);
243 if (comp.split) {
244 hBoxItems.push(me.splitters.west = me.createSplitter(comp));
245 }
246 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
247 if (percentage) {
248 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
249 delete comp.width;
250 }
251 }
252 comp = regions.north;
253 if (comp) {
254 comp.collapseDirection = Ext.Component.DIRECTION_TOP;
255 vBoxItems.push(comp);
256 if (comp.split) {
257 vBoxItems.push(me.splitters.north = me.createSplitter(comp));
258 }
259 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
260 if (percentage) {
261 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
262 delete comp.height;
263 }
264 }
265
266 // Decide into which Collection the center region goes.
267 if (regions.north || regions.south) {
268 if (regions.east || regions.west) {
269
270 // Create the embedded center. Mark it with the region: 'center' property so that it can be identified as the center.
271 vBoxItems.push(me.embeddedContainer = Ext.create('Ext.container.Container', {
272 xtype: 'container',
273 region: 'center',
274 id: me.owner.id + '-embedded-center',
275 cls: Ext.baseCSSPrefix + 'border-item',
276 flex: regions.center.flex,
277 maintainFlex: true,
278 layout: {
279 type: 'hbox',
280 align: 'stretch',
281 getVisibleItems: me.getVisibleItems
282 }
283 }));
284 hBoxItems.push(regions.center);
285 }
286 // No east or west: the original center goes straight into the vbox
287 else {
288 vBoxItems.push(regions.center);
289 }
290 }
291 // If we have no north or south, then the center is part of the HBox items
292 else {
293 hBoxItems.push(regions.center);
294 }
295
296 // Finish off the VBox and HBox item list.
297 comp = regions.south;
298 if (comp) {
299 comp.collapseDirection = Ext.Component.DIRECTION_BOTTOM;
300 if (comp.split) {
301 vBoxItems.push(me.splitters.south = me.createSplitter(comp));
302 }
303 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
304 if (percentage) {
305 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
306 delete comp.height;
307 }
308 vBoxItems.push(comp);
309 }
310 comp = regions.east;
311 if (comp) {
312 comp.collapseDirection = Ext.Component.DIRECTION_RIGHT;
313 if (comp.split) {
314 hBoxItems.push(me.splitters.east = me.createSplitter(comp));
315 }
316 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
317 if (percentage) {
318 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
319 delete comp.width;
320 }
321 hBoxItems.push(comp);
322 }
323
324 // Create the injected "items" collections for the Containers.
325 // If we have north or south, then the shadow Container will be a VBox.
326 // If there are also east or west regions, its center will be a shadow HBox.
327 // If there are *only* east or west regions, then the shadow layout will be an HBox (or Fit).
328 if (regions.north || regions.south) {
329
330 me.shadowContainer = Ext.create('Ext.container.Container', {
331 ownerCt: me.owner,
332 el: me.getTarget(),
333 layout: Ext.applyIf({
334 type: 'vbox',
335 align: 'stretch',
336 getVisibleItems: me.getVisibleItems
337 }, me.initialConfig)
338 });
339 me.createItems(me.shadowContainer, vBoxItems);
340
341 // Allow the Splitters to orientate themselves
342 if (me.splitters.north) {
343 me.splitters.north.ownerCt = me.shadowContainer;
344 }
345 if (me.splitters.south) {
346 me.splitters.south.ownerCt = me.shadowContainer;
347 }
348
349 // Inject items into the HBox Container if there is one - if there was an east or west.
350 if (me.embeddedContainer) {
351 me.embeddedContainer.ownerCt = me.shadowContainer;
352 me.createItems(me.embeddedContainer, hBoxItems);
353
354 // Allow the Splitters to orientate themselves
355 if (me.splitters.east) {
356 me.splitters.east.ownerCt = me.embeddedContainer;
357 }
358 if (me.splitters.west) {
359 me.splitters.west.ownerCt = me.embeddedContainer;
360 }
361
362 // These spliiters need to be constrained by components one-level below
363 // the component in their vobx. We update the min/maxHeight on the helper
364 // (embeddedContainer) prior to starting the split/drag. This has to be
365 // done on-the-fly to allow min/maxHeight of the E/C/W regions to be set
366 // dynamically.
367 Ext.each([me.splitters.north, me.splitters.south], function (splitter) {
368 if (splitter) {
369 splitter.on('beforedragstart', me.fixHeightConstraints, me);
370 }
371 });
372
373 // The east or west region wanted a percentage
374 if (horizontalFlex) {
375 regions.center.flex -= horizontalFlex;
376 }
377 // The north or south region wanted a percentage
378 if (verticalFlex) {
379 me.embeddedContainer.flex -= verticalFlex;
380 }
381 } else {
382 // The north or south region wanted a percentage
383 if (verticalFlex) {
384 regions.center.flex -= verticalFlex;
385 }
386 }
387 }
388 // If we have no north or south, then there's only one Container, and it's
389 // an HBox, or, if only a center region was specified, a Fit.
390 else {
391 me.shadowContainer = Ext.create('Ext.container.Container', {
392 ownerCt: me.owner,
393 el: me.getTarget(),
394 layout: Ext.applyIf({
395 type: (hBoxItems.length == 1) ? 'fit' : 'hbox',
396 align: 'stretch'
397 }, me.initialConfig)
398 });
399 me.createItems(me.shadowContainer, hBoxItems);
400
401 // Allow the Splitters to orientate themselves
402 if (me.splitters.east) {
403 me.splitters.east.ownerCt = me.shadowContainer;
404 }
405 if (me.splitters.west) {
406 me.splitters.west.ownerCt = me.shadowContainer;
407 }
408
409 // The east or west region wanted a percentage
410 if (horizontalFlex) {
411 regions.center.flex -= verticalFlex;
412 }
413 }
414
415 // Create upward links from the region Components to their shadow ownerCts
416 for (i = 0, items = me.shadowContainer.items.items, ln = items.length; i < ln; i++) {
417 items[i].shadowOwnerCt = me.shadowContainer;
418 }
419 if (me.embeddedContainer) {
420 for (i = 0, items = me.embeddedContainer.items.items, ln = items.length; i < ln; i++) {
421 items[i].shadowOwnerCt = me.embeddedContainer;
422 }
423 }
424
425 // This is the layout that we delegate all operations to
426 me.shadowLayout = me.shadowContainer.getLayout();
427
428 me.borderLayoutInitialized = true;
429 },
430
431 setupState: function(comp){
432 var getState = comp.getState;
433 comp.getState = function(){
434 // call the original getState
435 var state = getState.call(comp) || {},
436 region = comp.region;
437
438 state.collapsed = !!comp.collapsed;
439 if (region == 'west' || region == 'east') {
440 state.width = comp.getWidth();
441 } else {
442 state.height = comp.getHeight();
443 }
444 return state;
445 };
446 comp.addStateEvents(['collapse', 'expand', 'resize']);
447 },
448
449<span id='Ext-layout-container-Border-method-createItems'> /**
450</span> * Create the items collection for our shadow/embedded containers
451 * @private
452 */
453 createItems: function(container, items){
454 // Have to inject an items Collection *after* construction.
455 // The child items of the shadow layout must retain their original, user-defined ownerCt
456 delete container.items;
457 container.initItems();
458 container.items.addAll(items);
459 },
460
461 // Private
462 // Create a splitter for a child of the layout.
463 createSplitter: function(comp) {
464 var me = this,
465 interceptCollapse = (comp.collapseMode != 'header'),
466 resizer;
467
468 resizer = Ext.create('Ext.resizer.Splitter', {
469 hidden: !!comp.hidden,
470 collapseTarget: comp,
471 performCollapse: !interceptCollapse,
472 listeners: interceptCollapse ? {
473 click: {
474 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
475 element: 'collapseEl'
476 }
477 } : null
478 });
479
480 // Mini collapse means that the splitter is the placeholder Component
481 if (comp.collapseMode == 'mini') {
482 comp.placeholder = resizer;
483 resizer.collapsedCls = comp.collapsedCls;
484 }
485
486 // Arrange to hide/show a region's associated splitter when the region is hidden/shown
487 comp.on({
488 hide: me.onRegionVisibilityChange,
489 show: me.onRegionVisibilityChange,
490 scope: me
491 });
492 return resizer;
493 },
494
495 // Private
496 // Propagates the min/maxHeight values from the inner hbox items to its container.
497 fixHeightConstraints: function () {
498 var me = this,
499 ct = me.embeddedContainer,
500 maxHeight = 1e99, minHeight = -1;
501
502 if (!ct) {
503 return;
504 }
505
506 ct.items.each(function (item) {
507 if (Ext.isNumber(item.maxHeight)) {
508 maxHeight = Math.max(maxHeight, item.maxHeight);
509 }
510 if (Ext.isNumber(item.minHeight)) {
511 minHeight = Math.max(minHeight, item.minHeight);
512 }
513 });
514
515 ct.maxHeight = maxHeight;
516 ct.minHeight = minHeight;
517 },
518
519 // Hide/show a region's associated splitter when the region is hidden/shown
520 onRegionVisibilityChange: function(comp){
521 this.splitters[comp.region][comp.hidden ? 'hide' : 'show']();
522 this.layout();
523 },
524
525 // Called when a splitter mini-collapse tool is clicked on.
526 // The listener is only added if this layout is controlling collapsing,
527 // not if the component's collapseMode is 'mini' or 'header'.
528 onSplitterCollapseClick: function(comp) {
529 if (comp.collapsed) {
530 this.onPlaceHolderToolClick(null, null, null, {client: comp});
531 } else {
532 comp.collapse();
533 }
534 },
535
536<span id='Ext-layout-container-Border-method-getPlaceholder'> /**
537</span> * Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the
538 * layout will collapse. By default, this will be a {@link Ext.panel.Header Header} component (Docked to the
539 * appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}. config to customize this.
540 *
541 * **Note that this will be a fully instantiated Component, but will only be _rendered_ when the Panel is first
542 * collapsed.**
543 * @param {Ext.panel.Panel} panel The child Panel of the layout for which to return the {@link
544 * Ext.panel.Panel#placeholder placeholder}.
545 * @return {Ext.Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link
546 * Ext.panel.Panel#collapseMode collapseMode} is `'header'`, in which case _undefined_ is returned.
547 */
548 getPlaceholder: function(comp) {
549 var me = this,
550 placeholder = comp.placeholder,
551 shadowContainer = comp.shadowOwnerCt,
552 shadowLayout = shadowContainer.layout,
553 oppositeDirection = Ext.panel.Panel.prototype.getOppositeDirection(comp.collapseDirection),
554 horiz = (comp.region == 'north' || comp.region == 'south');
555
556 // No placeholder if the collapse mode is not the Border layout default
557 if (comp.collapseMode == 'header') {
558 return;
559 }
560
561 // Provide a replacement Container with an expand tool
562 if (!placeholder) {
563 if (comp.collapseMode == 'mini') {
564 placeholder = Ext.create('Ext.resizer.Splitter', {
565 id: 'collapse-placeholder-' + comp.id,
566 collapseTarget: comp,
567 performCollapse: false,
568 listeners: {
569 click: {
570 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
571 element: 'collapseEl'
572 }
573 }
574 });
575 placeholder.addCls(placeholder.collapsedCls);
576 } else {
577 placeholder = {
578 id: 'collapse-placeholder-' + comp.id,
579 margins: comp.initialConfig.margins || Ext.getClass(comp).prototype.margins,
580 xtype: 'header',
581 orientation: horiz ? 'horizontal' : 'vertical',
582 title: comp.title,
583 textCls: comp.headerTextCls,
584 iconCls: comp.iconCls,
585 baseCls: comp.baseCls + '-header',
586 ui: comp.ui,
587 indicateDrag: comp.draggable,
588 cls: Ext.baseCSSPrefix + 'region-collapsed-placeholder ' + Ext.baseCSSPrefix + 'region-collapsed-' + comp.collapseDirection + '-placeholder ' + comp.collapsedCls,
589 listeners: comp.floatable ? {
590 click: {
591 fn: function(e) {
592 me.floatCollapsedPanel(e, comp);
593 },
594 element: 'el'
595 }
596 } : null
597 };
598 // Hack for IE6/7/IEQuirks's inability to display an inline-block
599 if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) && !horiz) {
600 placeholder.width = 25;
601 }
602 if (!comp.hideCollapseTool) {
603 placeholder[horiz ? 'tools' : 'items'] = [{
604 xtype: 'tool',
605 client: comp,
606 type: 'expand-' + oppositeDirection,
607 handler: me.onPlaceHolderToolClick,
608 scope: me
609 }];
610 }
611 }
612 placeholder = me.owner.createComponent(placeholder);
613 if (comp.isXType('panel')) {
614 comp.on({
615 titlechange: me.onRegionTitleChange,
616 iconchange: me.onRegionIconChange,
617 scope: me
618 });
619 }
620 }
621
622 // The collapsed Component holds a reference to its placeholder and vice versa
623 comp.placeholder = placeholder;
624 placeholder.comp = comp;
625
626 return placeholder;
627 },
628
629<span id='Ext-layout-container-Border-method-onRegionTitleChange'> /**
630</span> * @private
631 * Update the placeholder title when panel title has been set or changed.
632 */
633 onRegionTitleChange: function(comp, newTitle) {
634 comp.placeholder.setTitle(newTitle);
635 },
636
637<span id='Ext-layout-container-Border-method-onRegionIconChange'> /**
638</span> * @private
639 * Update the placeholder iconCls when panel iconCls has been set or changed.
640 */
641 onRegionIconChange: function(comp, newIconCls) {
642 comp.placeholder.setIconCls(newIconCls);
643 },
644
645<span id='Ext-layout-container-Border-method-calculateChildBox'> /**
646</span> * @private
647 * Calculates the size and positioning of the passed child item. Must be present because Panel's expand,
648 * when configured with a flex, calls this method on its ownerCt's layout.
649 * @param {Ext.Component} child The child Component to calculate the box for
650 * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
651 */
652 calculateChildBox: function(comp) {
653 var me = this;
654 if (me.shadowContainer.items.contains(comp)) {
655 return me.shadowContainer.layout.calculateChildBox(comp);
656 }
657 else if (me.embeddedContainer && me.embeddedContainer.items.contains(comp)) {
658 return me.embeddedContainer.layout.calculateChildBox(comp);
659 }
660 },
661
662<span id='Ext-layout-container-Border-method-onBeforeRegionCollapse'> /**
663</span> * @private
664 * Intercepts the Panel's own collapse event and perform's substitution of the Panel
665 * with a placeholder Header orientated in the appropriate dimension.
666 * @param comp The Panel being collapsed.
667 * @param direction
668 * @param animate
669 * @returns {Boolean} false to inhibit the Panel from performing its own collapse.
670 */
671 onBeforeRegionCollapse: function(comp, direction, animate) {
672 if (comp.collapsedChangingLayout) {
673 //<debug warn>
674 if (Ext.global.console && Ext.global.console.warn) {
675 Ext.global.console.warn(Ext.getDisplayName(arguments.callee), 'aborted because the collapsed state is in the middle of changing');
676 }
677 //</debug>
678 return false;
679 }
680 comp.collapsedChangingLayout = true;
681 var me = this,
682 compEl = comp.el,
683 width,
684 miniCollapse = comp.collapseMode == 'mini',
685 shadowContainer = comp.shadowOwnerCt,
686 shadowLayout = shadowContainer.layout,
687 placeholder = comp.placeholder,
688 sl = me.owner.suspendLayout,
689 scsl = shadowContainer.suspendLayout,
690 isNorthOrWest = (comp.region == 'north' || comp.region == 'west'); // Flag to keep the placeholder non-adjacent to any Splitter
691
692 // Do not trigger a layout during transition to collapsed Component
693 me.owner.suspendLayout = true;
694 shadowContainer.suspendLayout = true;
695
696 // Prevent upward notifications from downstream layouts
697 shadowLayout.layoutBusy = true;
698 if (shadowContainer.componentLayout) {
699 shadowContainer.componentLayout.layoutBusy = true;
700 }
701 me.shadowContainer.layout.layoutBusy = true;
702 me.layoutBusy = true;
703 me.owner.componentLayout.layoutBusy = true;
704
705 // Provide a replacement Container with an expand tool
706 if (!placeholder) {
707 placeholder = me.getPlaceholder(comp);
708 }
709
710 // placeholder already in place; show it.
711 if (placeholder.shadowOwnerCt === shadowContainer) {
712 placeholder.show();
713 }
714 // Insert the collapsed placeholder Component into the appropriate Box layout shadow Container
715 // It must go next to its client Component, but non-adjacent to the splitter so splitter can find its collapse client.
716 // Inject an ownerCt value pointing to the owner, border layout Container as the user will expect.
717 else {
718 shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
719 placeholder.shadowOwnerCt = shadowContainer;
720 placeholder.ownerCt = me.owner;
721 }
722
723 // Flag the collapsing Component as hidden and show the placeholder.
724 // This causes the shadow Box layout's calculateChildBoxes to calculate the correct new arrangement.
725 // We hide or slideOut the Component's element
726 comp.hidden = true;
727
728 if (!placeholder.rendered) {
729 shadowLayout.renderItem(placeholder, shadowLayout.innerCt);
730
731 // The inserted placeholder does not have the proper size, so copy the width
732 // for N/S or the height for E/W from the component. This fixes EXTJSIV-1562
733 // without recursive layouts. This is only an issue initially. After this time,
734 // placeholder will have the correct width/height set by the layout (which has
735 // already happened when we get here initially).
736 if (comp.region == 'north' || comp.region == 'south') {
737 placeholder.setCalculatedSize(comp.getWidth());
738 } else {
739 placeholder.setCalculatedSize(undefined, comp.getHeight());
740 }
741 }
742
743 // Jobs to be done after the collapse has been done
744 function afterCollapse() {
745 // Reinstate automatic laying out.
746 me.owner.suspendLayout = sl;
747 shadowContainer.suspendLayout = scsl;
748 delete shadowLayout.layoutBusy;
749 if (shadowContainer.componentLayout) {
750 delete shadowContainer.componentLayout.layoutBusy;
751 }
752 delete me.shadowContainer.layout.layoutBusy;
753 delete me.layoutBusy;
754 delete me.owner.componentLayout.layoutBusy;
755 delete comp.collapsedChangingLayout;
756
757 // Fire the collapse event: The Panel has in fact been collapsed, but by substitution of an alternative Component
758 comp.collapsed = true;
759 comp.fireEvent('collapse', comp);
760 }
761
762 /*
763 * Set everything to the new positions. Note that we
764 * only want to animate the collapse if it wasn't configured
765 * initially with collapsed: true
766 */
767 if (comp.animCollapse && me.initialCollapsedComplete) {
768 shadowLayout.layout();
769 compEl.dom.style.zIndex = 100;
770
771 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
772 if (!miniCollapse) {
773 placeholder.el.hide();
774 }
775 compEl.slideOut(me.slideDirection[comp.region], {
776 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
777 listeners: {
778 afteranimate: function() {
779 compEl.show().setLeftTop(-10000, -10000);
780 compEl.dom.style.zIndex = '';
781
782 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
783 if (!miniCollapse) {
784 placeholder.el.slideIn(me.slideDirection[comp.region], {
785 easing: 'linear',
786 duration: 100
787 });
788 }
789 afterCollapse();
790 }
791 }
792 });
793 } else {
794 compEl.setLeftTop(-10000, -10000);
795 shadowLayout.layout();
796 afterCollapse();
797 }
798
799 return false;
800 },
801
802 // Hijack the expand operation to remove the placeholder and slide the region back in.
803 onBeforeRegionExpand: function(comp, animate) {
804 // We don't check for comp.collapsedChangingLayout here because onPlaceHolderToolClick does it
805 this.onPlaceHolderToolClick(null, null, null, {client: comp, shouldFireBeforeexpand: false});
806 return false;
807 },
808
809 // Called when the collapsed placeholder is clicked to reinstate a "collapsed" (in reality hidden) Panel.
810 onPlaceHolderToolClick: function(e, target, owner, tool) {
811 var me = this,
812 comp = tool.client,
813
814 // Hide the placeholder unless it was the Component's preexisting splitter
815 hidePlaceholder = (comp.collapseMode != 'mini') || !comp.split,
816 compEl = comp.el,
817 toCompBox,
818 placeholder = comp.placeholder,
819 placeholderEl = placeholder.el,
820 shadowContainer = comp.shadowOwnerCt,
821 shadowLayout = shadowContainer.layout,
822 curSize,
823 sl = me.owner.suspendLayout,
824 scsl = shadowContainer.suspendLayout,
825 isFloating;
826
827 if (comp.collapsedChangingLayout) {
828 //<debug warn>
829 if (Ext.global.console && Ext.global.console.warn) {
830 Ext.global.console.warn(Ext.getDisplayName(arguments.callee), 'aborted because the collapsed state is in the middle of changing');
831 }
832 //</debug>
833 return false;
834 }
835 if (tool.shouldFireBeforeexpand !== false && comp.fireEvent('beforeexpand', comp, true) === false) {
836 return false;
837 }
838 comp.collapsedChangingLayout = true;
839 // If the slide in is still going, stop it.
840 // This will either leave the Component in its fully floated state (which is processed below)
841 // or in its collapsed state. Either way, we expand it..
842 if (comp.getActiveAnimation()) {
843 comp.stopAnimation();
844 }
845
846 // If the Component is fully floated when they click the placeholder Tool,
847 // it will be primed with a slide out animation object... so delete that
848 // and remove the mouseout listeners
849 if (comp.slideOutAnim) {
850 // Remove mouse leave monitors
851 compEl.un(comp.panelMouseMon);
852 placeholderEl.un(comp.placeholderMouseMon);
853
854 delete comp.slideOutAnim;
855 delete comp.panelMouseMon;
856 delete comp.placeholderMouseMon;
857
858 // If the Panel was floated and primed with a slideOut animation, we don't want to animate its layout operation.
859 isFloating = true;
860 }
861
862 // Do not trigger a layout during transition to expanded Component
863 me.owner.suspendLayout = true;
864 shadowContainer.suspendLayout = true;
865
866 // Prevent upward notifications from downstream layouts
867 shadowLayout.layoutBusy = true;
868 if (shadowContainer.componentLayout) {
869 shadowContainer.componentLayout.layoutBusy = true;
870 }
871 me.shadowContainer.layout.layoutBusy = true;
872 me.layoutBusy = true;
873 me.owner.componentLayout.layoutBusy = true;
874
875 // Unset the hidden and collapsed flags set in onBeforeRegionCollapse. The shadowLayout will now take it into account
876 // Find where the shadow Box layout plans to put the expanding Component.
877 comp.hidden = false;
878 comp.collapsed = false;
879 if (hidePlaceholder) {
880 placeholder.hidden = true;
881 }
882 toCompBox = shadowLayout.calculateChildBox(comp);
883
884 // Show the collapse tool in case it was hidden by the slide-in
885 if (comp.collapseTool) {
886 comp.collapseTool.show();
887 }
888
889 // If we're going to animate, we need to hide the component before moving it back into position
890 if (comp.animCollapse && !isFloating) {
891 compEl.setStyle('visibility', 'hidden');
892 }
893 compEl.setLeftTop(toCompBox.left, toCompBox.top);
894
895 // Equalize the size of the expanding Component prior to animation
896 // in case the layout area has changed size during the time it was collapsed.
897 curSize = comp.getSize();
898 if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
899 me.setItemSize(comp, toCompBox.width, toCompBox.height);
900 }
901
902 // Jobs to be done after the expand has been done
903 function afterExpand() {
904 // Reinstate automatic laying out.
905 me.owner.suspendLayout = sl;
906 shadowContainer.suspendLayout = scsl;
907 delete shadowLayout.layoutBusy;
908 if (shadowContainer.componentLayout) {
909 delete shadowContainer.componentLayout.layoutBusy;
910 }
911 delete me.shadowContainer.layout.layoutBusy;
912 delete me.layoutBusy;
913 delete me.owner.componentLayout.layoutBusy;
914 delete comp.collapsedChangingLayout;
915
916 // In case it was floated out and they clicked the re-expand tool
917 comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
918
919 // Fire the expand event: The Panel has in fact been expanded, but by removal of an alternative Component
920 comp.fireEvent('expand', comp);
921 }
922
923 // Hide the placeholder
924 if (hidePlaceholder) {
925 placeholder.el.hide();
926 }
927
928 // Slide the expanding Component to its new position.
929 // When that is done, layout the layout.
930 if (comp.animCollapse && !isFloating) {
931 compEl.dom.style.zIndex = 100;
932 compEl.slideIn(me.slideDirection[comp.region], {
933 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
934 listeners: {
935 afteranimate: function() {
936 compEl.dom.style.zIndex = '';
937 comp.hidden = false;
938 shadowLayout.onLayout();
939 afterExpand();
940 }
941 }
942 });
943 } else {
944 shadowLayout.onLayout();
945 afterExpand();
946 }
947 },
948
949 floatCollapsedPanel: function(e, comp) {
950
951 if (comp.floatable === false) {
952 return;
953 }
954
955 var me = this,
956 compEl = comp.el,
957 placeholder = comp.placeholder,
958 placeholderEl = placeholder.el,
959 shadowContainer = comp.shadowOwnerCt,
960 shadowLayout = shadowContainer.layout,
961 placeholderBox = shadowLayout.getChildBox(placeholder),
962 scsl = shadowContainer.suspendLayout,
963 curSize, toCompBox, compAnim;
964
965 // Ignore clicks on tools.
966 if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
967 return;
968 }
969
970 // It's *being* animated, ignore the click.
971 // Possible future enhancement: Stop and *reverse* the current active Fx.
972 if (compEl.getActiveAnimation()) {
973 return;
974 }
975
976 // If the Component is already fully floated when they click the placeholder,
977 // it will be primed with a slide out animation object... so slide it out.
978 if (comp.slideOutAnim) {
979 me.slideOutFloatedComponent(comp);
980 return;
981 }
982
983 // Function to be called when the mouse leaves the floated Panel
984 // Slide out when the mouse leaves the region bounded by the slid Component and its placeholder.
985 function onMouseLeaveFloated(e) {
986 var slideRegion = compEl.getRegion().union(placeholderEl.getRegion()).adjust(1, -1, -1, 1);
987
988 // If mouse is not within slide Region, slide it out
989 if (!slideRegion.contains(e.getPoint())) {
990 me.slideOutFloatedComponent(comp);
991 }
992 }
993
994 // Monitor for mouseouting of the placeholder. Hide it if they exit for half a second or more
995 comp.placeholderMouseMon = placeholderEl.monitorMouseLeave(500, onMouseLeaveFloated);
996
997 // Do not trigger a layout during slide out of the Component
998 shadowContainer.suspendLayout = true;
999
1000 // Prevent upward notifications from downstream layouts
1001 me.layoutBusy = true;
1002 me.owner.componentLayout.layoutBusy = true;
1003
1004 // The collapse tool is hidden while slid.
1005 // It is re-shown on expand.
1006 if (comp.collapseTool) {
1007 comp.collapseTool.hide();
1008 }
1009
1010 // Set flags so that the layout will calculate the boxes for what we want
1011 comp.hidden = false;
1012 comp.collapsed = false;
1013 placeholder.hidden = true;
1014
1015 // Recalculate new arrangement of the Component being floated.
1016 toCompBox = shadowLayout.calculateChildBox(comp);
1017 placeholder.hidden = false;
1018
1019 // Component to appear just after the placeholder, whatever "after" means in the context of the shadow Box layout.
1020 if (comp.region == 'north' || comp.region == 'west') {
1021 toCompBox[shadowLayout.parallelBefore] += placeholderBox[shadowLayout.parallelPrefix] - 1;
1022 } else {
1023 toCompBox[shadowLayout.parallelBefore] -= (placeholderBox[shadowLayout.parallelPrefix] - 1);
1024 }
1025 compEl.setStyle('visibility', 'hidden');
1026 compEl.setLeftTop(toCompBox.left, toCompBox.top);
1027
1028 // Equalize the size of the expanding Component prior to animation
1029 // in case the layout area has changed size during the time it was collapsed.
1030 curSize = comp.getSize();
1031 if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
1032 me.setItemSize(comp, toCompBox.width, toCompBox.height);
1033 }
1034
1035 // This animation slides the collapsed Component's el out to just beyond its placeholder
1036 compAnim = {
1037 listeners: {
1038 afteranimate: function() {
1039 shadowContainer.suspendLayout = scsl;
1040 delete me.layoutBusy;
1041 delete me.owner.componentLayout.layoutBusy;
1042
1043 // Prime the Component with an Anim config object to slide it back out
1044 compAnim.listeners = {
1045 afterAnimate: function() {
1046 compEl.show().removeCls(Ext.baseCSSPrefix + 'border-region-slide-in').setLeftTop(-10000, -10000);
1047
1048 // Reinstate the correct, current state after slide out animation finishes
1049 comp.hidden = true;
1050 comp.collapsed = true;
1051 delete comp.slideOutAnim;
1052 delete comp.panelMouseMon;
1053 delete comp.placeholderMouseMon;
1054 }
1055 };
1056 comp.slideOutAnim = compAnim;
1057 }
1058 },
1059 duration: 500
1060 };
1061
1062 // Give the element the correct class which places it at a high z-index
1063 compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');
1064
1065 // Begin the slide in
1066 compEl.slideIn(me.slideDirection[comp.region], compAnim);
1067
1068 // Monitor for mouseouting of the slid area. Hide it if they exit for half a second or more
1069 comp.panelMouseMon = compEl.monitorMouseLeave(500, onMouseLeaveFloated);
1070
1071 },
1072
1073 slideOutFloatedComponent: function(comp) {
1074 var compEl = comp.el,
1075 slideOutAnim;
1076
1077 // Remove mouse leave monitors
1078 compEl.un(comp.panelMouseMon);
1079 comp.placeholder.el.un(comp.placeholderMouseMon);
1080
1081 // Slide the Component out
1082 compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);
1083
1084 delete comp.slideOutAnim;
1085 delete comp.panelMouseMon;
1086 delete comp.placeholderMouseMon;
1087 },
1088
1089 /*
1090 * @private
1091 * Ensure any collapsed placeholder Component is destroyed along with its region.
1092 * Can't do this in onDestroy because they may remove a Component and use it elsewhere.
1093 */
1094 onRegionDestroy: function(comp) {
1095 var placeholder = comp.placeholder;
1096 if (placeholder) {
1097 delete placeholder.ownerCt;
1098 placeholder.destroy();
1099 }
1100 },
1101
1102 /*
1103 * @private
1104 * Ensure any shadow Containers are destroyed.
1105 * Ensure we don't keep references to Components.
1106 */
1107 onDestroy: function() {
1108 var me = this,
1109 shadowContainer = me.shadowContainer,
1110 embeddedContainer = me.embeddedContainer;
1111
1112 if (shadowContainer) {
1113 delete shadowContainer.ownerCt;
1114 Ext.destroy(shadowContainer);
1115 }
1116
1117 if (embeddedContainer) {
1118 delete embeddedContainer.ownerCt;
1119 Ext.destroy(embeddedContainer);
1120 }
1121 delete me.regions;
1122 delete me.splitters;
1123 delete me.shadowContainer;
1124 delete me.embeddedContainer;
1125 me.callParent(arguments);
1126 }
1127});
1128</pre>
1129</body>
1130</html>