X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/container/AbstractContainer.js diff --git a/src/container/AbstractContainer.js b/src/container/AbstractContainer.js new file mode 100644 index 00000000..62de3a7f --- /dev/null +++ b/src/container/AbstractContainer.js @@ -0,0 +1,879 @@ +/** + * @class Ext.container.AbstractContainer + * @extends Ext.Component + *
An abstract base class which provides shared methods for Containers across the Sencha product line.
+ * Please refer to sub class's documentation + */ +Ext.define('Ext.container.AbstractContainer', { + + /* Begin Definitions */ + + extend: 'Ext.Component', + + requires: [ + 'Ext.util.MixedCollection', + 'Ext.layout.container.Auto', + 'Ext.ZIndexManager' + ], + + /* End Definitions */ + /** + * @cfg {String/Object} layout + **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:
+ *If the {@link #layout} configuration is not explicitly specified for + * a general purpose container (e.g. Container or Panel) the + * {@link Ext.layout.container.Auto 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).
+ *layout
may be specified as either as an Object or
+ * as a String:
+layout: {
+ type: 'vbox',
+ align: 'left'
+}
+
+ *
+ * type
The layout type to be used for this container. If not specified, + * a default {@link Ext.layout.container.Auto} will be created and used.
+ *Valid layout type
values are:
{@link Ext.layout.container.Auto Auto}
Default{@link Ext.layout.container.Card card}
{@link Ext.layout.container.Fit fit}
{@link Ext.layout.container.HBox hbox}
{@link Ext.layout.container.VBox vbox}
{@link Ext.layout.container.Anchor anchor}
{@link Ext.layout.container.Table table}
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
+ * specified.
+layout: {
+ type: 'vbox',
+ padding: '5',
+ align: 'left'
+}
+
+ * layout
The layout type
to be used for this container (see list
+ * of valid layout type values above).
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.
A single item, or an array of child Components to be added to this container
+ *Unless configured with a {@link #layout}, a Container simply renders child Components serially into + * its encapsulating element and performs no sizing or positioning upon them.
+ *
Example:
+ *
+// specifying a single item
+items: {...},
+layout: 'fit', // The single items is sized to fit
+
+// specifying multiple items
+items: [{...}, {...}],
+layout: 'hbox', // The items are arranged horizontally
+
+ * Each item may be:
+ *If a configuration object is specified, the actual type of Component to be + * instantiated my be indicated by using the {@link Ext.Component#xtype xtype} option.
+ *Every Component class has its own {@link Ext.Component#xtype xtype}.
+ *If an {@link Ext.Component#xtype xtype} is not explicitly
+ * specified, the {@link #defaultType} for the Container is used, which by default is usually panel
.
Notes:
+ *Ext uses lazy rendering. Child Components will only be rendered + * should it become necessary. Items are automatically laid out when they are first + * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.
+ *Do not specify {@link Ext.panel.Panel#contentEl contentEl}
or
+ * {@link Ext.panel.Panel#html html}
with items
.
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.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}
.
Usage:
+defaults: { // defaults are applied to items, not the container
+ autoScroll:true
+},
+items: [
+ {
+ xtype: 'panel', // defaults do not have precedence over
+ id: 'panel1', // options in config objects, so the defaults
+ autoScroll: false // will not be applied here, panel1 will be autoScroll:false
+ },
+ new Ext.panel.Panel({ // defaults do have precedence over options
+ id: 'panel2', // options in components, so the defaults
+ autoScroll: false // will be applied here, panel2 will be autoScroll:true.
+ })
+]
+ */
+
+ /** @cfg {Boolean} suspendLayout
+ * If true, suspend calls to doLayout. Useful when batching multiple adds to a container and not passing them
+ * as multiple arguments or an array.
+ */
+ suspendLayout : false,
+
+ /** @cfg {Boolean} autoDestroy
+ * If true the container will automatically destroy any contained component that is removed from it, else
+ * destruction must be handled manually.
+ * Defaults to true.
+ */
+ autoDestroy : true,
+
+ /** @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'
.
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(){
+ var me = this;
+ me.addEvents(
+ /**
+ * @event afterlayout
+ * Fires when the components in this container are arranged by the associated layout manager.
+ * @param {Ext.container.Container} this
+ * @param {ContainerLayout} layout The ContainerLayout implementation for this container
+ */
+ 'afterlayout',
+ /**
+ * @event beforeadd
+ * Fires before any {@link Ext.Component} is added or inserted into the container.
+ * A handler can return false to cancel the add.
+ * @param {Ext.container.Container} this
+ * @param {Ext.Component} component The component being added
+ * @param {Number} index The index at which the component will be added to the container's items collection
+ */
+ 'beforeadd',
+ /**
+ * @event beforeremove
+ * Fires before any {@link Ext.Component} is removed from the container. A handler can return
+ * false to cancel the remove.
+ * @param {Ext.container.Container} this
+ * @param {Ext.Component} component The component being removed
+ */
+ 'beforeremove',
+ /**
+ * @event add
+ * @bubbles
+ * Fires after any {@link Ext.Component} is added or inserted into the container.
+ * @param {Ext.container.Container} this
+ * @param {Ext.Component} component The component that was added
+ * @param {Number} index The index at which the component was added to the container's items collection
+ */
+ 'add',
+ /**
+ * @event remove
+ * @bubbles
+ * Fires after any {@link Ext.Component} is removed from the container.
+ * @param {Ext.container.Container} this
+ * @param {Ext.Component} component The component that was removed
+ */
+ 'remove',
+ /**
+ * @event beforecardswitch
+ * Fires before this container switches the active card. This event
+ * is only available if this container uses a CardLayout. Note that
+ * TabPanel and Carousel both get a CardLayout by default, so both
+ * will have this event.
+ * A handler can return false to cancel the card switch.
+ * @param {Ext.container.Container} this
+ * @param {Ext.Component} newCard The card that will be switched to
+ * @param {Ext.Component} oldCard The card that will be switched from
+ * @param {Number} index The index of the card that will be switched to
+ * @param {Boolean} animated True if this cardswitch will be animated
+ */
+ 'beforecardswitch',
+ /**
+ * @event cardswitch
+ * Fires after this container switches the active card. If the card
+ * is switched using an animation, this event will fire after the
+ * animation has finished. This event is only available if this container
+ * uses a CardLayout. Note that TabPanel and Carousel both get a CardLayout
+ * by default, so both will have this event.
+ * @param {Ext.container.Container} this
+ * @param {Ext.Component} newCard The card that has been switched to
+ * @param {Ext.Component} oldCard The card that has been switched from
+ * @param {Number} index The index of the card that has been switched to
+ * @param {Boolean} animated True if this cardswitch was animated
+ */
+ 'cardswitch'
+ );
+
+ // layoutOnShow stack
+ me.layoutOnShow = Ext.create('Ext.util.MixedCollection');
+ me.callParent();
+ me.initItems();
+ },
+
+ // @private
+ initItems : function() {
+ var me = this,
+ items = me.items;
+
+ /**
+ * The MixedCollection containing all the child items of this container.
+ * @property items
+ * @type Ext.util.MixedCollection
+ */
+ me.items = Ext.create('Ext.util.MixedCollection', false, me.getComponentId);
+
+ if (items) {
+ if (!Ext.isArray(items)) {
+ items = [items];
+ }
+
+ me.add(items);
+ }
+ },
+
+ // @private
+ afterRender : function() {
+ this.getLayout();
+ this.callParent();
+ },
+
+ // @private
+ setLayout : function(layout) {
+ var currentLayout = this.layout;
+
+ if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
+ currentLayout.setOwner(null);
+ }
+
+ this.layout = layout;
+ layout.setOwner(this);
+ },
+
+ /**
+ * Returns the {@link Ext.layout.container.AbstractContainer layout} instance currently associated with this Container.
+ * If a layout has not been instantiated yet, that is done first
+ * @return {Ext.layout.container.AbstractContainer} The layout
+ */
+ getLayout : function() {
+ var me = this;
+ if (!me.layout || !me.layout.isLayout) {
+ me.setLayout(Ext.layout.Layout.create(me.layout, 'autocontainer'));
+ }
+
+ return me.layout;
+ },
+
+ /**
+ * Manually force this container's layout to be recalculated. The framwork uses this internally to refresh layouts
+ * form most cases.
+ * @return {Ext.container.Container} this
+ */
+ doLayout : function() {
+ var me = this,
+ layout = me.getLayout();
+
+ if (me.rendered && layout && !me.suspendLayout) {
+ // If either dimension is being auto-set, then it requires a ComponentLayout to be run.
+ if ((!Ext.isNumber(me.width) || !Ext.isNumber(me.height)) && me.componentLayout.type !== 'autocomponent') {
+ // Only run the ComponentLayout if it is not already in progress
+ if (me.componentLayout.layoutBusy !== true) {
+ me.doComponentLayout();
+ if (me.componentLayout.layoutCancelled === true) {
+ layout.layout();
+ }
+ }
+ }
+ // Both dimensions defined, run a ContainerLayout
+ else {
+ // Only run the ContainerLayout if it is not already in progress
+ if (layout.layoutBusy !== true) {
+ layout.layout();
+ }
+ }
+ }
+
+ return me;
+ },
+
+ // @private
+ afterLayout : function(layout) {
+ this.fireEvent('afterlayout', this, layout);
+ },
+
+ // @private
+ prepareItems : function(items, applyDefaults) {
+ if (!Ext.isArray(items)) {
+ items = [items];
+ }
+
+ // Make sure defaults are applied and item is initialized
+ var i = 0,
+ len = items.length,
+ item;
+
+ for (; i < len; i++) {
+ item = items[i];
+ if (applyDefaults) {
+ item = this.applyDefaults(item);
+ }
+ items[i] = this.lookupComponent(item);
+ }
+ return items;
+ },
+
+ // @private
+ applyDefaults : function(config) {
+ var defaults = this.defaults;
+
+ if (defaults) {
+ if (Ext.isFunction(defaults)) {
+ defaults = defaults.call(this, config);
+ }
+
+ if (Ext.isString(config)) {
+ config = Ext.ComponentManager.get(config);
+ Ext.applyIf(config, defaults);
+ } else if (!config.isComponent) {
+ Ext.applyIf(config, defaults);
+ } else {
+ Ext.applyIf(config, defaults);
+ }
+ }
+
+ return config;
+ },
+
+ // @private
+ lookupComponent : function(comp) {
+ return Ext.isString(comp) ? Ext.ComponentManager.get(comp) : this.createComponent(comp);
+ },
+
+ // @private
+ createComponent : function(config, defaultType) {
+ // // add in ownerCt at creation time but then immediately
+ // // remove so that onBeforeAdd can handle it
+ // var component = Ext.create(Ext.apply({ownerCt: this}, config), defaultType || this.defaultType);
+ //
+ // delete component.initialConfig.ownerCt;
+ // delete component.ownerCt;
+
+ return Ext.ComponentManager.create(config, defaultType || this.defaultType);
+ },
+
+ // @private - used as the key lookup function for the items collection
+ getComponentId : function(comp) {
+ return comp.getItemId();
+ },
+
+ /**
+
+Adds {@link Ext.Component Component}(s) to this Container.
+
+##Description:##
+
+- Fires the {@link #beforeadd} event before adding.
+- The Container's {@link #defaults default config values} will be applied
+ accordingly (see `{@link #defaults}` for details).
+- Fires the `{@link #add}` event after the component has been added.
+
+##Notes:##
+
+If the Container is __already rendered__ when `add`
+is called, it will render the newly added Component into its content area.
+
+__**If**__ the Container was configured with a size-managing {@link #layout} manager, the Container
+will recalculate its internal layout at this time too.
+
+Note that the default layout manager simply renders child Components sequentially into the content area and thereafter performs no sizing.
+
+If adding multiple new child Components, pass them as an array to the `add` method, so that only one layout recalculation is performed.
+
+ tb = new {@link Ext.toolbar.Toolbar}({
+ renderTo: document.body
+ }); // toolbar is rendered
+ tb.add([{text:'Button 1'}, {text:'Button 2'}]); // add multiple items. ({@link #defaultType} for {@link Ext.toolbar.Toolbar Toolbar} is 'button')
+
+##Warning:##
+
+Components directly managed by the BorderLayout layout manager
+may not be removed or added. See the Notes for {@link Ext.layout.container.Border BorderLayout}
+for more details.
+
+ * @param {...Object/Array} Component
+ * Either one or more Components to add or an Array of Components to add.
+ * See `{@link #items}` for additional information.
+ *
+ * @return {Ext.Component/Array} The Components that were added.
+ * @markdown
+ */
+ add : function() {
+ var me = this,
+ args = Array.prototype.slice.call(arguments),
+ hasMultipleArgs,
+ items,
+ results = [],
+ i,
+ ln,
+ item,
+ index = -1,
+ cmp;
+
+ if (typeof args[0] == 'number') {
+ index = args.shift();
+ }
+
+ hasMultipleArgs = args.length > 1;
+ if (hasMultipleArgs || Ext.isArray(args[0])) {
+
+ items = hasMultipleArgs ? args : args[0];
+ // Suspend Layouts while we add multiple items to the container
+ me.suspendLayout = true;
+ for (i = 0, ln = items.length; i < ln; i++) {
+ item = items[i];
+
+ //
Called by Component#doAutoRender
+ *Register a Container configured floating: true
with this Container's {@link Ext.ZIndexManager ZIndexManager}.
Components added in ths way will not participate in the layout, but will be rendered + * upon first show in the way that {@link Ext.window.Window Window}s are.
+ * + */ + registerFloatingItem: function(cmp) { + var me = this; + if (!me.floatingItems) { + me.floatingItems = Ext.create('Ext.ZIndexManager', me); + } + me.floatingItems.register(cmp); + }, + + onAdd : Ext.emptyFn, + onRemove : Ext.emptyFn, + + /** + * 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 + * Component has been inserted. + * @param {Number} index The index at which the Component will be inserted + * into the Container's items collection + * @param {Ext.Component} component The child Component to insert.this
reference) of the
+ * function call will be the scope provided or the current component. The arguments to the function
+ * will be the args provided or the current component. If the function returns false at any point,
+ * the cascade is stopped on that branch.
+ * @param {Function} fn The function to call
+ * @param {Object} scope (optional) The scope of the function (defaults to current component)
+ * @param {Array} args (optional) The args to call the function with. The current component always passed as the last argument.
+ * @return {Ext.Container} this
+ */
+ cascade : function(fn, scope, origArgs){
+ var me = this,
+ cs = me.items ? me.items.items : [],
+ len = cs.length,
+ i = 0,
+ c,
+ args = origArgs ? origArgs.concat(me) : [me],
+ componentIndex = args.length - 1;
+
+ if (fn.apply(scope || me, args) !== false) {
+ for(; i < len; i++){
+ c = cs[i];
+ if (c.cascade) {
+ c.cascade(fn, scope, origArgs);
+ } else {
+ args[componentIndex] = c;
+ fn.apply(scope || cs, args);
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Examines this container's {@link #items}
property
+ * and gets a direct child component of this container.
+ * @param {String/Number} comp This parameter may be any of the following:
+ * String
: representing the {@link Ext.Component#itemId itemId}
+ * or {@link Ext.Component#id id}
of the child component Number
: representing the position of the child component
+ * within the {@link #items}
propertyFor additional information see {@link Ext.util.MixedCollection#get}. + * @return Ext.Component The component (if found). + */ + getComponent : function(comp) { + if (Ext.isObject(comp)) { + comp = comp.getItemId(); + } + + return this.items.get(comp); + }, + + /** + * Retrieves all descendant components which match the passed selector. + * Executes an Ext.ComponentQuery.query using this container as its root. + * @param {String} selector Selector complying to an Ext.ComponentQuery selector + * @return {Array} Ext.Component's which matched the selector + */ + query : function(selector) { + return Ext.ComponentQuery.query(selector, this); + }, + + /** + * Retrieves the first direct child of this container which matches the passed selector. + * The passed in selector must comply with an Ext.ComponentQuery selector. + * @param {String} selector An Ext.ComponentQuery selector + * @return Ext.Component + */ + child : function(selector) { + return this.query('> ' + selector)[0] || null; + }, + + /** + * Retrieves the first descendant of this container which matches the passed selector. + * The passed in selector must comply with an Ext.ComponentQuery selector. + * @param {String} selector An Ext.ComponentQuery selector + * @return Ext.Component + */ + down : function(selector) { + return this.query(selector)[0] || null; + }, + + // inherit docs + show : function() { + this.callParent(arguments); + this.performDeferredLayouts(); + return this; + }, + + // Lay out any descendant containers who queued a layout operation during the time this was hidden + // This is also called by Panel after it expands because descendants of a collapsed Panel allso queue any layout ops. + performDeferredLayouts: function() { + var layoutCollection = this.layoutOnShow, + ln = layoutCollection.getCount(), + i = 0, + needsLayout, + item; + + for (; i < ln; i++) { + item = layoutCollection.get(i); + needsLayout = item.needsLayout; + + if (Ext.isObject(needsLayout)) { + item.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize, needsLayout.ownerCt); + } + } + layoutCollection.clear(); + }, + + //@private + // Enable all immediate children that was previously disabled + onEnable: function() { + Ext.Array.each(this.query('[isFormField]'), function(item) { + if (item.resetDisable) { + item.enable(); + delete item.resetDisable; + } + }); + this.callParent(); + }, + + // @private + // Disable all immediate children that was previously disabled + onDisable: function() { + Ext.Array.each(this.query('[isFormField]'), function(item) { + if (item.resetDisable !== false && !item.disabled) { + item.disable(); + item.resetDisable = true; + } + }); + this.callParent(); + }, + + /** + * Occurs before componentLayout is run. Returning false from this method will prevent the containerLayout + * from being executed. + */ + beforeLayout: function() { + return true; + }, + + // @private + beforeDestroy : function() { + var me = this, + items = me.items, + c; + + if (items) { + while ((c = items.first())) { + me.doRemove(c, true); + } + } + + Ext.destroy( + me.layout, + me.floatingItems + ); + me.callParent(); + } +}); \ No newline at end of file