501f14482151caf744d478f86edce8b1f7b2e7a5
[roojs1] / Roo / BorderLayout.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11 /**
12  * @class Roo.BorderLayout
13  * @extends Roo.LayoutManager
14  * @children Roo.ContentPanel
15  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
16  * please see: <br><br>
17  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
18  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
19  * Example:
20  <pre><code>
21  var layout = new Roo.BorderLayout(document.body, {
22     north: {
23         initialSize: 25,
24         titlebar: false
25     },
26     west: {
27         split:true,
28         initialSize: 200,
29         minSize: 175,
30         maxSize: 400,
31         titlebar: true,
32         collapsible: true
33     },
34     east: {
35         split:true,
36         initialSize: 202,
37         minSize: 175,
38         maxSize: 400,
39         titlebar: true,
40         collapsible: true
41     },
42     south: {
43         split:true,
44         initialSize: 100,
45         minSize: 100,
46         maxSize: 200,
47         titlebar: true,
48         collapsible: true
49     },
50     center: {
51         titlebar: true,
52         autoScroll:true,
53         resizeTabs: true,
54         minTabWidth: 50,
55         preferredTabWidth: 150
56     }
57 });
58
59 // shorthand
60 var CP = Roo.ContentPanel;
61
62 layout.beginUpdate();
63 layout.add("north", new CP("north", "North"));
64 layout.add("south", new CP("south", {title: "South", closable: true}));
65 layout.add("west", new CP("west", {title: "West"}));
66 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
67 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
68 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
69 layout.getRegion("center").showPanel("center1");
70 layout.endUpdate();
71 </code></pre>
72
73 <b>The container the layout is rendered into can be either the body element or any other element.
74 If it is not the body element, the container needs to either be an absolute positioned element,
75 or you will need to add "position:relative" to the css of the container.  You will also need to specify
76 the container size if it is not the body element.</b>
77
78 * @constructor
79 * Create a new BorderLayout
80 * @param {String/HTMLElement/Element} container The container this layout is bound to
81 * @param {Object} config Configuration options
82  */
83 Roo.BorderLayout = function(container, config){
84     config = config || {};
85     Roo.BorderLayout.superclass.constructor.call(this, container, config);
86     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
87     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
88         var target = this.factory.validRegions[i];
89         if(config[target]){
90             this.addRegion(target, config[target]);
91         }
92     }
93 };
94
95 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
96         /*
97         * @cfg {Roo.bootstrap.Component} cn[] children
98         */
99         /**
100          * @cfg {Roo.LayoutRegion} east
101          */
102         /**
103          * @cfg {Roo.LayoutRegion} west
104          */
105         /**
106          * @cfg {Roo.LayoutRegion} north
107          */
108         /**
109          * @cfg {Roo.LayoutRegion} south
110          */
111         /**
112          * @cfg {Roo.LayoutRegion} center
113          */
114     /**
115      * Creates and adds a new region if it doesn't already exist.
116      * @param {String} target The target region key (north, south, east, west or center).
117      * @param {Object} config The regions config object
118      * @return {BorderLayoutRegion} The new region
119      */
120     addRegion : function(target, config){
121         if(!this.regions[target]){
122             var r = this.factory.create(target, this, config);
123             this.bindRegion(target, r);
124         }
125         return this.regions[target];
126     },
127
128     // private (kinda)
129     bindRegion : function(name, r){
130         this.regions[name] = r;
131         r.on("visibilitychange", this.layout, this);
132         r.on("paneladded", this.layout, this);
133         r.on("panelremoved", this.layout, this);
134         r.on("invalidated", this.layout, this);
135         r.on("resized", this.onRegionResized, this);
136         r.on("collapsed", this.onRegionCollapsed, this);
137         r.on("expanded", this.onRegionExpanded, this);
138     },
139
140     /**
141      * Performs a layout update.
142      */
143     layout : function(){
144         if(this.updating) {
145             return;
146         }
147         var size = this.getViewSize();
148         var w = size.width;
149         var h = size.height;
150         var centerW = w;
151         var centerH = h;
152         var centerY = 0;
153         var centerX = 0;
154         //var x = 0, y = 0;
155
156         var rs = this.regions;
157         var north = rs["north"];
158         var south = rs["south"]; 
159         var west = rs["west"];
160         var east = rs["east"];
161         var center = rs["center"];
162         //if(this.hideOnLayout){ // not supported anymore
163             //c.el.setStyle("display", "none");
164         //}
165         if(north && north.isVisible()){
166             var b = north.getBox();
167             var m = north.getMargins();
168             b.width = w - (m.left+m.right);
169             b.x = m.left;
170             b.y = m.top;
171             centerY = b.height + b.y + m.bottom;
172             centerH -= centerY;
173             north.updateBox(this.safeBox(b));
174         }
175         if(south && south.isVisible()){
176             var b = south.getBox();
177             var m = south.getMargins();
178             b.width = w - (m.left+m.right);
179             b.x = m.left;
180             var totalHeight = (b.height + m.top + m.bottom);
181             b.y = h - totalHeight + m.top;
182             centerH -= totalHeight;
183             south.updateBox(this.safeBox(b));
184         }
185         if(west && west.isVisible()){
186             var b = west.getBox();
187             var m = west.getMargins();
188             b.height = centerH - (m.top+m.bottom);
189             b.x = m.left;
190             b.y = centerY + m.top;
191             var totalWidth = (b.width + m.left + m.right);
192             centerX += totalWidth;
193             centerW -= totalWidth;
194             west.updateBox(this.safeBox(b));
195         }
196         if(east && east.isVisible()){
197             var b = east.getBox();
198             var m = east.getMargins();
199             b.height = centerH - (m.top+m.bottom);
200             var totalWidth = (b.width + m.left + m.right);
201             b.x = w - totalWidth + m.left;
202             b.y = centerY + m.top;
203             centerW -= totalWidth;
204             east.updateBox(this.safeBox(b));
205         }
206         if(center){
207             var m = center.getMargins();
208             var centerBox = {
209                 x: centerX + m.left,
210                 y: centerY + m.top,
211                 width: centerW - (m.left+m.right),
212                 height: centerH - (m.top+m.bottom)
213             };
214             //if(this.hideOnLayout){
215                 //center.el.setStyle("display", "block");
216             //}
217             center.updateBox(this.safeBox(centerBox));
218         }
219         this.el.repaint();
220         this.fireEvent("layout", this);
221     },
222
223     // private
224     safeBox : function(box){
225         box.width = Math.max(0, box.width);
226         box.height = Math.max(0, box.height);
227         return box;
228     },
229
230     /**
231      * Adds a ContentPanel (or subclass) to this layout.
232      * @param {String} target The target region key (north, south, east, west or center).
233      * @param {Roo.ContentPanel} panel The panel to add
234      * @return {Roo.ContentPanel} The added panel
235      */
236     add : function(target, panel){
237          
238         target = target.toLowerCase();
239         return this.regions[target].add(panel);
240     },
241
242     /**
243      * Remove a ContentPanel (or subclass) to this layout.
244      * @param {String} target The target region key (north, south, east, west or center).
245      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
246      * @return {Roo.ContentPanel} The removed panel
247      */
248     remove : function(target, panel){
249         target = target.toLowerCase();
250         return this.regions[target].remove(panel);
251     },
252
253     /**
254      * Searches all regions for a panel with the specified id
255      * @param {String} panelId
256      * @return {Roo.ContentPanel} The panel or null if it wasn't found
257      */
258     findPanel : function(panelId){
259         var rs = this.regions;
260         for(var target in rs){
261             if(typeof rs[target] != "function"){
262                 var p = rs[target].getPanel(panelId);
263                 if(p){
264                     return p;
265                 }
266             }
267         }
268         return null;
269     },
270
271     /**
272      * Searches all regions for a panel with the specified id and activates (shows) it.
273      * @param {String/ContentPanel} panelId The panels id or the panel itself
274      * @return {Roo.ContentPanel} The shown panel or null
275      */
276     showPanel : function(panelId) {
277       var rs = this.regions;
278       for(var target in rs){
279          var r = rs[target];
280          if(typeof r != "function"){
281             if(r.hasPanel(panelId)){
282                return r.showPanel(panelId);
283             }
284          }
285       }
286       return null;
287    },
288
289    /**
290      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
291      * @param {Roo.state.Provider} provider (optional) An alternate state provider
292      */
293     restoreState : function(provider){
294         if(!provider){
295             provider = Roo.state.Manager;
296         }
297         var sm = new Roo.LayoutStateManager();
298         sm.init(this, provider);
299     },
300
301     /**
302      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
303      * object should contain properties for each region to add ContentPanels to, and each property's value should be
304      * a valid ContentPanel config object.  Example:
305      * <pre><code>
306 // Create the main layout
307 var layout = new Roo.BorderLayout('main-ct', {
308     west: {
309         split:true,
310         minSize: 175,
311         titlebar: true
312     },
313     center: {
314         title:'Components'
315     }
316 }, 'main-ct');
317
318 // Create and add multiple ContentPanels at once via configs
319 layout.batchAdd({
320    west: {
321        id: 'source-files',
322        autoCreate:true,
323        title:'Ext Source Files',
324        autoScroll:true,
325        fitToFrame:true
326    },
327    center : {
328        el: cview,
329        autoScroll:true,
330        fitToFrame:true,
331        toolbar: tb,
332        resizeEl:'cbody'
333    }
334 });
335 </code></pre>
336      * @param {Object} regions An object containing ContentPanel configs by region name
337      */
338     batchAdd : function(regions){
339         this.beginUpdate();
340         for(var rname in regions){
341             var lr = this.regions[rname];
342             if(lr){
343                 this.addTypedPanels(lr, regions[rname]);
344             }
345         }
346         this.endUpdate();
347     },
348
349     // private
350     addTypedPanels : function(lr, ps){
351         if(typeof ps == 'string'){
352             lr.add(new Roo.ContentPanel(ps));
353         }
354         else if(ps instanceof Array){
355             for(var i =0, len = ps.length; i < len; i++){
356                 this.addTypedPanels(lr, ps[i]);
357             }
358         }
359         else if(!ps.events){ // raw config?
360             var el = ps.el;
361             delete ps.el; // prevent conflict
362             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
363         }
364         else {  // panel object assumed!
365             lr.add(ps);
366         }
367     },
368     /**
369      * Adds a xtype elements to the layout.
370      * <pre><code>
371
372 layout.addxtype({
373        xtype : 'ContentPanel',
374        region: 'west',
375        items: [ .... ]
376    }
377 );
378
379 layout.addxtype({
380         xtype : 'NestedLayoutPanel',
381         region: 'west',
382         layout: {
383            center: { },
384            west: { }   
385         },
386         items : [ ... list of content panels or nested layout panels.. ]
387    }
388 );
389 </code></pre>
390      * @param {Object} cfg Xtype definition of item to add.
391      */
392     addxtype : function(cfg)
393     {
394         // basically accepts a pannel...
395         // can accept a layout region..!?!?
396         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
397         
398         if (!cfg.xtype.match(/Panel$/)) {
399             return false;
400         }
401         var ret = false;
402         
403         if (typeof(cfg.region) == 'undefined') {
404             Roo.log("Failed to add Panel, region was not set");
405             Roo.log(cfg);
406             return false;
407         }
408         var region = cfg.region;
409         delete cfg.region;
410         
411           
412         var xitems = [];
413         if (cfg.items) {
414             xitems = cfg.items;
415             delete cfg.items;
416         }
417         var nb = false;
418         
419         switch(cfg.xtype) 
420         {
421             case 'ContentPanel':  // ContentPanel (el, cfg)
422             case 'ScrollPanel':  // ContentPanel (el, cfg)
423             case 'ViewPanel': 
424                 if(cfg.autoCreate) {
425                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
426                 } else {
427                     var el = this.el.createChild();
428                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
429                 }
430                 
431                 this.add(region, ret);
432                 break;
433             
434             
435             case 'TreePanel': // our new panel!
436                 cfg.el = this.el.createChild();
437                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
438                 this.add(region, ret);
439                 break;
440             
441             case 'NestedLayoutPanel': 
442                 // create a new Layout (which is  a Border Layout...
443                 var el = this.el.createChild();
444                 var clayout = cfg.layout;
445                 delete cfg.layout;
446                 clayout.items   = clayout.items  || [];
447                 // replace this exitems with the clayout ones..
448                 xitems = clayout.items;
449                  
450                 
451                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
452                     cfg.background = false;
453                 }
454                 var layout = new Roo.BorderLayout(el, clayout);
455                 
456                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
457                 //console.log('adding nested layout panel '  + cfg.toSource());
458                 this.add(region, ret);
459                 nb = {}; /// find first...
460                 break;
461                 
462             case 'GridPanel': 
463             
464                 // needs grid and region
465                 
466                 //var el = this.getRegion(region).el.createChild();
467                 var el = this.el.createChild();
468                 // create the grid first...
469                 
470                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
471                 delete cfg.grid;
472                 if (region == 'center' && this.active ) {
473                     cfg.background = false;
474                 }
475                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
476                 
477                 this.add(region, ret);
478                 if (cfg.background) {
479                     ret.on('activate', function(gp) {
480                         if (!gp.grid.rendered) {
481                             gp.grid.render();
482                         }
483                     });
484                 } else {
485                     grid.render();
486                 }
487                 break;
488            
489            
490            
491                 
492                 
493                 
494             default:
495                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
496                     
497                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
498                     this.add(region, ret);
499                 } else {
500                 
501                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
502                     return null;
503                 }
504                 
505              // GridPanel (grid, cfg)
506             
507         }
508         this.beginUpdate();
509         // add children..
510         var region = '';
511         var abn = {};
512         Roo.each(xitems, function(i)  {
513             region = nb && i.region ? i.region : false;
514             
515             var add = ret.addxtype(i);
516            
517             if (region) {
518                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
519                 if (!i.background) {
520                     abn[region] = nb[region] ;
521                 }
522             }
523             
524         });
525         this.endUpdate();
526
527         // make the last non-background panel active..
528         //if (nb) { Roo.log(abn); }
529         if (nb) {
530             
531             for(var r in abn) {
532                 region = this.getRegion(r);
533                 if (region) {
534                     // tried using nb[r], but it does not work..
535                      
536                     region.showPanel(abn[r]);
537                    
538                 }
539             }
540         }
541         return ret;
542         
543     }
544 });
545
546 /**
547  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
548  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
549  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
550  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
551  * <pre><code>
552 // shorthand
553 var CP = Roo.ContentPanel;
554
555 var layout = Roo.BorderLayout.create({
556     north: {
557         initialSize: 25,
558         titlebar: false,
559         panels: [new CP("north", "North")]
560     },
561     west: {
562         split:true,
563         initialSize: 200,
564         minSize: 175,
565         maxSize: 400,
566         titlebar: true,
567         collapsible: true,
568         panels: [new CP("west", {title: "West"})]
569     },
570     east: {
571         split:true,
572         initialSize: 202,
573         minSize: 175,
574         maxSize: 400,
575         titlebar: true,
576         collapsible: true,
577         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
578     },
579     south: {
580         split:true,
581         initialSize: 100,
582         minSize: 100,
583         maxSize: 200,
584         titlebar: true,
585         collapsible: true,
586         panels: [new CP("south", {title: "South", closable: true})]
587     },
588     center: {
589         titlebar: true,
590         autoScroll:true,
591         resizeTabs: true,
592         minTabWidth: 50,
593         preferredTabWidth: 150,
594         panels: [
595             new CP("center1", {title: "Close Me", closable: true}),
596             new CP("center2", {title: "Center Panel", closable: false})
597         ]
598     }
599 }, document.body);
600
601 layout.getRegion("center").showPanel("center1");
602 </code></pre>
603  * @param config
604  * @param targetEl
605  */
606 Roo.BorderLayout.create = function(config, targetEl){
607     var layout = new Roo.BorderLayout(targetEl || document.body, config);
608     layout.beginUpdate();
609     var regions = Roo.BorderLayout.RegionFactory.validRegions;
610     for(var j = 0, jlen = regions.length; j < jlen; j++){
611         var lr = regions[j];
612         if(layout.regions[lr] && config[lr].panels){
613             var r = layout.regions[lr];
614             var ps = config[lr].panels;
615             layout.addTypedPanels(r, ps);
616         }
617     }
618     layout.endUpdate();
619     return layout;
620 };
621
622 // private
623 Roo.BorderLayout.RegionFactory = {
624     // private
625     validRegions : ["north","south","east","west","center"],
626
627     // private
628     create : function(target, mgr, config){
629         target = target.toLowerCase();
630         if(config.lightweight || config.basic){
631             return new Roo.BasicLayoutRegion(mgr, config, target);
632         }
633         switch(target){
634             case "north":
635                 return new Roo.NorthLayoutRegion(mgr, config);
636             case "south":
637                 return new Roo.SouthLayoutRegion(mgr, config);
638             case "east":
639                 return new Roo.EastLayoutRegion(mgr, config);
640             case "west":
641                 return new Roo.WestLayoutRegion(mgr, config);
642             case "center":
643                 return new Roo.CenterLayoutRegion(mgr, config);
644         }
645         throw 'Layout region "'+target+'" not supported.';
646     }
647 };