/*! * Ext JS Library 3.3.0 * Copyright(c) 2006-2010 Ext JS, Inc. * licensing@extjs.com * http://www.extjs.com/license */ /** * @class Ext.layout.BorderLayout * @extends Ext.layout.ContainerLayout *This is a multi-pane, application-oriented UI layout style that supports multiple * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.
*This class is intended to be extended or created via the layout:'border' * {@link Ext.Container#layout} config, and should generally not need to be created directly * via the new keyword.
*BorderLayout does not have any direct config options (other than inherited ones). * All configuration options available for customizing the BorderLayout are at the * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion} * levels.
*Example usage:
**var myBorderPanel = new Ext.Panel({ {@link Ext.Component#renderTo renderTo}: document.body, {@link Ext.BoxComponent#width width}: 700, {@link Ext.BoxComponent#height height}: 500, {@link Ext.Panel#title title}: 'Border Layout', {@link Ext.Container#layout layout}: 'border', {@link Ext.Container#items items}: [{ {@link Ext.Panel#title title}: 'South Region is resizable', {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south', // position for region {@link Ext.BoxComponent#height height}: 100, {@link Ext.layout.BorderLayout.Region#split split}: true, // enable resizing {@link Ext.SplitBar#minSize minSize}: 75, // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50} {@link Ext.SplitBar#maxSize maxSize}: 150, {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5' },{ // xtype: 'panel' implied by default {@link Ext.Panel#title title}: 'West Region is collapsible', {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west', {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5', {@link Ext.BoxComponent#width width}: 200, {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true, // make collapsible {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed {@link Ext.Component#id id}: 'west-region-container', {@link Ext.Container#layout layout}: 'fit', {@link Ext.Panel#unstyled unstyled}: true },{ {@link Ext.Panel#title title}: 'Center Region', {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center', // center region is required, no width/height specified {@link Ext.Component#xtype xtype}: 'container', {@link Ext.Container#layout layout}: 'fit', {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0' }] });
Notes:
*/ Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, { // private monitorResize:true, // private rendered : false, type: 'border', targetCls: 'x-border-layout-ct', getLayoutTargetSize : function() { var target = this.container.getLayoutTarget(); return target ? target.getViewSize() : {}; }, // private onLayout : function(ct, target){ var collapsed, i, c, pos, items = ct.items.items, len = items.length; if(!this.rendered){ collapsed = []; for(i = 0; i < len; i++) { c = items[i]; pos = c.region; if(c.collapsed){ collapsed.push(c); } c.collapsed = false; if(!c.rendered){ c.render(target, i); c.getPositionEl().addClass('x-border-panel'); } this[pos] = pos != 'center' && c.split ? new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) : new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos); this[pos].render(target, c); } this.rendered = true; } var size = this.getLayoutTargetSize(); if(size.width < 20 || size.height < 20){ // display none? if(collapsed){ this.restoreCollapsed = collapsed; } return; }else if(this.restoreCollapsed){ collapsed = this.restoreCollapsed; delete this.restoreCollapsed; } var w = size.width, h = size.height, centerW = w, centerH = h, centerY = 0, centerX = 0, n = this.north, s = this.south, west = this.west, e = this.east, c = this.center, b, m, totalWidth, totalHeight; if(!c && Ext.layout.BorderLayout.WARN !== false){ throw 'No center region defined in BorderLayout ' + ct.id; } if(n && n.isVisible()){ b = n.getSize(); m = n.getMargins(); b.width = w - (m.left+m.right); b.x = m.left; b.y = m.top; centerY = b.height + b.y + m.bottom; centerH -= centerY; n.applyLayout(b); } if(s && s.isVisible()){ b = s.getSize(); m = s.getMargins(); b.width = w - (m.left+m.right); b.x = m.left; totalHeight = (b.height + m.top + m.bottom); b.y = h - totalHeight + m.top; centerH -= totalHeight; s.applyLayout(b); } if(west && west.isVisible()){ b = west.getSize(); m = west.getMargins(); b.height = centerH - (m.top+m.bottom); b.x = m.left; b.y = centerY + m.top; totalWidth = (b.width + m.left + m.right); centerX += totalWidth; centerW -= totalWidth; west.applyLayout(b); } if(e && e.isVisible()){ b = e.getSize(); m = e.getMargins(); b.height = centerH - (m.top+m.bottom); totalWidth = (b.width + m.left + m.right); b.x = w - totalWidth + m.left; b.y = centerY + m.top; centerW -= totalWidth; e.applyLayout(b); } if(c){ m = c.getMargins(); var centerBox = { x: centerX + m.left, y: centerY + m.top, width: centerW - (m.left+m.right), height: centerH - (m.top+m.bottom) }; c.applyLayout(centerBox); } if(collapsed){ for(i = 0, len = collapsed.length; i < len; i++){ collapsed[i].collapse(false); } } if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue target.repaint(); } // Putting a border layout into an overflowed container is NOT correct and will make a second layout pass necessary. if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) { var ts = this.getLayoutTargetSize(); if (ts.width != size.width || ts.height != size.height){ this.adjustmentPass = true; this.onLayout(ct, target); } } delete this.adjustmentPass; }, destroy: function() { var r = ['north', 'south', 'east', 'west'], i, region; for (i = 0; i < r.length; i++) { region = this[r[i]]; if(region){ if(region.destroy){ region.destroy(); }else if (region.split){ region.split.destroy(true); } } } Ext.layout.BorderLayout.superclass.destroy.call(this); } /** * @property activeItem * @hide */ }); /** * @class Ext.layout.BorderLayout.Region **
- Any container using the BorderLayout must have a child item with region:'center'. * The child item in the center region will always be resized to fill the remaining space not used by * the other regions in the layout.
*- Any child items with a region of west or east must have width defined * (an integer representing the number of pixels that the region should take up).
*- Any child items with a region of north or south must have height defined.
*- The regions of a BorderLayout are fixed at render time and thereafter, its child Components may not be removed or added. To add/remove * Components within a BorderLayout, have them wrapped by an additional Container which is directly * managed by the BorderLayout. If the region is to be collapsible, the Container used directly * by the BorderLayout manager should be a Panel. In the following example a Container (an Ext.Panel) * is added to the west region: *
**wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container'); wrc.{@link Ext.Panel#removeAll removeAll}(); wrc.{@link Ext.Container#add add}({ title: 'Added Panel', html: 'Some content' }); wrc.{@link Ext.Container#doLayout doLayout}(); *
- To reference a {@link Ext.layout.BorderLayout.Region Region}: *
**wr = myBorderPanel.layout.west; *
This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer * within the layout. Each region has its own {@link Ext.layout.ContainerLayout layout} that is * independent of other regions and the containing BorderLayout, and can be any of the * {@link Ext.layout.ContainerLayout valid Ext layout types}.
*Region size is managed automatically and cannot be changed by the user -- for * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.
* @constructor * Create a new Region. * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region. * @param {Object} config The configuration options * @param {String} position The region position. Valid values are: north, south, * east, west and center. Every {@link Ext.layout.BorderLayout BorderLayout} * must have a center region for the primary content -- all other regions are optional. */ Ext.layout.BorderLayout.Region = function(layout, config, pos){ Ext.apply(this, config); this.layout = layout; this.position = pos; this.state = {}; if(typeof this.margins == 'string'){ this.margins = this.layout.parseMargins(this.margins); } this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins); if(this.collapsible){ if(typeof this.cmargins == 'string'){ this.cmargins = this.layout.parseMargins(this.cmargins); } if(this.collapseMode == 'mini' && !this.cmargins){ this.cmargins = {left:0,top:0,right:0,bottom:0}; }else{ this.cmargins = Ext.applyIf(this.cmargins || {}, pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins); } } }; Ext.layout.BorderLayout.Region.prototype = { /** * @cfg {Boolean} animFloat * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated * panel that will close again once the user mouses out of that panel (or clicks out if * {@link #autoHide} = false). Setting {@link #animFloat} = false will * prevent the open and close of these floated panels from being animated (defaults to true). */ /** * @cfg {Boolean} autoHide * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated * panel. If autoHide = true, the panel will automatically hide after the user mouses * out of the panel. If autoHide = false, the panel will continue to display until the * user clicks outside of the panel (defaults to true). */ /** * @cfg {String} collapseMode * collapseMode supports two configuration values:**
- undefined (default)
*By default, {@link #collapsible} * regions are collapsed by clicking the expand/collapse tool button that renders into the region's * title bar.- 'mini'
*Optionally, when collapseMode is set to * 'mini' the region's split bar will also display a small collapse button in the center of * the bar. In 'mini' mode the region will collapse to a thinner bar than in normal mode. *Note: if a collapsible region does not have a title bar, then set collapseMode = * 'mini' and {@link #split} = true in order for the region to be {@link #collapsible} * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.
*See also {@link #cmargins}.
*/ /** * @cfg {Object} margins * An object containing margins to apply to the region when in the expanded state in the * format:*{ top: (top margin), right: (right margin), bottom: (bottom margin), left: (left margin) }
May also be a string containing space-separated, numeric margin values. The order of the * sides associated with each value matches the way CSS processes margin values:
***
- If there is only one value, it applies to all sides.
*- If there are two values, the top and bottom borders are set to the first value and the * right and left are set to the second.
*- If there are three values, the top is set to the first value, the left and right are set * to the second, and the bottom is set to the third.
*- If there are four values, they apply to the top, right, bottom, and left, respectively.
*Defaults to:
*/ /** * @cfg {Object} cmargins * An object containing margins to apply to the region when in the collapsed state in the * format:* {top:0, right:0, bottom:0, left:0} *
*{ top: (top margin), right: (right margin), bottom: (bottom margin), left: (left margin) }
May also be a string containing space-separated, numeric margin values. The order of the * sides associated with each value matches the way CSS processes margin values.
*
true to allow the user to collapse this region (defaults to false). If * true, an expand/collapse tool button will automatically be rendered into the title * bar of the region, otherwise the button will not be shown.
*Note: that a title bar is required to display the collapse/expand toggle button -- if * no title is specified for the region's panel, the region will only be collapsible if * {@link #collapseMode} = 'mini' and {@link #split} = true. */ collapsible : false,
/** * @cfg {Boolean} split *true to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to * resize the regions dynamically. Defaults to false creating a * {@link Ext.layout.BorderLayout.Region Region}.
Notes:
The minimum allowable width in pixels for this region (defaults to 50). * maxWidth may also be specified.
Note: setting the {@link Ext.SplitBar#minSize minSize} / * {@link Ext.SplitBar#maxSize maxSize} supersedes any specified * minWidth / maxWidth.
*/ minWidth:50, /** * @cfg {Number} minHeight * The minimum allowable height in pixels for this region (defaults to 50) * maxHeight may also be specified.Note: setting the {@link Ext.SplitBar#minSize minSize} / * {@link Ext.SplitBar#maxSize maxSize} supersedes any specified * minHeight / maxHeight.
*/ minHeight:50, // private defaultMargins : {left:0,top:0,right:0,bottom:0}, // private defaultNSCMargins : {left:5,top:5,right:5,bottom:5}, // private defaultEWCMargins : {left:5,top:0,right:5,bottom:0}, floatingZIndex: 100, /** * True if this region is collapsed. Read-only. * @type Boolean * @property */ isCollapsed : false, /** * This region's panel. Read-only. * @type Ext.Panel * @property panel */ /** * This region's layout. Read-only. * @type Layout * @property layout */ /** * This region's layout position (north, south, east, west or center). Read-only. * @type String * @property position */ // private render : function(ct, p){ this.panel = p; p.el.enableDisplayMode(); this.targetEl = ct; this.el = p.el; var gs = p.getState, ps = this.position; p.getState = function(){ return Ext.apply(gs.call(p) || {}, this.state); }.createDelegate(this); if(ps != 'center'){ p.allowQueuedExpand = false; p.on({ beforecollapse: this.beforeCollapse, collapse: this.onCollapse, beforeexpand: this.beforeExpand, expand: this.onExpand, hide: this.onHide, show: this.onShow, scope: this }); if(this.collapsible || this.floatable){ p.collapseEl = 'el'; p.slideAnchor = this.getSlideAnchor(); } if(p.tools && p.tools.toggle){ p.tools.toggle.addClass('x-tool-collapse-'+ps); p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over'); } } }, // private getCollapsedEl : function(){ if(!this.collapsedEl){ if(!this.toolTemplate){ var tt = new Ext.Template( 'This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that * has a built-in {@link Ext.SplitBar} for user resizing of regions. The movement of the split bar * is configurable to move either {@link #tickSize smooth or incrementally}.
* @constructor * Create a new SplitRegion. * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region. * @param {Object} config The configuration options * @param {String} position The region position. Valid values are: north, south, east, west and center. Every * BorderLayout must have a center region for the primary content -- all other regions are optional. */ Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){ Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos); // prevent switch this.applyLayout = this.applyFns[pos]; }; Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, { /** * @cfg {Number} tickSize * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}. * By default, the {@link Ext.SplitBar SplitBar} moves smoothly. */ /** * @cfg {String} splitTip * The tooltip to display when the user hovers over a * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar * (defaults to "Drag to resize."). Only applies if * {@link #useSplitTips} = true. */ splitTip : "Drag to resize.", /** * @cfg {String} collapsibleSplitTip * The tooltip to display when the user hovers over a * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar * (defaults to "Drag to resize. Double click to hide."). Only applies if * {@link #useSplitTips} = true. */ collapsibleSplitTip : "Drag to resize. Double click to hide.", /** * @cfg {Boolean} useSplitTips * true to display a tooltip when the user hovers over a region's split bar * (defaults to false). The tooltip text will be the value of either * {@link #splitTip} or {@link #collapsibleSplitTip} as appropriate. */ useSplitTips : false, // private splitSettings : { north : { orientation: Ext.SplitBar.VERTICAL, placement: Ext.SplitBar.TOP, maxFn : 'getVMaxSize', minProp: 'minHeight', maxProp: 'maxHeight' }, south : { orientation: Ext.SplitBar.VERTICAL, placement: Ext.SplitBar.BOTTOM, maxFn : 'getVMaxSize', minProp: 'minHeight', maxProp: 'maxHeight' }, east : { orientation: Ext.SplitBar.HORIZONTAL, placement: Ext.SplitBar.RIGHT, maxFn : 'getHMaxSize', minProp: 'minWidth', maxProp: 'maxWidth' }, west : { orientation: Ext.SplitBar.HORIZONTAL, placement: Ext.SplitBar.LEFT, maxFn : 'getHMaxSize', minProp: 'minWidth', maxProp: 'maxWidth' } }, // private applyFns : { west : function(box){ if(this.isCollapsed){ return this.applyLayoutCollapsed(box); } var sd = this.splitEl.dom, s = sd.style; this.panel.setPosition(box.x, box.y); var sw = sd.offsetWidth; s.left = (box.x+box.width-sw)+'px'; s.top = (box.y)+'px'; s.height = Math.max(0, box.height)+'px'; this.panel.setSize(box.width-sw, box.height); }, east : function(box){ if(this.isCollapsed){ return this.applyLayoutCollapsed(box); } var sd = this.splitEl.dom, s = sd.style; var sw = sd.offsetWidth; this.panel.setPosition(box.x+sw, box.y); s.left = (box.x)+'px'; s.top = (box.y)+'px'; s.height = Math.max(0, box.height)+'px'; this.panel.setSize(box.width-sw, box.height); }, north : function(box){ if(this.isCollapsed){ return this.applyLayoutCollapsed(box); } var sd = this.splitEl.dom, s = sd.style; var sh = sd.offsetHeight; this.panel.setPosition(box.x, box.y); s.left = (box.x)+'px'; s.top = (box.y+box.height-sh)+'px'; s.width = Math.max(0, box.width)+'px'; this.panel.setSize(box.width, box.height-sh); }, south : function(box){ if(this.isCollapsed){ return this.applyLayoutCollapsed(box); } var sd = this.splitEl.dom, s = sd.style; var sh = sd.offsetHeight; this.panel.setPosition(box.x, box.y+sh); s.left = (box.x)+'px'; s.top = (box.y)+'px'; s.width = Math.max(0, box.width)+'px'; this.panel.setSize(box.width, box.height-sh); } }, // private render : function(ct, p){ Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p); var ps = this.position; this.splitEl = ct.createChild({ cls: "x-layout-split x-layout-split-"+ps, html: " ", id: this.panel.id + '-xsplit' }); if(this.collapseMode == 'mini'){ this.miniSplitEl = this.splitEl.createChild({ cls: "x-layout-mini x-layout-mini-"+ps, html: " " }); this.miniSplitEl.addClassOnOver('x-layout-mini-over'); this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true}); } var s = this.splitSettings[ps]; this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation); this.split.tickSize = this.tickSize; this.split.placement = s.placement; this.split.getMaximumSize = this[s.maxFn].createDelegate(this); this.split.minSize = this.minSize || this[s.minProp]; this.split.on("beforeapply", this.onSplitMove, this); this.split.useShim = this.useShim === true; this.maxSize = this.maxSize || this[s.maxProp]; if(p.hidden){ this.splitEl.hide(); } if(this.useSplitTips){ this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip; } if(this.collapsible){ this.splitEl.on("dblclick", this.onCollapseClick, this); } }, //docs inherit from superclass getSize : function(){ if(this.isCollapsed){ return this.collapsedEl.getSize(); } var s = this.panel.getSize(); if(this.position == 'north' || this.position == 'south'){ s.height += this.splitEl.dom.offsetHeight; }else{ s.width += this.splitEl.dom.offsetWidth; } return s; }, // private getHMaxSize : function(){ var cmax = this.maxSize || 10000; var center = this.layout.center; return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth()); }, // private getVMaxSize : function(){ var cmax = this.maxSize || 10000; var center = this.layout.center; return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight()); }, // private onSplitMove : function(split, newSize){ var s = this.panel.getSize(); this.lastSplitSize = newSize; if(this.position == 'north' || this.position == 'south'){ this.panel.setSize(s.width, newSize); this.state.height = newSize; }else{ this.panel.setSize(newSize, s.height); this.state.width = newSize; } this.layout.layout(); this.panel.saveState(); return false; }, /** * Returns a reference to the split bar in use by this region. * @return {Ext.SplitBar} The split bar */ getSplitBar : function(){ return this.split; }, // inherit docs destroy : function() { Ext.destroy(this.miniSplitEl, this.split, this.splitEl); Ext.layout.BorderLayout.SplitRegion.superclass.destroy.call(this); } }); Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;