X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/6746dc89c47ed01b165cc1152533605f97eb8e8d..f562e4c6e5fac7bcb445985b99acbea4d706e6f0:/src/panel/Table.js diff --git a/src/panel/Table.js b/src/panel/Table.js index 20793fb5..32de89a7 100644 --- a/src/panel/Table.js +++ b/src/panel/Table.js @@ -13,10 +13,9 @@ If you are unsure which license is appropriate for your use, please contact the */ /** - * @class Ext.panel.Table - * @extends Ext.panel.Panel * @author Nicolas Ferrero - * TablePanel is the basis of both TreePanel and GridPanel. + * + * TablePanel is the basis of both {@link Ext.tree.Panel TreePanel} and {@link Ext.grid.Panel GridPanel}. * * TablePanel aggregates: * @@ -25,7 +24,6 @@ If you are unsure which license is appropriate for your use, please contact the * - a Store * - Scrollers * - Ext.grid.header.Container - * */ Ext.define('Ext.panel.Table', { extend: 'Ext.panel.Panel', @@ -39,64 +37,124 @@ Ext.define('Ext.panel.Table', { 'Ext.grid.Lockable' ], - cls: Ext.baseCSSPrefix + 'grid', + extraBaseCls: Ext.baseCSSPrefix + 'grid', extraBodyCls: Ext.baseCSSPrefix + 'grid-body', layout: 'fit', /** - * Boolean to indicate that a view has been injected into the panel. - * @property hasView + * @property {Boolean} hasView + * True to indicate that a view has been injected into the panel. */ hasView: false, // each panel should dictate what viewType and selType to use + /** + * @cfg {String} viewType + * An xtype of view to use. This is automatically set to 'gridview' by {@link Ext.grid.Panel Grid} + * and to 'treeview' by {@link Ext.tree.Panel Tree}. + */ viewType: null, + + /** + * @cfg {Object} viewConfig + * A config object that will be applied to the grid's UI view. Any of the config options available for + * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified. + */ + + /** + * @cfg {Ext.view.Table} view + * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to + * view (instead of creating an entire View instance). + */ + + /** + * @cfg {String} selType + * An xtype of selection model to use. Defaults to 'rowmodel'. This is used to create selection model if just + * a config object or nothing at all given in {@link #selModel} config. + */ selType: 'rowmodel', + /** + * @cfg {Ext.selection.Model/Object} selModel + * A {@link Ext.selection.Model selection model} instance or config object. In latter case the {@link #selType} + * config option determines to which type of selection model this config is applied. + */ + + /** + * @cfg {Boolean} multiSelect + * True to enable 'MULTI' selection mode on selection model. See {@link Ext.selection.Model#mode}. + */ + + /** + * @cfg {Boolean} simpleSelect + * True to enable 'SIMPLE' selection mode on selection model. See {@link Ext.selection.Model#mode}. + */ + + /** + * @cfg {Ext.data.Store} store (required) + * The {@link Ext.data.Store Store} the grid should use as its data source. + */ + /** * @cfg {Number} scrollDelta * Number of pixels to scroll when scrolling with mousewheel. - * Defaults to 40. */ scrollDelta: 40, /** * @cfg {String/Boolean} scroll - * Valid values are 'both', 'horizontal' or 'vertical'. true implies 'both'. false implies 'none'. - * Defaults to true. + * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'. + * True implies 'both'. False implies 'none'. */ scroll: true, /** - * @cfg {Array} columns - * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this grid. Each - * column definition provides the header text for the column, and a definition of where the data for that column comes from. + * @cfg {Ext.grid.column.Column[]} columns + * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this + * grid. Each column definition provides the header text for the column, and a definition of where the data for that + * column comes from. */ /** * @cfg {Boolean} forceFit - * Specify as true to force the columns to fit into the available width. Headers are first sized according to configuration, whether that be - * a specific width, or flex. Then they are all proportionally changed in width so that the entire content width is used.. + * Ttrue to force the columns to fit into the available width. Headers are first sized according to configuration, + * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire + * content width is used. */ /** - * @cfg {Boolean} hideHeaders - * Specify as true to hide the headers. + * @cfg {Ext.grid.feature.Feature[]} features + * An array of grid Features to be added to this grid. See {@link Ext.grid.feature.Feature} for usage. */ /** - * @cfg {Boolean} deferRowRender

Defaults to true to enable deferred row rendering.

- *

This allows the GridView to execute a refresh quickly, with the expensive update of the row - * structure deferred so that layouts with GridPanels appear, and lay out more quickly.

+ * @cfg {Boolean} [hideHeaders=false] + * True to hide column headers. */ + /** + * @cfg {Boolean} deferRowRender + * Defaults to true to enable deferred row rendering. + * + * This allows the View to execute a refresh quickly, with the expensive update of the row structure deferred so + * that layouts with GridPanels appear, and lay out more quickly. + */ + + deferRowRender: true, + /** * @cfg {Boolean} sortableColumns - * Defaults to true. Set to false to disable column sorting via clicking the - * header and via the Sorting menu items. + * False to disable column sorting via clicking the header and via the Sorting menu items. */ sortableColumns: true, + /** + * @cfg {Boolean} [enableLocking=false] + * True to enable locking support for this grid. Alternatively, locking will also be automatically + * enabled if any of the columns in the column configuration contain the locked config option. + */ + enableLocking: false, + verticalScrollDock: 'right', verticalScrollerType: 'gridscroller', @@ -111,19 +169,19 @@ Ext.define('Ext.panel.Table', { /** * @cfg {Boolean} enableColumnMove - * Defaults to true. Set to false to disable column dragging within this grid. + * False to disable column dragging within this grid. */ enableColumnMove: true, /** * @cfg {Boolean} enableColumnResize - * Defaults to true. Set to false to disable column resizing within this grid. + * False to disable column resizing within this grid. */ enableColumnResize: true, /** * @cfg {Boolean} enableColumnHide - * Defaults to true. Set to false to disable column hiding within this grid. + * False to disable column hiding within this grid. */ enableColumnHide: true, @@ -132,9 +190,6 @@ Ext.define('Ext.panel.Table', { if (!this.viewType) { Ext.Error.raise("You must specify a viewType config."); } - if (!this.store) { - Ext.Error.raise("You must specify a store config"); - } if (this.headers) { Ext.Error.raise("The headers config is not supported. Please specify columns instead."); } @@ -149,19 +204,13 @@ Ext.define('Ext.panel.Table', { view, border = me.border; - // We cannot buffer this because that will wait for the 30msec from afterLayout (or what - // ever event triggers it) and we may be in the middle of an animation; that is a bad - // time to injectView because it causes a layout (by calling add on the container). A - // throttled func will be called immediately on first call and then block subsequent - // (rapid fire) calls for 30msec before allowing another call to go through. Similar - // results, but the action moves from the trailing edge of the interval to the leading - // edge. - me.injectView = Ext.Function.createThrottled(me.injectView, 30, me); - if (me.hideHeaders) { border = false; } + // Look up the configured Store. If none configured, use the fieldless, empty Store defined in Ext.data.Store. + me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store'); + // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer // Either way, we extract a columns property referencing an array of Column definitions. if (headerCtCfg instanceof Ext.grid.header.Container) { @@ -187,30 +236,35 @@ Ext.define('Ext.panel.Table', { // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a // special view will be injected by the Ext.grid.Lockable mixin, so no processing of . - if (Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) { + if (me.enableLocking || Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) { me.self.mixin('lockable', Ext.grid.Lockable); me.injectLockable(); } } - me.store = Ext.data.StoreManager.lookup(me.store); me.addEvents( /** * @event reconfigure - * Fires after a reconfigure + * Fires after a reconfigure. * @param {Ext.panel.Table} this */ 'reconfigure', + /** + * @event viewready + * Fires when the grid view is available (use this for selecting a default row). + * @param {Ext.panel.Table} this + */ + 'viewready', /** * @event scrollerhide - * Fires when a scroller is hidden + * Fires when a scroller is hidden. * @param {Ext.grid.Scroller} scroller * @param {String} orientation Orientation, can be 'vertical' or 'horizontal' */ 'scrollerhide', /** * @event scrollershow - * Fires when a scroller is shown + * Fires when a scroller is shown. * @param {Ext.grid.Scroller} scroller * @param {String} orientation Orientation, can be 'vertical' or 'horizontal' */ @@ -219,6 +273,9 @@ Ext.define('Ext.panel.Table', { me.bodyCls = me.bodyCls || ''; me.bodyCls += (' ' + me.extraBodyCls); + + me.cls = me.cls || ''; + me.cls += (' ' + me.extraBaseCls); // autoScroll is not a valid configuration delete me.autoScroll; @@ -278,9 +335,12 @@ Ext.define('Ext.panel.Table', { }); } - me.headerCt.on('columnresize', me.onHeaderResize, me); - me.relayEvents(me.headerCt, ['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange']); + me.headerCt.on('resize', me.onHeaderResize, me); + me.relayHeaderCtEvents(me.headerCt); me.features = me.features || []; + if (!Ext.isArray(me.features)) { + me.features = [me.features]; + } me.dockedItems = me.dockedItems || []; me.dockedItems.unshift(me.headerCt); me.viewConfig = me.viewConfig || {}; @@ -290,268 +350,215 @@ Ext.define('Ext.panel.Table', { // getView converts viewConfig into a View instance view = me.getView(); - if (view) { - me.mon(view.store, { - load: me.onStoreLoad, - scope: me - }); - me.mon(view, { - refresh: me.onViewRefresh, - scope: me - }); - this.relayEvents(view, [ - /** - * @event beforeitemmousedown - * Fires before the mousedown event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemmousedown', - /** - * @event beforeitemmouseup - * Fires before the mouseup event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemmouseup', - /** - * @event beforeitemmouseenter - * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemmouseenter', - /** - * @event beforeitemmouseleave - * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemmouseleave', - /** - * @event beforeitemclick - * Fires before the click event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemclick', - /** - * @event beforeitemdblclick - * Fires before the dblclick event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemdblclick', - /** - * @event beforeitemcontextmenu - * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemcontextmenu', - /** - * @event itemmousedown - * Fires when there is a mouse down on an item - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemmousedown', - /** - * @event itemmouseup - * Fires when there is a mouse up on an item - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemmouseup', - /** - * @event itemmouseenter - * Fires when the mouse enters an item. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemmouseenter', - /** - * @event itemmouseleave - * Fires when the mouse leaves an item. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemmouseleave', - /** - * @event itemclick - * Fires when an item is clicked. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemclick', - /** - * @event itemdblclick - * Fires when an item is double clicked. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemdblclick', - /** - * @event itemcontextmenu - * Fires when an item is right clicked. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemcontextmenu', - /** - * @event beforecontainermousedown - * Fires before the mousedown event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainermousedown', - /** - * @event beforecontainermouseup - * Fires before the mouseup event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainermouseup', - /** - * @event beforecontainermouseover - * Fires before the mouseover event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainermouseover', - /** - * @event beforecontainermouseout - * Fires before the mouseout event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainermouseout', - /** - * @event beforecontainerclick - * Fires before the click event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainerclick', - /** - * @event beforecontainerdblclick - * Fires before the dblclick event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainerdblclick', - /** - * @event beforecontainercontextmenu - * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainercontextmenu', - /** - * @event containermouseup - * Fires when there is a mouse up on the container - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containermouseup', - /** - * @event containermouseover - * Fires when you move the mouse over the container. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containermouseover', - /** - * @event containermouseout - * Fires when you move the mouse out of the container. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containermouseout', - /** - * @event containerclick - * Fires when the container is clicked. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containerclick', - /** - * @event containerdblclick - * Fires when the container is double clicked. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containerdblclick', - /** - * @event containercontextmenu - * Fires when the container is right clicked. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containercontextmenu', - - /** - * @event selectionchange - * Fires when the selected nodes change. Relayed event from the underlying selection model. - * @param {Ext.view.View} this - * @param {Array} selections Array of the selected nodes - */ - 'selectionchange', - /** - * @event beforeselect - * Fires before a selection is made. If any handlers return false, the selection is cancelled. - * @param {Ext.view.View} this - * @param {HTMLElement} node The node to be selected - * @param {Array} selections Array of currently selected nodes - */ - 'beforeselect' - ]); - } + view.on({ + afterrender: function () { + // hijack the view el's scroll method + view.el.scroll = Ext.Function.bind(me.elScroll, me); + // We use to listen to document.body wheel events, but that's a + // little much. We scope just to the view now. + me.mon(view.el, { + mousewheel: me.onMouseWheel, + scope: me + }); + }, + single: true + }); + me.items = [view]; + me.hasView = true; + + me.mon(view.store, { + load: me.onStoreLoad, + scope: me + }); + me.mon(view, { + viewReady: me.onViewReady, + resize: me.onViewResize, + refresh: { + fn: me.onViewRefresh, + scope: me, + buffer: 50 + }, + scope: me + }); + this.relayEvents(view, [ + /** + * @event beforeitemmousedown + * @alias Ext.view.View#beforeitemmousedown + */ + 'beforeitemmousedown', + /** + * @event beforeitemmouseup + * @alias Ext.view.View#beforeitemmouseup + */ + 'beforeitemmouseup', + /** + * @event beforeitemmouseenter + * @alias Ext.view.View#beforeitemmouseenter + */ + 'beforeitemmouseenter', + /** + * @event beforeitemmouseleave + * @alias Ext.view.View#beforeitemmouseleave + */ + 'beforeitemmouseleave', + /** + * @event beforeitemclick + * @alias Ext.view.View#beforeitemclick + */ + 'beforeitemclick', + /** + * @event beforeitemdblclick + * @alias Ext.view.View#beforeitemdblclick + */ + 'beforeitemdblclick', + /** + * @event beforeitemcontextmenu + * @alias Ext.view.View#beforeitemcontextmenu + */ + 'beforeitemcontextmenu', + /** + * @event itemmousedown + * @alias Ext.view.View#itemmousedown + */ + 'itemmousedown', + /** + * @event itemmouseup + * @alias Ext.view.View#itemmouseup + */ + 'itemmouseup', + /** + * @event itemmouseenter + * @alias Ext.view.View#itemmouseenter + */ + 'itemmouseenter', + /** + * @event itemmouseleave + * @alias Ext.view.View#itemmouseleave + */ + 'itemmouseleave', + /** + * @event itemclick + * @alias Ext.view.View#itemclick + */ + 'itemclick', + /** + * @event itemdblclick + * @alias Ext.view.View#itemdblclick + */ + 'itemdblclick', + /** + * @event itemcontextmenu + * @alias Ext.view.View#itemcontextmenu + */ + 'itemcontextmenu', + /** + * @event beforecontainermousedown + * @alias Ext.view.View#beforecontainermousedown + */ + 'beforecontainermousedown', + /** + * @event beforecontainermouseup + * @alias Ext.view.View#beforecontainermouseup + */ + 'beforecontainermouseup', + /** + * @event beforecontainermouseover + * @alias Ext.view.View#beforecontainermouseover + */ + 'beforecontainermouseover', + /** + * @event beforecontainermouseout + * @alias Ext.view.View#beforecontainermouseout + */ + 'beforecontainermouseout', + /** + * @event beforecontainerclick + * @alias Ext.view.View#beforecontainerclick + */ + 'beforecontainerclick', + /** + * @event beforecontainerdblclick + * @alias Ext.view.View#beforecontainerdblclick + */ + 'beforecontainerdblclick', + /** + * @event beforecontainercontextmenu + * @alias Ext.view.View#beforecontainercontextmenu + */ + 'beforecontainercontextmenu', + /** + * @event containermouseup + * @alias Ext.view.View#containermouseup + */ + 'containermouseup', + /** + * @event containermouseover + * @alias Ext.view.View#containermouseover + */ + 'containermouseover', + /** + * @event containermouseout + * @alias Ext.view.View#containermouseout + */ + 'containermouseout', + /** + * @event containerclick + * @alias Ext.view.View#containerclick + */ + 'containerclick', + /** + * @event containerdblclick + * @alias Ext.view.View#containerdblclick + */ + 'containerdblclick', + /** + * @event containercontextmenu + * @alias Ext.view.View#containercontextmenu + */ + 'containercontextmenu', + /** + * @event selectionchange + * @alias Ext.selection.Model#selectionchange + */ + 'selectionchange', + /** + * @event beforeselect + * @alias Ext.selection.RowModel#beforeselect + */ + 'beforeselect', + /** + * @event select + * @alias Ext.selection.RowModel#select + */ + 'select', + /** + * @event beforedeselect + * @alias Ext.selection.RowModel#beforedeselect + */ + 'beforedeselect', + /** + * @event deselect + * @alias Ext.selection.RowModel#deselect + */ + 'deselect' + ]); } + me.callParent(arguments); }, + + onRender: function(){ + var vScroll = this.verticalScroller, + hScroll = this.horizontalScroller; + + if (vScroll) { + vScroll.ensureDimension(); + } + if (hScroll) { + hScroll.ensureDimension(); + } + this.callParent(arguments); + }, // state management initStateEvents: function(){ @@ -596,40 +603,42 @@ Ext.define('Ext.panel.Table', { return ret; }, + relayHeaderCtEvents: function (headerCt) { + this.relayEvents(headerCt, [ + /** + * @event columnresize + * @alias Ext.grid.header.Container#columnresize + */ + 'columnresize', + /** + * @event columnmove + * @alias Ext.grid.header.Container#columnmove + */ + 'columnmove', + /** + * @event columnhide + * @alias Ext.grid.header.Container#columnhide + */ + 'columnhide', + /** + * @event columnshow + * @alias Ext.grid.header.Container#columnshow + */ + 'columnshow', + /** + * @event sortchange + * @alias Ext.grid.header.Container#sortchange + */ + 'sortchange' + ]); + }, + getState: function(){ - var state = this.callParent(), - sorter = this.store.sorters.first(), - headers = this.headerCt.items.items, - header, - len = headers.length, - i = 0; - - state.columns = []; - for (; i < len; i++) { - header = headers[i]; - state.columns[i] = { - id: header.headerId - }; + var me = this, + state = me.callParent(), + sorter = me.store.sorters.first(); - // We only store state which has changed from the initial state. - // So that current software settings do not override future software settings. - // Only user-changed state should be saved. - if (header.hidden !== (header.initialConfig.hidden||header.self.prototype.hidden)) { - state.columns[i].hidden = header.hidden; - } - if (header.sortable !== header.initialConfig.sortable) { - state.columns[i].sortable = header.sortable; - } - if (header.flex) { - if (header.flex !== header.initialConfig.flex) { - state.columns[i].flex = header.flex; - } - } else { - if (header.width !== header.initialConfig.width) { - state.columns[i].width = header.width; - } - } - } + state.columns = (me.headerCt || me).getColumnsState(); if (sorter) { state.sort = { @@ -637,60 +646,25 @@ Ext.define('Ext.panel.Table', { direction: sorter.direction }; } + return state; }, applyState: function(state) { - var headers = state.columns, - length = headers ? headers.length : 0, - headerCt = this.headerCt, - items = headerCt.items, + var me = this, sorter = state.sort, - store = this.store, - i = 0, - index, - headerState, - header; + store = me.store, + columns = state.columns; - headerCt.suspendLayout = true; + delete state.columns; // Ensure superclass has applied *its* state. // AbstractComponent saves dimensions (and anchor/flex) plus collapsed state. - this.callParent(arguments); - - for (; i < length; ++i) { - headerState = headers[i]; - header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']'); - index = items.indexOf(header); - if (i !== index) { - headerCt.moveHeader(index, i); - } + me.callParent(arguments); - // Only state properties which were saved should be restored. - // (Only user-changed properties were saved by getState) - if (Ext.isDefined(headerState.hidden)) { - header.hidden = headerState.hidden; - } - if (Ext.isDefined(headerState.sortable)) { - header.sortable = headerState.sortable; - } - if (Ext.isDefined(headerState.flex)) { - delete header.width; - header.flex = headerState.flex; - } else if (Ext.isDefined(headerState.width)) { - delete header.flex; - header.minWidth = headerState.width; - if (header.rendered) { - header.setWidth(headerState.width); - } else { - header.width = headerState.width; - } - } + if (columns) { + (me.headerCt || me).applyColumnsState(columns); } - headerCt.suspendLayout = false; - - // After setting width and flexes while layout is suspended, column Container's Container layout needs running. - headerCt.doLayout(); if (sorter) { if (store.remoteSort) { @@ -724,7 +698,7 @@ Ext.define('Ext.panel.Table', { if (!me.view) { sm = me.getSelectionModel(); me.view = me.createComponent(Ext.apply({}, me.viewConfig, { - deferRowRender: me.deferRowRender, + deferInitialRefresh: me.deferRowRender, xtype: me.viewType, store: me.store, headerCt: me.headerCt, @@ -760,71 +734,33 @@ Ext.define('Ext.panel.Table', { if (direction === "up" || direction === "left") { distance = -distance; } - + if (direction === "down" || direction === "up") { scroller = me.getVerticalScroller(); - scroller.scrollByDeltaY(distance); + + //if the grid does not currently need a vertical scroller don't try to update it (EXTJSIV-3891) + if (scroller) { + scroller.scrollByDeltaY(distance); + } } else { scroller = me.getHorizontalScroller(); - scroller.scrollByDeltaX(distance); - } - }, - - /** - * @private - * Called after this Component has achieved its correct initial size, after all layouts have done their thing. - * This is so we can add the View only after the initial size is known. This method is throttled 30ms. - */ - injectView: function() { - if (!this.hasView && !this.collapsed) { - var me = this, - view = me.getView(); - - me.hasView = true; - me.add(view); - - function viewReady () { - // hijack the view el's scroll method - view.el.scroll = Ext.Function.bind(me.elScroll, me); - // We use to listen to document.body wheel events, but that's a - // little much. We scope just to the view now. - me.mon(view.el, { - mousewheel: me.onMouseWheel, - scope: me - }); - if (!me.height) { - me.doComponentLayout(); - } + + //if the grid does not currently need a horizontal scroller don't try to update it (EXTJSIV-3891) + if (scroller) { + scroller.scrollByDeltaX(distance); } - - if (view.rendered) { - viewReady(); - } else { - view.on({ - afterrender: viewReady, - single: true - }); - } - } - }, - - afterExpand: function() { - // TODO - this is *not* called when part of an accordion! - this.callParent(arguments); - if (!this.hasView) { - this.injectView(); } }, /** * @private - * Process UI events from the view. Propagate them to whatever internal Components need to process them + * Processes UI events from the view. Propagates them to whatever internal Components need to process them. * @param {String} type Event type, eg 'click' - * @param {TableView} view TableView Component - * @param {HtmlElement} cell Cell HtmlElement the event took place within + * @param {Ext.view.Table} view TableView Component + * @param {HTMLElement} cell Cell HtmlElement the event took place within * @param {Number} recordIndex Index of the associated Store Model (-1 if none) * @param {Number} cellIndex Cell index within the row - * @param {EventObject} e Original event + * @param {Ext.EventObject} e Original event */ processEvent: function(type, view, cell, recordIndex, cellIndex, e) { var me = this, @@ -837,10 +773,16 @@ Ext.define('Ext.panel.Table', { }, /** - * Request a recalculation of scrollbars and put them in if they are needed. + * Requests a recalculation of scrollbars and puts them in if they are needed. */ determineScrollbars: function() { + // Set a flag so that afterComponentLayout does not recurse back into here. + if (this.determineScrollbarsRunning) { + return; + } + this.determineScrollbarsRunning = true; var me = this, + view = me.view, box, tableEl, scrollWidth, @@ -854,11 +796,12 @@ Ext.define('Ext.panel.Table', { reqScrollbars = 0; // 1 = vertical, 2 = horizontal, 3 = both // If we are not collapsed, and the view has been rendered AND filled, then we can determine scrollbars - if (!me.collapsed && me.view && me.view.el && me.view.el.dom.firstChild) { + if (!me.collapsed && view && view.viewReady) { // Calculate maximum, *scrollbarless* space which the view has available. // It will be the Fit Layout's calculated size, plus the widths of any currently shown scrollbars - box = me.layout.getLayoutTargetSize(); + box = view.el.getSize(); + clientWidth = box.width + ((curScrollbars & 1) ? verticalScroller.width : 0); clientHeight = box.height + ((curScrollbars & 2) ? horizontalScroller.height : 0); @@ -871,7 +814,7 @@ Ext.define('Ext.panel.Table', { if (verticalScroller && verticalScroller.el) { scrollHeight = verticalScroller.getSizeCalculation().height; } else { - tableEl = me.view.el.child('table', true); + tableEl = view.el.child('table', true); scrollHeight = tableEl ? tableEl.offsetHeight : 0; } @@ -917,31 +860,27 @@ Ext.define('Ext.panel.Table', { } me.suspendLayout = false; - // After docked scrollers are correctly configured, lay out the Component. - // Set a flag so that afterComponentLayout does not recurse back into here. - me.changingScrollBars = true; - me.doComponentLayout(me.getWidth(), me.getHeight(), false, me.ownerCt); - me.changingScrollBars = false; + // Lay out the Component. + me.doComponentLayout(); + // Lay out me.items + me.getLayout().layout(); } } + delete me.determineScrollbarsRunning; }, - afterComponentLayout: function() { - var me = this; - me.callParent(arguments); - - // Insert the View the first time the Panel has a Component layout performed. - me.injectView(); + onViewResize: function() { + this.determineScrollbars(); + }, - // Avoid recursion - if (!me.changingScrollBars) { - me.determineScrollbars(); - } - me.invalidateScroller(); + afterComponentLayout: function() { + this.callParent(arguments); + this.determineScrollbars(); + this.invalidateScroller(); }, onHeaderResize: function() { - if (this.view && this.view.rendered) { + if (!this.componentLayout.layoutBusy && this.view && this.view.rendered) { this.determineScrollbars(); this.invalidateScroller(); } @@ -970,7 +909,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Hide the verticalScroller and remove the horizontalScrollerPresentCls. + * Hides the verticalScroller and removes the horizontalScrollerPresentCls. */ hideHorizontalScroller: function() { var me = this; @@ -985,7 +924,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Show the horizontalScroller and add the horizontalScrollerPresentCls. + * Shows the horizontalScroller and add the horizontalScrollerPresentCls. */ showHorizontalScroller: function() { var me = this; @@ -1001,7 +940,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Hide the verticalScroller and remove the verticalScrollerPresentCls. + * Hides the verticalScroller and removes the verticalScrollerPresentCls. */ hideVerticalScroller: function() { var me = this; @@ -1015,7 +954,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Show the verticalScroller and add the verticalScrollerPresentCls. + * Shows the verticalScroller and adds the verticalScrollerPresentCls. */ showVerticalScroller: function() { var me = this; @@ -1035,13 +974,14 @@ Ext.define('Ext.panel.Table', { // only trigger a layout when reserveOffset is changing if (layout && layout.reserveOffset !== reserveOffset) { layout.reserveOffset = reserveOffset; - headerCt.doLayout(); + if (!this.suspendLayout) { + headerCt.doLayout(); + } } }, /** - * Invalides scrollers that are present and forces a recalculation. - * (Not related to showing/hiding the scrollers) + * Invalides scrollers that are present and forces a recalculation. (Not related to showing/hiding the scrollers) */ invalidateScroller: function() { var me = this, @@ -1082,7 +1022,7 @@ Ext.define('Ext.panel.Table', { var me = this, vertScroller = me.getVerticalScroller(), horizScroller = me.getHorizontalScroller(), - scrollDelta = me.scrollDelta / -5, + scrollDelta = -me.scrollDelta, deltas = e.getWheelDeltas(), deltaX = scrollDelta * deltas.x, deltaY = scrollDelta * deltas.y, @@ -1125,18 +1065,36 @@ Ext.define('Ext.panel.Table', { /** * @private - * Determine and invalidate scrollers on view refresh + * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready + */ + onViewReady: function() { + var me = this; + me.fireEvent('viewready', me); + if (me.deferRowRender) { + me.determineScrollbars(); + me.invalidateScroller(); + } + }, + + /** + * @private + * Determines and invalidates scrollers on view refresh */ onViewRefresh: function() { - this.determineScrollbars(); - if (this.invalidateScrollerOnRefresh) { - this.invalidateScroller(); + var me = this; + + // Refresh *during* render must be ignored. + if (!me.rendering) { + this.determineScrollbars(); + if (this.invalidateScrollerOnRefresh) { + this.invalidateScroller(); + } } }, /** * Sets the scrollTop of the TablePanel. - * @param {Number} deltaY + * @param {Number} top */ setScrollTop: function(top) { var me = this, @@ -1171,10 +1129,10 @@ Ext.define('Ext.panel.Table', { /** * Scrolls the TablePanel by deltaX - * @param {Number} deltaY + * @param {Number} deltaX */ scrollByDeltaX: function(deltaX) { - var horizontalScroller = this.getVerticalScroller(); + var horizontalScroller = this.getHorizontalScroller(); if (horizontalScroller) { horizontalScroller.scrollByDeltaX(deltaX); @@ -1182,14 +1140,14 @@ Ext.define('Ext.panel.Table', { }, /** - * Get left hand side marker for header resizing. + * Gets left hand side marker for header resizing. * @private */ getLhsMarker: function() { var me = this; if (!me.lhsMarker) { - me.lhsMarker = Ext.core.DomHelper.append(me.el, { + me.lhsMarker = Ext.DomHelper.append(me.el, { cls: Ext.baseCSSPrefix + 'grid-resize-marker' }, true); } @@ -1197,14 +1155,14 @@ Ext.define('Ext.panel.Table', { }, /** - * Get right hand side marker for header resizing. + * Gets right hand side marker for header resizing. * @private */ getRhsMarker: function() { var me = this; if (!me.rhsMarker) { - me.rhsMarker = Ext.core.DomHelper.append(me.el, { + me.rhsMarker = Ext.DomHelper.append(me.el, { cls: Ext.baseCSSPrefix + 'grid-resize-marker' }, true); } @@ -1212,8 +1170,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Returns the selection model being used and creates it via the configuration - * if it has not been created already. + * Returns the selection model being used and creates it via the configuration if it has not been created already. * @return {Ext.selection.Model} selModel */ getSelectionModel: function(){ @@ -1286,12 +1243,20 @@ Ext.define('Ext.panel.Table', { me.store = store; me.getView().bindStore(store); }, + + beforeDestroy: function(){ + // may be some duplication here since the horizontal and vertical + // scroller may be part of the docked items, but we need to clean + // them up in case they aren't visible. + Ext.destroy(this.horizontalScroller, this.verticalScroller); + this.callParent(); + }, /** - * Reconfigure the table with a new store/column. - * Either the store or the column can be ommitted if you don't wish to change them. - * @param {Ext.data.Store} store The new store. - * @param {Array} columns An array of column configs + * Reconfigures the table with a new store/columns. Either the store or the columns can be ommitted if you don't wish + * to change them. + * @param {Ext.data.Store} store (Optional) The new store. + * @param {Object[]} columns (Optional) An array of column configs */ reconfigure: function(store, columns) { var me = this, @@ -1300,12 +1265,10 @@ Ext.define('Ext.panel.Table', { if (me.lockable) { me.reconfigureLockable(store, columns); } else { - headerCt.suspendLayout = true; - headerCt.removeAll(); if (columns) { + headerCt.suspendLayout = true; + headerCt.removeAll(); headerCt.add(columns); - } else { - headerCt.doLayout(); } if (store) { store = Ext.StoreManager.lookup(store); @@ -1314,6 +1277,7 @@ Ext.define('Ext.panel.Table', { me.getView().refresh(); } if (columns) { + headerCt.suspendLayout = false; me.forceComponentLayout(); } }