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