Roo/bootstrap/layout/Border.js
[roojs1] / Roo / bootstrap / layout / Border.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.bootstrap.layout.Border
13  * @extends Roo.bootstrap.layout.Manager
14  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
15  * please see: examples/bootstrap/nested.html<br><br>
16  
17 <b>The container the layout is rendered into can be either the body element or any other element.
18 If it is not the body element, the container needs to either be an absolute positioned element,
19 or you will need to add "position:relative" to the css of the container.  You will also need to specify
20 the container size if it is not the body element.</b>
21
22 * @constructor
23 * Create a new Border
24 * @param {Object} config Configuration options
25  */
26 Roo.bootstrap.layout.Border = function(config){
27     config = config || {};
28     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
29     
30     
31     
32     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33         if(config[region]){
34             config[region].region = region;
35             this.addRegion(config[region]);
36         }
37     },this);
38     
39 };
40
41 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
42
43 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
44     
45     onRender : function(ctr, pos)
46     {
47         Roo.bootstrap.layout.Border.superclass.onRender.call(this,ctr,pos);
48         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
49             if(this.regions[region]){
50                 this.regions[region].onRender(this.el, pos);
51             }
52         },this);
53         
54         
55     },
56     
57     
58     /**
59      * Creates and adds a new region if it doesn't already exist.
60      * @param {String} target The target region key (north, south, east, west or center).
61      * @param {Object} config The regions config object
62      * @return {BorderLayoutRegion} The new region
63      */
64     addRegion : function(config)
65     {
66         if(!this.regions[config.region]){
67             var r = this.factory(config);
68             this.bindRegion(r);
69         }
70         return this.regions[config.region];
71     },
72
73     // private (kinda)
74     bindRegion : function(r){
75         this.regions[r.config.region] = r;
76         
77         r.on("visibilitychange",    this.layout, this);
78         r.on("paneladded",          this.layout, this);
79         r.on("panelremoved",        this.layout, this);
80         r.on("invalidated",         this.layout, this);
81         r.on("resized",             this.onRegionResized, this);
82         r.on("collapsed",           this.onRegionCollapsed, this);
83         r.on("expanded",            this.onRegionExpanded, this);
84     },
85
86     /**
87      * Performs a layout update.
88      */
89     layout : function()
90     {
91         if(this.updating) {
92             return;
93         }
94         var size = this.getViewSize();
95         var w = size.width;
96         var h = size.height;
97         var centerW = w;
98         var centerH = h;
99         var centerY = 0;
100         var centerX = 0;
101         //var x = 0, y = 0;
102
103         var rs = this.regions;
104         var north = rs["north"];
105         var south = rs["south"]; 
106         var west = rs["west"];
107         var east = rs["east"];
108         var center = rs["center"];
109         //if(this.hideOnLayout){ // not supported anymore
110             //c.el.setStyle("display", "none");
111         //}
112         if(north && north.isVisible()){
113             var b = north.getBox();
114             var m = north.getMargins();
115             b.width = w - (m.left+m.right);
116             b.x = m.left;
117             b.y = m.top;
118             centerY = b.height + b.y + m.bottom;
119             centerH -= centerY;
120             north.updateBox(this.safeBox(b));
121         }
122         if(south && south.isVisible()){
123             var b = south.getBox();
124             var m = south.getMargins();
125             b.width = w - (m.left+m.right);
126             b.x = m.left;
127             var totalHeight = (b.height + m.top + m.bottom);
128             b.y = h - totalHeight + m.top;
129             centerH -= totalHeight;
130             south.updateBox(this.safeBox(b));
131         }
132         if(west && west.isVisible()){
133             var b = west.getBox();
134             var m = west.getMargins();
135             b.height = centerH - (m.top+m.bottom);
136             b.x = m.left;
137             b.y = centerY + m.top;
138             var totalWidth = (b.width + m.left + m.right);
139             centerX += totalWidth;
140             centerW -= totalWidth;
141             west.updateBox(this.safeBox(b));
142         }
143         if(east && east.isVisible()){
144             var b = east.getBox();
145             var m = east.getMargins();
146             b.height = centerH - (m.top+m.bottom);
147             var totalWidth = (b.width + m.left + m.right);
148             b.x = w - totalWidth + m.left;
149             b.y = centerY + m.top;
150             centerW -= totalWidth;
151             east.updateBox(this.safeBox(b));
152         }
153         if(center){
154             var m = center.getMargins();
155             var centerBox = {
156                 x: centerX + m.left,
157                 y: centerY + m.top,
158                 width: centerW - (m.left+m.right),
159                 height: centerH - (m.top+m.bottom)
160             };
161             //if(this.hideOnLayout){
162                 //center.el.setStyle("display", "block");
163             //}
164             center.updateBox(this.safeBox(centerBox));
165         }
166         this.el.repaint();
167         this.fireEvent("layout", this);
168     },
169
170     // private
171     safeBox : function(box){
172         box.width = Math.max(0, box.width);
173         box.height = Math.max(0, box.height);
174         return box;
175     },
176
177     /**
178      * Adds a ContentPanel (or subclass) to this layout.
179      * @param {String} target The target region key (north, south, east, west or center).
180      * @param {Roo.ContentPanel} panel The panel to add
181      * @return {Roo.ContentPanel} The added panel
182      */
183     add : function(target, panel){
184          
185         target = target.toLowerCase();
186         return this.regions[target].add(panel);
187     },
188
189     /**
190      * Remove a ContentPanel (or subclass) to this layout.
191      * @param {String} target The target region key (north, south, east, west or center).
192      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
193      * @return {Roo.ContentPanel} The removed panel
194      */
195     remove : function(target, panel){
196         target = target.toLowerCase();
197         return this.regions[target].remove(panel);
198     },
199
200     /**
201      * Searches all regions for a panel with the specified id
202      * @param {String} panelId
203      * @return {Roo.ContentPanel} The panel or null if it wasn't found
204      */
205     findPanel : function(panelId){
206         var rs = this.regions;
207         for(var target in rs){
208             if(typeof rs[target] != "function"){
209                 var p = rs[target].getPanel(panelId);
210                 if(p){
211                     return p;
212                 }
213             }
214         }
215         return null;
216     },
217
218     /**
219      * Searches all regions for a panel with the specified id and activates (shows) it.
220      * @param {String/ContentPanel} panelId The panels id or the panel itself
221      * @return {Roo.ContentPanel} The shown panel or null
222      */
223     showPanel : function(panelId) {
224       var rs = this.regions;
225       for(var target in rs){
226          var r = rs[target];
227          if(typeof r != "function"){
228             if(r.hasPanel(panelId)){
229                return r.showPanel(panelId);
230             }
231          }
232       }
233       return null;
234    },
235
236    /**
237      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
238      * @param {Roo.state.Provider} provider (optional) An alternate state provider
239      */
240    /*
241     restoreState : function(provider){
242         if(!provider){
243             provider = Roo.state.Manager;
244         }
245         var sm = new Roo.LayoutStateManager();
246         sm.init(this, provider);
247     },
248 */
249  
250  
251     /**
252      * Adds a xtype elements to the layout.
253      * <pre><code>
254
255 layout.addxtype({
256        xtype : 'ContentPanel',
257        region: 'west',
258        items: [ .... ]
259    }
260 );
261
262 layout.addxtype({
263         xtype : 'NestedLayoutPanel',
264         region: 'west',
265         layout: {
266            center: { },
267            west: { }   
268         },
269         items : [ ... list of content panels or nested layout panels.. ]
270    }
271 );
272 </code></pre>
273      * @param {Object} cfg Xtype definition of item to add.
274      */
275     addxtype : function(cfg)
276     {
277         // basically accepts a pannel...
278         // can accept a layout region..!?!?
279         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
280         
281         
282         // theory?  children can only be panels??
283         
284         //if (!cfg.xtype.match(/Panel$/)) {
285         //    return false;
286         //}
287         var ret = false;
288         
289         if (typeof(cfg.region) == 'undefined') {
290             Roo.log("Failed to add Panel, region was not set");
291             Roo.log(cfg);
292             return false;
293         }
294         var region = cfg.region;
295         delete cfg.region;
296         
297           
298         var xitems = [];
299         if (cfg.items) {
300             xitems = cfg.items;
301             delete cfg.items;
302         }
303         var nb = false;
304         
305         switch(cfg.xtype) 
306         {
307             case 'Content':  // ContentPanel (el, cfg)
308             case 'Scroll':  // ContentPanel (el, cfg)
309             case 'View': 
310                 cfg.autoCreate = true;
311                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
312                 //} else {
313                 //    var el = this.el.createChild();
314                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
315                 //}
316                 
317                 this.add(region, ret);
318                 break;
319             
320             /*
321             case 'TreePanel': // our new panel!
322                 cfg.el = this.el.createChild();
323                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
324                 this.add(region, ret);
325                 break;
326             */
327             
328             case 'Nest': 
329                 // create a new Layout (which is  a Border Layout...
330                 
331                 var clayout = cfg.layout;
332                 clayout.el  = this.el.createChild();
333                 clayout.items   = clayout.items  || [];
334                 
335                 delete cfg.layout;
336                 
337                 // replace this exitems with the clayout ones..
338                 xitems = clayout.items;
339                  
340                 // force background off if it's in center...
341                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
342                     cfg.background = false;
343                 }
344                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
345                 
346                 
347                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
348                 //console.log('adding nested layout panel '  + cfg.toSource());
349                 this.add(region, ret);
350                 nb = {}; /// find first...
351                 break;
352             
353             case 'Grid':
354                 
355                 // needs grid and region
356                 
357                 //var el = this.getRegion(region).el.createChild();
358                 var el = this.el.createChild();
359                 // create the grid first...
360                 cfg.grid.container = el;
361                 cfg.grid.scrollBody = true;
362                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
363                 
364                 
365                 if (region == 'center' && this.active ) {
366                     cfg.background = false;
367                 }
368                 
369                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
370                 
371                 this.add(region, ret);
372                 
373                 if (cfg.background) {
374                     // render grid on panel activation (if panel background)
375                     ret.on('activate', function(gp) {
376                         if (!gp.grid.rendered) {
377                             gp.grid.render(gp.grid.getGridEl());
378                         }
379                     });
380                 } else {
381                     cfg.grid.render(cfg.grid.getGridEl());
382                 }
383                 break;
384            
385            
386             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
387                 // it was the old xcomponent building that caused this before.
388                 // espeically if border is the top element in the tree.
389                 ret = this;
390                 break; 
391                 
392                     
393                 
394                 
395                 
396             default:
397                 /*
398                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
399                     
400                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
401                     this.add(region, ret);
402                 } else {
403                 */
404                     Roo.log(cfg);
405                     throw "Can not add '" + cfg.xtype + "' to Border";
406                     return null;
407              
408                                 
409              
410         }
411         this.beginUpdate();
412         // add children..
413         var region = '';
414         var abn = {};
415         Roo.each(xitems, function(i)  {
416             region = nb && i.region ? i.region : false;
417             
418             var add = ret.addxtype(i);
419            
420             if (region) {
421                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
422                 if (!i.background) {
423                     abn[region] = nb[region] ;
424                 }
425             }
426             
427         });
428         this.endUpdate();
429
430         // make the last non-background panel active..
431         //if (nb) { Roo.log(abn); }
432         if (nb) {
433             
434             for(var r in abn) {
435                 region = this.getRegion(r);
436                 if (region) {
437                     // tried using nb[r], but it does not work..
438                      
439                     region.showPanel(abn[r]);
440                    
441                 }
442             }
443         }
444         return ret;
445         
446     },
447     
448     
449 // private
450     factory : function(cfg)
451     {
452         
453         var validRegions = Roo.bootstrap.layout.Border.regions;
454
455         var target = cfg.region;
456         cfg.mgr = this;
457         
458         var r = Roo.bootstrap.layout;
459         Roo.log(target);
460         switch(target){
461             case "north":
462                 return new r.North(cfg);
463             case "south":
464                 return new r.South(cfg);
465             case "east":
466                 return new r.East(cfg);
467             case "west":
468                 return new r.West(cfg);
469             case "center":
470                 return new r.Center(cfg);
471         }
472         throw 'Layout region "'+target+'" not supported.';
473     }
474     
475     
476 });
477