X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/c930e9176a5a85509c5b0230e2bff5c22a591432..530ef4b6c5b943cfa68b779d11cf7de29aa878bf:/src/widgets/Container.js diff --git a/src/widgets/Container.js b/src/widgets/Container.js index 3a14a741..2d22c196 100644 --- a/src/widgets/Container.js +++ b/src/widgets/Container.js @@ -1,6 +1,6 @@ /*! - * Ext JS Library 3.0.0 - * Copyright(c) 2006-2009 Ext JS, LLC + * Ext JS Library 3.2.1 + * Copyright(c) 2006-2010 Ext JS, Inc. * licensing@extjs.com * http://www.extjs.com/license */ @@ -13,12 +13,12 @@ *

The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}. * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight * Container to be encapsulated by an HTML element to your specifications by using the - * {@link Ext.Component#autoEl autoEl} config option. This is a useful technique when creating + * {@link Ext.Component#autoEl autoEl} config option. This is a useful technique when creating * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels} * for example.

* *

The code below illustrates both how to explicitly create a Container, and how to implicitly - * create one using the 'container' xtype:


+ * create one using the 'container' xtype:

 // explicitly create a Container
 var embeddedColumns = new Ext.Container({
     autoEl: 'div',  // This is the default
@@ -63,7 +63,7 @@ var embeddedColumns = new Ext.Container({
  * Container, and does not apply any sizing at all.

*

A common mistake is when a developer neglects to specify a * {@link #layout} (e.g. widgets like GridPanels or - * TreePanels are added to Containers for which no {@link #layout} + * TreePanels are added to Containers for which no {@link #layout} * has been specified). If a Container is left to use the default * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its * child components will be resized, or changed in any way when the Container @@ -89,12 +89,11 @@ myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid); *

Overnesting is a common problem. * An example of overnesting occurs when a GridPanel is added to a TabPanel * by wrapping the GridPanel inside a wrapping Panel (that has no - * {@link #layout} specified) and then add that wrapping Panel + * {@link #layout} specified) and then add that wrapping Panel * to the TabPanel. The point to realize is that a GridPanel is a * Component which can be added directly to a Container. If the wrapping Panel - * has no {@link #layout} configuration, then the overnested + * has no {@link #layout} configuration, then the overnested * GridPanel will not be sized as expected.

-

* *

Adding via remote configuration

* @@ -122,7 +121,7 @@ Ext.Ajax.request({ });
*

The server script needs to return an executable Javascript statement which, when processed - * using eval(), will return either a config object with an {@link Ext.Component#xtype xtype}, + * using eval(), will return either a config object with an {@link Ext.Component#xtype xtype}, * or an instantiated Component. The server might return this for example:


 (function() {
     function formatDate(value){
@@ -162,10 +161,10 @@ Ext.Ajax.request({
     return grid;  // return instantiated component
 })();
 
- *

When the above code fragment is passed through the eval function in the success handler + *

When the above code fragment is passed through the eval function in the success handler * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function * runs, and returns the instantiated grid component.

- *

Note: since the code above is generated by a server script, the baseParams for + *

Note: since the code above is generated by a server script, the baseParams for * the Store, the metadata to allow generation of the Record layout, and the ColumnModel * can all be generated into the code since these are all known on the server.

* @@ -180,11 +179,12 @@ Ext.Container = Ext.extend(Ext.BoxComponent, { */ /** * @cfg {String/Object} layout - * When creating complex UIs, it is important to remember that sizing and - * positioning of child items is the responsibility of the Container's - * layout manager. If you expect child items to be sized in response to - * user interactions, you must specify a layout manager which - * creates and manages the type of layout you have in mind. For example:

+     * 

*Important: In order for child items to be correctly sized and + * positioned, typically a layout manager must be specified through + * the layout configuration option.

+ *

The sizing and positioning of child {@link items} is the responsibility of + * the Container's layout manager which creates and manages the type of layout + * you have in mind. For example:


 new Ext.Window({
     width:300, height: 300,
     layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')
@@ -193,13 +193,17 @@ new Ext.Window({
     }]
 }).show();
      * 
- *

Omitting the {@link #layout} config means that the - * {@link Ext.layout.ContainerLayout default layout manager} will be used which does - * nothing but render child components sequentially into the Container (no sizing or - * positioning will be performed in this situation).

- *

The layout manager class for this container may be specified as either as an - * Object or as a String:

- *
* - *
  • type
  • + *
  • type
  • *

    The layout type to be used for this container. If not specified, * a default {@link Ext.layout.ContainerLayout} will be created and used.

    - *

    Valid layout type values are:

    + *

    Valid layout type values are:

    *
    * *
  • Layout specific configuration properties
  • *

    Additional layout specific configuration properties may also be * specified. For complete details regarding the valid config options for - * each layout type, see the layout class corresponding to the type + * each layout type, see the layout class corresponding to the type * specified.

    * * @@ -251,13 +255,13 @@ layoutConfig: { align: 'left' } - *
  • layout
  • - *

    The layout type to be used for this container (see list + *

  • layout
  • + *

    The layout type to be used for this container (see list * of valid layout type values above).


    - *
  • {@link #layoutConfig}
  • + *
  • {@link #layoutConfig}
  • *

    Additional layout specific configuration properties. For complete * details regarding the valid config options for each layout type, see the - * layout class corresponding to the layout specified.

    + * layout class corresponding to the layout specified.

    * */ /** @@ -268,12 +272,12 @@ layoutConfig: { */ /** * @cfg {Boolean/Number} bufferResize - * When set to true (100 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer + * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers - * with a large quantity of sub-components for which frequent layout calls would be expensive. + * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to 50. */ - bufferResize: 100, - + bufferResize: 50, + /** * @cfg {String/Number} activeItem * A string component id or the numeric index of the component that should be initially activated within the @@ -284,7 +288,7 @@ layoutConfig: { */ /** * @cfg {Object/Array} items - *
    ** IMPORTANT: be sure to specify a {@link #layout} ! **
    + *
    ** IMPORTANT: be sure to {@link #layout specify a layout} if needed ! **
    *

    A single item, or an array of child Components to be added to this container, * for example:

    *
    
    @@ -321,14 +325,18 @@ layout: 'anchor', // specify a layout!
          * 
          */
         /**
    -     * @cfg {Object} defaults
    -     * 

    A config object that will be applied to all components added to this container either via the {@link #items} - * config or via the {@link #add} or {@link #insert} methods. The defaults config can contain any - * number of name/value property pairs to be added to each item, and should be valid for the types of items - * being added to the container. For example, to automatically apply padding to the body of each of a set of - * contained {@link Ext.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}.


    - *

    Note: defaults will not be applied to config objects if the option is already specified. - * For example:

    
    +     * @cfg {Object|Function} defaults
    +     * 

    This option is a means of applying default settings to all added items whether added through the {@link #items} + * config or via the {@link #add} or {@link #insert} methods.

    + *

    If an added item is a config object, and not an instantiated Component, then the default properties are + * unconditionally applied. If the added item is an instantiated Component, then the default properties are + * applied conditionally so as not to override existing properties in the item.

    + *

    If the defaults option is specified as a function, then the function will be called using this Container as the + * scope (this reference) and passing the added item as the first parameter. Any resulting object + * from that call is then applied to the item as default properties.

    + *

    For example, to automatically apply padding to the body of each of a set of + * contained {@link Ext.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}.

    + *

    Usage:

    
     defaults: {               // defaults are applied to items, not the container
         autoScroll:true
     },
    @@ -366,11 +374,24 @@ items: [
         /** @cfg {String} defaultType
          * 

    The default {@link Ext.Component xtype} of child Components to create in this Container when * a child item is specified as a raw configuration object, rather than as an instantiated Component.

    - *

    Defaults to 'panel', except {@link Ext.menu.Menu} which defaults to 'menuitem', - * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to 'button'.

    + *

    Defaults to 'panel', except {@link Ext.menu.Menu} which defaults to 'menuitem', + * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to 'button'.

    */ defaultType : 'panel', + /** @cfg {String} resizeEvent + * The event to listen to for resizing in layouts. Defaults to 'resize'. + */ + resizeEvent: 'resize', + + /** + * @cfg {Array} bubbleEvents + *

    An array of events that, when fired, should be bubbled to any parent container. + * See {@link Ext.util.Observable#enableBubble}. + * Defaults to ['add', 'remove']. + */ + bubbleEvents: ['add', 'remove'], + // private initComponent : function(){ Ext.Container.superclass.initComponent.call(this); @@ -419,8 +440,6 @@ items: [ 'remove' ); - this.enableBubble('add', 'remove'); - /** * The collection of components in this container as a {@link Ext.util.MixedCollection} * @type MixedCollection @@ -429,11 +448,7 @@ items: [ var items = this.items; if(items){ delete this.items; - if(Ext.isArray(items) && items.length > 0){ - this.add.apply(this, items); - }else{ - this.add(items); - } + this.add(items); } }, @@ -450,34 +465,41 @@ items: [ if(this.layout && this.layout != layout){ this.layout.setContainer(null); } - this.initItems(); this.layout = layout; + this.initItems(); layout.setContainer(this); }, - // private - render : function(){ - Ext.Container.superclass.render.apply(this, arguments); - if(this.layout){ - if(Ext.isObject(this.layout) && !this.layout.layout){ - this.layoutConfig = this.layout; - this.layout = this.layoutConfig.type; - } - if(typeof this.layout == 'string'){ - this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig); - } - this.setLayout(this.layout); + afterRender: function(){ + // Render this Container, this should be done before setLayout is called which + // will hook onResize + Ext.Container.superclass.afterRender.call(this); + if(!this.layout){ + this.layout = 'auto'; + } + if(Ext.isObject(this.layout) && !this.layout.layout){ + this.layoutConfig = this.layout; + this.layout = this.layoutConfig.type; + } + if(Ext.isString(this.layout)){ + this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig); + } + this.setLayout(this.layout); - if(this.activeItem !== undefined){ - var item = this.activeItem; - delete this.activeItem; - this.layout.setActiveItem(item); - } + // If a CardLayout, the active item set + if(this.activeItem !== undefined){ + var item = this.activeItem; + delete this.activeItem; + this.layout.setActiveItem(item); } + + // If we have no ownerCt, render and size all children if(!this.ownerCt){ - // force a layout if no ownerCt is set this.doLayout(false, true); } + + // This is a manually configured flag set by users in conjunction with renderTo. + // Not to be confused with the flag by the same name used in Layouts. if(this.monitorResize === true){ Ext.EventManager.onWindowResize(this.doLayout, this, [false]); } @@ -510,10 +532,10 @@ items: [ * *

    Notes : *

      - *
    • If the Container is already rendered when add + *
    • If the Container is already rendered when add * is called, you may need to call {@link #doLayout} to refresh the view which causes * any unrendered child Components to be rendered. This is required so that you can - * add multiple child components if needed while only refreshing the layout + * add multiple child components if needed while only refreshing the layout * once. For example:
      
       var tb = new {@link Ext.Toolbar}();
       tb.render(document.body);  // toolbar is rendered
      @@ -525,32 +547,49 @@ tb.{@link #doLayout}();             // refresh the layout
            * may not be removed or added.  See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
            * for more details.
    • *
    - * @param {Object/Array} component - *

    Either a single component or an Array of components to add. See + * @param {...Object/Array} component + *

    Either one or more Components to add or an Array of Components to add. See * {@link #items} for additional information.

    - * @param {Object} (Optional) component_2 - * @param {Object} (Optional) component_n - * @return {Ext.Component} component The Component (or config object) that was added. + * @return {Ext.Component/Array} The Components that were added. */ add : function(comp){ this.initItems(); var args = arguments.length > 1; if(args || Ext.isArray(comp)){ + var result = []; Ext.each(args ? arguments : comp, function(c){ - this.add(c); + result.push(this.add(c)); }, this); - return; + return result; } var c = this.lookupComponent(this.applyDefaults(comp)); - var pos = this.items.length; - if(this.fireEvent('beforeadd', this, c, pos) !== false && this.onBeforeAdd(c) !== false){ + var index = this.items.length; + if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){ this.items.add(c); - c.ownerCt = this; - this.fireEvent('add', this, c, pos); + // *onAdded + c.onAdded(this, index); + this.onAdd(c); + this.fireEvent('add', this, c, index); } return c; }, + onAdd : function(c){ + // Empty template method + }, + + // private + onAdded : function(container, pos) { + //overridden here so we can cascade down, not worth creating a template method. + this.ownerCt = container; + this.initRef(); + //initialize references for child items + this.cascade(function(c){ + c.initRef(); + }); + this.fireEvent('added', this, container, pos); + }, + /** * Inserts a Component into this Container at a specified index. Fires the * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the @@ -573,20 +612,21 @@ tb.{@link #doLayout}(); // refresh the layout this.initItems(); var a = arguments, len = a.length; if(len > 2){ + var result = []; for(var i = len-1; i >= 1; --i) { - this.insert(index, a[i]); + result.push(this.insert(index, a[i])); } - return; + return result; } var c = this.lookupComponent(this.applyDefaults(comp)); - - if(c.ownerCt == this && this.items.indexOf(c) < index){ - --index; - } - + index = Math.min(index, this.items.length); if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){ + if(c.ownerCt == this){ + this.items.remove(c); + } this.items.insert(index, c); - c.ownerCt = this; + c.onAdded(this, index); + this.onAdd(c); this.fireEvent('add', this, c, index); } return c; @@ -594,14 +634,18 @@ tb.{@link #doLayout}(); // refresh the layout // private applyDefaults : function(c){ - if(this.defaults){ - if(typeof c == 'string'){ + var d = this.defaults; + if(d){ + if(Ext.isFunction(d)){ + d = d.call(this, c); + } + if(Ext.isString(c)){ c = Ext.ComponentMgr.get(c); - Ext.apply(c, this.defaults); + Ext.apply(c, d); }else if(!c.events){ - Ext.applyIf(c, this.defaults); + Ext.applyIf(c, d); }else{ - Ext.apply(c, this.defaults); + Ext.apply(c, d); } } return c; @@ -629,19 +673,35 @@ tb.{@link #doLayout}(); // refresh the layout this.initItems(); var c = this.getComponent(comp); if(c && this.fireEvent('beforeremove', this, c) !== false){ - this.items.remove(c); - delete c.ownerCt; - if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){ - c.destroy(); - } - if(this.layout && this.layout.activeItem == c){ - delete this.layout.activeItem; - } + this.doRemove(c, autoDestroy); this.fireEvent('remove', this, c); } return c; }, + onRemove: function(c){ + // Empty template method + }, + + // private + doRemove: function(c, autoDestroy){ + var l = this.layout, + hasLayout = l && this.rendered; + + if(hasLayout){ + l.onRemove(c); + } + this.items.remove(c); + c.onRemoved(); + this.onRemove(c); + if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){ + c.destroy(); + } + if(hasLayout){ + l.afterRemove(c); + } + }, + /** * Removes all components from this container. * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. @@ -669,9 +729,9 @@ tb.{@link #doLayout}(); // refresh the layout * and gets a direct child component of this container. * @param {String/Number} comp This parameter may be any of the following: *
      - *
    • a String : representing the {@link Ext.Component#itemId itemId} + *
    • a String : representing the {@link Ext.Component#itemId itemId} * or {@link Ext.Component#id id} of the child component
    • - *
    • a Number : representing the position of the child component + *
    • a Number : representing the position of the child component * within the {@link #items} property
    • *
    *

    For additional information see {@link Ext.util.MixedCollection#get}. @@ -679,14 +739,14 @@ tb.{@link #doLayout}(); // refresh the layout */ getComponent : function(comp){ if(Ext.isObject(comp)){ - return comp; + comp = comp.getItemId(); } return this.items.get(comp); }, // private lookupComponent : function(comp){ - if(typeof comp == 'string'){ + if(Ext.isString(comp)){ return Ext.ComponentMgr.get(comp); }else if(!comp.events){ return this.createComponent(comp); @@ -695,8 +755,28 @@ tb.{@link #doLayout}(); // refresh the layout }, // private - createComponent : function(config){ - return Ext.create(config, this.defaultType); + createComponent : function(config, defaultType){ + if (config.render) { + return config; + } + // add in ownerCt at creation time but then immediately + // remove so that onBeforeAdd can handle it + var c = Ext.create(Ext.apply({ + ownerCt: this + }, config), defaultType || this.defaultType); + delete c.initialConfig.ownerCt; + delete c.ownerCt; + return c; + }, + + /** + * @private + * We can only lay out if there is a view area in which to layout. + * display:none on the layout target, *or any of its parent elements* will mean it has no view area. + */ + canLayout : function() { + var el = this.getVisibilityEl(); + return el && el.dom && !el.isStyle("display", "none"); }, /** @@ -707,13 +787,14 @@ tb.{@link #doLayout}(); // refresh the layout * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden. * @return {Ext.Container} this */ - doLayout: function(shallow, force){ + + doLayout : function(shallow, force){ var rendered = this.rendered, - forceLayout = this.forceLayout; + forceLayout = force || this.forceLayout; - if(!this.isVisible() || this.collapsed){ + if(this.collapsed || !this.canLayout()){ this.deferLayout = this.deferLayout || !shallow; - if(!(force || forceLayout)){ + if(!forceLayout){ return; } shallow = shallow && !this.deferLayout; @@ -728,23 +809,55 @@ tb.{@link #doLayout}(); // refresh the layout for(var i = 0, len = cs.length; i < len; i++){ var c = cs[i]; if(c.doLayout){ - c.forceLayout = forceLayout; - c.doLayout(); + c.doLayout(false, forceLayout); } } } if(rendered){ - this.onLayout(shallow, force); + this.onLayout(shallow, forceLayout); } + // Initial layout completed + this.hasLayout = true; delete this.forceLayout; }, - //private onLayout : Ext.emptyFn, + // private + shouldBufferLayout: function(){ + /* + * Returns true if the container should buffer a layout. + * This is true only if the container has previously been laid out + * and has a parent container that is pending a layout. + */ + var hl = this.hasLayout; + if(this.ownerCt){ + // Only ever buffer if we've laid out the first time and we have one pending. + return hl ? !this.hasLayoutPending() : false; + } + // Never buffer initial layout + return hl; + }, + + // private + hasLayoutPending: function(){ + // Traverse hierarchy to see if any parent container has a pending layout. + var pending = false; + this.ownerCt.bubble(function(c){ + if(c.layoutPending){ + pending = true; + return false; + } + }); + return pending; + }, + onShow : function(){ + // removes css classes that were added to hide Ext.Container.superclass.onShow.call(this); - if(this.deferLayout !== undefined){ + // If we were sized during the time we were hidden, layout. + if(Ext.isDefined(this.deferLayout)){ + delete this.deferLayout; this.doLayout(true); } }, @@ -756,7 +869,7 @@ tb.{@link #doLayout}(); // refresh the layout */ getLayout : function(){ if(!this.layout){ - var layout = new Ext.layout.ContainerLayout(this.layoutConfig); + var layout = new Ext.layout.AutoLayout(this.layoutConfig); this.setLayout(layout); } return this.layout; @@ -764,8 +877,11 @@ tb.{@link #doLayout}(); // refresh the layout // private beforeDestroy : function(){ + var c; if(this.items){ - Ext.destroy.apply(Ext, this.items.items); + while(c = this.items.first()){ + this.doRemove(c, true); + } } if(this.monitorResize){ Ext.EventManager.removeResizeListener(this.doLayout, this);