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