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... - might need to check if this is fixed?
372                 // it was the old xcomponent building that caused this before.
373                 // espeically if border is the top element in the tree.
374                 ret = this;
375                 break; 
376                 
377                     
378                 
379                 
380                 
381             default:
382                 /*
383                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
384                     
385                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
386                     this.add(region, ret);
387                 } else {
388                 */
389                     Roo.log(cfg);
390                     throw "Can not add '" + cfg.xtype + "' to Border";
391                     return null;
392              
393                                 
394              
395         }
396         this.beginUpdate();
397         // add children..
398         var region = '';
399         var abn = {};
400         Roo.each(xitems, function(i)  {
401             region = nb && i.region ? i.region : false;
402             
403             var add = ret.addxtype(i);
404            
405             if (region) {
406                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
407                 if (!i.background) {
408                     abn[region] = nb[region] ;
409                 }
410             }
411             
412         });
413         this.endUpdate();
414
415         // make the last non-background panel active..
416         //if (nb) { Roo.log(abn); }
417         if (nb) {
418             
419             for(var r in abn) {
420                 region = this.getRegion(r);
421                 if (region) {
422                     // tried using nb[r], but it does not work..
423                      
424                     region.showPanel(abn[r]);
425                    
426                 }
427             }
428         }
429         return ret;
430         
431     },
432     
433     
434 // private
435     factory : function(cfg)
436     {
437         
438         var validRegions = Roo.bootstrap.layout.Border.regions;
439
440         var target = cfg.region;
441         cfg.mgr = this;
442         
443         var r = Roo.bootstrap.layout;
444         Roo.log(target);
445         switch(target){
446             case "north":
447                 return new r.North(cfg);
448             case "south":
449                 return new r.South(cfg);
450             case "east":
451                 return new r.East(cfg);
452             case "west":
453                 return new r.West(cfg);
454             case "center":
455                 return new r.Center(cfg);
456         }
457         throw 'Layout region "'+target+'" not supported.';
458     }
459     
460     
461 });
462